├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── SECURITY.md ├── appveyor.yml ├── botbuilder-teams-js ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── activityProcessor │ │ ├── index.ts │ │ ├── teamsActivityProcessor.ts │ │ ├── teamsEvents.ts │ │ ├── teamsFactory.ts │ │ └── teamsInvoke.ts │ ├── connector │ │ ├── index.ts │ │ ├── teamsConnectorClient.ts │ │ └── teamsConnectorClientContext.ts │ ├── index.ts │ ├── middlewares │ │ ├── dropChannelActivitiesMiddleware.ts │ │ ├── dropChatActivitiesMiddleware.ts │ │ ├── dropNonTeamsActivitiesMiddleware.ts │ │ ├── index.ts │ │ └── teamsMiddleware.ts │ ├── schema │ │ ├── extension │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── models │ │ │ ├── index.ts │ │ │ ├── mappers.ts │ │ │ ├── parameters.ts │ │ │ └── teamsMappers.ts │ │ └── operations │ │ │ ├── index.ts │ │ │ └── teams.ts │ ├── stateStorage │ │ ├── index.ts │ │ └── teamSpecificConversationState.ts │ ├── teamsAdapter.ts │ └── teamsContext.ts ├── swagger │ ├── build.js │ ├── extension.json │ ├── generate.cmd │ ├── generate.sh │ └── teamsAPI.json ├── tests │ └── teamsContext.js └── tsconfig.json └── samples ├── README.md ├── echo-bot-with-counter ├── .env ├── bot-file.json ├── package.json ├── src │ ├── app.ts │ └── bot.ts ├── teams-app-manifest │ ├── icon-color.png │ ├── icon-outline.png │ └── manifest.json └── tsconfig.json ├── file-bot ├── .env ├── files │ └── teams-logo.png ├── package.json ├── src │ ├── app.ts │ └── bot.ts ├── teams-app-manifest │ ├── icon-color.png │ ├── icon-outline.png │ └── manifest.json └── tsconfig.json ├── message-extension-bot ├── .env ├── bot-file.json ├── package-lock.json ├── package.json ├── src │ ├── app.ts │ └── bot.ts ├── teams-app-manifest │ ├── icon-color.png │ ├── icon-outline.png │ └── manifest.json └── tsconfig.json └── teams-bot ├── .env ├── bot-file.json ├── package.json ├── src ├── app.ts └── bot.ts ├── teams-app-manifest ├── icon-color.png ├── icon-outline.png └── manifest.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/lib/** 2 | **/node_modules/** 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "attach", 10 | "name": "Attach by Process ID for VS Code debugging", 11 | "processId": "${command:PickProcess}", 12 | "port": 5566 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This functionality is now in the core Bot Framework SDK 2 | 3 | This functionality has been moved into the core Bot Framework SDK. You should update your Bot Framework SDK to version 4.6 rather than using this SDK. 4 | 5 | This SDK will be deprecated, and will not receive further updates. 6 | 7 | # Bot Builder SDK4 - Microsoft Teams Extensions 8 | 9 | [![Build status](https://ci.appveyor.com/api/projects/status/4kghlfgmsw2tnk0o/branch/master?svg=true)](https://ci.appveyor.com/project/robin-liao/botbuilder-microsoftteams-node/branch/master) 10 | 11 | The Microsoft Bot Builder SDK Teams Extensions allow bots built using Bot Builder SDK to consume Teams functionality easily. **[Review the documentation](https://msdn.microsoft.com/en-us/microsoft-teams/bots)** to get started! 12 | 13 | # Samples 14 | 15 | Get started quickly with our samples: 16 | 17 | * Sample bots [here](https://github.com/OfficeDev/BotBuilder-MicrosoftTeams-node/tree/master/samples) 18 | 19 | 20 | # This SDK allows you to easily... 21 | 22 | * Fetch a list of channels in a team 23 | * Fetch profile info about all members of a team 24 | * Fetch tenant-id from an incoming message to bot 25 | * Create 1:1 chat with a specific user 26 | * Mention a specific user 27 | * Consume various events like channel-created, team-renamed, etc. 28 | * Accept messages only from specific tenants 29 | * Write Compose Extensions 30 | * _and more!_ 31 | 32 | # Getting started 33 | 34 | * This SDK is the extension of [Bot Framework SDK 4](https://github.com/Microsoft/botbuilder-js), so you may start with [the quickstart](https://docs.microsoft.com/en-us/azure/bot-service/javascript/bot-builder-javascript-quickstart?view=azure-bot-service-4.0) or [the example for Azure Web app bot](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-nodejs-tutorial-bf-v4) if you never have experience for it. 35 | 36 | * Once you've got your bot scaffolded out, install the Teams BotBuilder package: 37 | 38 | ```bash 39 | npm install botbuilder-teams@4.0.0-beta1 40 | ``` 41 | 42 | * To extend your bot to support Microsoft Teams, add middleware to adapter: 43 | ```typescript 44 | // use Teams middleware 45 | adapter.use(new teams.TeamsMiddleware()); 46 | ``` 47 | * Now in the `onTurn` method of your bot, to do any Teams specific stuff, first grab the `TeamsContext` as shown below: 48 | ```typescript 49 | import { TeamsContext } from 'botbuilder-teams'; 50 | 51 | export class Bot { 52 | async onTurn(turnContext: TurnContext) { 53 | const teamsCtx: TeamsContext = TeamsContext.from(ctx); 54 | } 55 | } 56 | ``` 57 | * And once you have `teamsContext`, you may utilize code autocomplete provided by Visual Studio Code to discover all the operations you can do. For instance, here's how you can fetch the list of channels in the team and fetch information about the team: 58 | ```typescript 59 | // Now fetch the Team ID, Channel ID, and Tenant ID off of the incoming activity 60 | const incomingTeamId = teamsCtx.team.id; 61 | const incomingChannelid = teamsCtx.channel.id; 62 | const incomingTenantId = teamsCtx.tenant.id; 63 | 64 | // Make an operation call to fetch the list of channels in the team, and print count of channels. 65 | var channels = await teamsCtx.teamsConnectorClient.teams.fetchChannelList(incomingTeamId); 66 | await turnContext.sendActivity(`You have ${channels.conversations.length} channels in this team`); 67 | 68 | // Make an operation call to fetch details of the team where the activity was posted, and print it. 69 | var teamInfo = await teamsCtx.teamsConnectorClient.teams.fetchTeamDetails(incomingTeamId); 70 | await turnContext.sendActivity(`Name of this team is ${teamInfo.name} and group-id is ${teamInfo.aadGroupId}`); 71 | ``` 72 | 73 | # Questions, bugs, feature requests, and contributions 74 | Please review the information [here](https://msdn.microsoft.com/en-us/microsoft-teams/feedback). 75 | 76 | # Contributing 77 | 78 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 79 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Select image for build machine 2 | image: ubuntu 3 | 4 | # Test against the latest version of this Node.js version 5 | environment: 6 | nodejs_version: "8.11.3" 7 | 8 | # Pre-install init script 9 | init: 10 | - nvm install $nodejs_version 11 | - nvm use $nodejs_version 12 | - node --version 13 | - npm --version 14 | 15 | # Install scripts. (runs after repo cloning) 16 | install: 17 | - cd botbuilder-teams-js 18 | - npm install 19 | 20 | # Post-install build scripts. 21 | build_script: 22 | - npm run build 23 | 24 | # Post-install test scripts. 25 | test_script: 26 | - npm run test 27 | -------------------------------------------------------------------------------- /botbuilder-teams-js/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/botbuilder-teams-js/README.md -------------------------------------------------------------------------------- /botbuilder-teams-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-teams", 3 | "version": "4.3.0-beta1", 4 | "description": "Bot Builder V4 extension SDK for Microsoft Teams", 5 | "author": "Microsoft Corp", 6 | "license": "MIT", 7 | "main": "./lib/index.js", 8 | "scripts": { 9 | "prepublish": "npm run build", 10 | "build": "tsc", 11 | "test": "cd tests && mocha ." 12 | }, 13 | "typings": "./lib/index.d.ts", 14 | "dependencies": { 15 | "adaptivecards": "^1.0.0", 16 | "botbuilder": "4.3.2", 17 | "botframework-connector": "4.3.2", 18 | "jsdom": "14.0.0" 19 | }, 20 | "devDependencies": { 21 | "@types/jsdom": "^12.2.3", 22 | "@types/mocha": "^5.2.6", 23 | "autorest": "^2.0.4283", 24 | "fs-extra": "^7.0.1", 25 | "mocha": "^6.1.4", 26 | "replace": "^1.0.1", 27 | "rimraf": "^2.6.3", 28 | "typescript": "^3.3.3333" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/activityProcessor/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | export * from './teamsEvents'; 25 | export * from './teamsActivityProcessor'; 26 | export * from './teamsFactory'; 27 | export * from './teamsInvoke'; -------------------------------------------------------------------------------- /botbuilder-teams-js/src/activityProcessor/teamsActivityProcessor.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import { TurnContext, ActivityTypes } from 'botbuilder'; 25 | import { TeamsChannelData, FileDownloadInfo, FileDownloadInfoAttachment } from '../schema'; 26 | import { TeamEventType, IMembersAddedEvent, IMembersRemovedEvent, IChannelCreatedEvent, IChannelDeletedEvent, IChannelRenamedEvent, ITeamRenamedEvent } from './teamsEvents'; 27 | import { ITeamsInvokeActivityHandler, InvokeActivity } from './teamsInvoke'; 28 | import { TeamsFactory } from './teamsFactory'; 29 | 30 | /** 31 | * Event handlers for message activities 32 | */ 33 | export interface IMessageActivityHandler { 34 | /** 35 | * Handles the message activity. 36 | * @param context The turn context. 37 | */ 38 | onMessage?: (context: TurnContext) => Promise; 39 | 40 | /** 41 | * Handles the message activity with file download info where the file is sent from users to bots. 42 | * @param context The turn context. 43 | * @param attachment File download information. Bot can access `downloadUrl` to download the file that user sends to bots. 44 | */ 45 | onMessageWithFileDownloadInfo?: (context: TurnContext, attachment: FileDownloadInfo) => Promise; 46 | } 47 | 48 | /** 49 | * Event handlers for Teams conversation update activities. 50 | */ 51 | export interface ITeamsConversationUpdateActivityHandler { 52 | /** 53 | * Handles the team members added event. 54 | * @param event Event object. 55 | */ 56 | onTeamMembersAdded?: (event: IMembersAddedEvent) => Promise; 57 | 58 | /** 59 | * Handles the team members removed event. 60 | * @param event Event object. 61 | */ 62 | onTeamMembersRemoved?: (event: IMembersRemovedEvent) => Promise; 63 | 64 | /** 65 | * Handles the channel created event. 66 | * @param event Event object. 67 | */ 68 | onChannelCreated?: (event: IChannelCreatedEvent) => Promise; 69 | 70 | /** 71 | * Handles the channel deleted event. 72 | * @param event Event object. 73 | */ 74 | onChannelDeleted?: (event: IChannelDeletedEvent) => Promise; 75 | 76 | /** 77 | * Handles the channel renamed event. 78 | * @param event Event object. 79 | */ 80 | onChannelRenamed?: (event: IChannelRenamedEvent) => Promise; 81 | 82 | /** 83 | * Handles the team renamed event. 84 | * @param event Event object. 85 | */ 86 | onTeamRenamed?: (event: ITeamRenamedEvent) => Promise; 87 | 88 | /** 89 | * Handle generic Teams conversation update event. This handler will be hit only when none of above is applied. 90 | * @param turnContext Current turn context. 91 | */ 92 | onConversationUpdateActivity?: (turnContext: TurnContext) => Promise; 93 | } 94 | 95 | /** 96 | * Event handlers for message reaction activities 97 | */ 98 | export interface IMessageReactionActivityHandler { 99 | /** 100 | * Handles the message reaction activity 101 | * @param turnContext Current turn context. 102 | */ 103 | onMessageReaction?: (turnContext: TurnContext) => Promise; 104 | } 105 | 106 | /** 107 | * Extension types of `ActivityTypes` for Teams 108 | */ 109 | export enum ActivityTypesEx { 110 | InvokeResponse = 'invokeResponse' 111 | } 112 | 113 | /** 114 | * Teams activity processor - the helper class to dispatch the `onTurn` events to corresponding handlers 115 | * 116 | * @remarks 117 | * This is the helper class to process every `onTurn` events and dispatch the current event 118 | * to corresponding handlers. To use this, simply register and set up the handlers in advance 119 | * and then forward `onTurn` event to `processIncomingActivity`: 120 | * ```typescript 121 | * class Bot { 122 | * private activitProc = new TeamsActivityProcessor(); 123 | * 124 | * constructor () { 125 | * this.activityProc.messageActivityHandler = { 126 | * onMessage: async (ctx: TurnContext) => { 127 | * // ... do whatever you want ... 128 | * }; 129 | * }; 130 | * } 131 | * 132 | * async onTurn(turnContext: TurnContext) { 133 | * await this.activityProc.processIncomingActivity(turnContext); 134 | * } 135 | * } 136 | * ``` 137 | */ 138 | export class TeamsActivityProcessor { 139 | /** 140 | * Initializes a new instance of the `TeamsActivityProcessor` class 141 | * @param messageActivityHandler The message activity handler 142 | * @param conversationUpdateActivityHandler The conversation update activity handler. 143 | * @param invokeActivityHandler the invoke activity handler. 144 | * @param messageReactionActivityHandler The message reaction activity handler. 145 | */ 146 | constructor ( 147 | public messageActivityHandler?: IMessageActivityHandler, 148 | public conversationUpdateActivityHandler?: ITeamsConversationUpdateActivityHandler, 149 | public invokeActivityHandler?: ITeamsInvokeActivityHandler, 150 | public messageReactionActivityHandler?: IMessageReactionActivityHandler 151 | ) { 152 | } 153 | 154 | /** 155 | * Processes the incoming activity. 156 | * @param turnContext The correct turn context. 157 | */ 158 | public async processIncomingActivity (turnContext: TurnContext) { 159 | switch (turnContext.activity.type) { 160 | case ActivityTypes.Message: 161 | { 162 | await this.processOnMessageActivity(turnContext); 163 | break; 164 | } 165 | 166 | case ActivityTypes.ConversationUpdate: 167 | { 168 | await this.processTeamsConversationUpdate(turnContext); 169 | break; 170 | } 171 | 172 | case ActivityTypes.Invoke: 173 | { 174 | const invokeResponse = await InvokeActivity.dispatchHandler(this.invokeActivityHandler, turnContext); 175 | if (invokeResponse) { 176 | await turnContext.sendActivity({ value: invokeResponse, type: ActivityTypesEx.InvokeResponse }); 177 | } 178 | break; 179 | } 180 | 181 | case ActivityTypes.MessageReaction: 182 | { 183 | const handler = this.messageReactionActivityHandler; 184 | if (handler && handler.onMessageReaction) { 185 | await handler.onMessageReaction(turnContext); 186 | } 187 | break; 188 | } 189 | } 190 | } 191 | 192 | private async processOnMessageActivity (turnContext: TurnContext) { 193 | const handler = this.messageActivityHandler; 194 | if (handler) { 195 | if (handler.onMessageWithFileDownloadInfo) { 196 | const attachments = turnContext.activity.attachments || []; 197 | const fileDownload = attachments.map(x => TeamsFactory.isFileDownloadInfoAttachment(x) && x).shift(); 198 | if (fileDownload) { 199 | return await handler.onMessageWithFileDownloadInfo(turnContext, fileDownload.content); 200 | } 201 | } 202 | 203 | if (handler.onMessage) { 204 | await handler.onMessage(turnContext); 205 | } 206 | } 207 | } 208 | 209 | private async processTeamsConversationUpdate (turnContext: TurnContext) { 210 | const handler = this.conversationUpdateActivityHandler; 211 | if (handler) { 212 | const channelData: TeamsChannelData = turnContext.activity.channelData; 213 | if (channelData && channelData.eventType) { 214 | switch (channelData.eventType as TeamEventType) { 215 | case 'teamMembersAdded': 216 | handler.onTeamMembersAdded && await handler.onTeamMembersAdded({ 217 | eventType: 'teamMembersAdded', 218 | turnContext: turnContext, 219 | team: channelData.team, 220 | tenant: channelData.tenant, 221 | membersAdded: turnContext.activity.membersAdded, 222 | }); 223 | return; 224 | 225 | case 'teamMembersRemoved': 226 | handler.onTeamMembersRemoved && await handler.onTeamMembersRemoved({ 227 | eventType: 'teamMembersRemoved', 228 | turnContext: turnContext, 229 | team: channelData.team, 230 | tenant: channelData.tenant, 231 | membersRemoved: turnContext.activity.membersRemoved 232 | }); 233 | return; 234 | 235 | case 'channelCreated': 236 | handler.onChannelCreated && await handler.onChannelCreated({ 237 | eventType: 'channelCreated', 238 | turnContext: turnContext, 239 | team: channelData.team, 240 | tenant: channelData.tenant, 241 | channel: channelData.channel 242 | }); 243 | return; 244 | 245 | case 'channelDeleted': 246 | handler.onChannelDeleted && await handler.onChannelDeleted({ 247 | eventType: 'channelDeleted', 248 | turnContext: turnContext, 249 | team: channelData.team, 250 | tenant: channelData.tenant, 251 | channel: channelData.channel 252 | }); 253 | return; 254 | 255 | case 'channelRenamed': 256 | handler.onChannelRenamed && await handler.onChannelRenamed({ 257 | eventType: 'channelRenamed', 258 | turnContext: turnContext, 259 | team: channelData.team, 260 | tenant: channelData.tenant, 261 | channel: channelData.channel 262 | }); 263 | return; 264 | 265 | case 'teamRenamed': 266 | handler.onTeamRenamed && await handler.onTeamRenamed({ 267 | eventType: 'teamRenamed', 268 | turnContext: turnContext, 269 | team: channelData.team, 270 | tenant: channelData.tenant 271 | }); 272 | return; 273 | } 274 | } 275 | 276 | if (handler.onConversationUpdateActivity) { 277 | await handler.onConversationUpdateActivity(turnContext); 278 | } 279 | } 280 | } 281 | } -------------------------------------------------------------------------------- /botbuilder-teams-js/src/activityProcessor/teamsEvents.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as models from '../schema/models'; 25 | import { TurnContext, ChannelAccount } from 'botbuilder'; 26 | 27 | /** 28 | * Literal type of team event name values. 29 | */ 30 | export type TeamEventType = 31 | 'teamMembersAdded' 32 | | 'teamMembersRemoved' 33 | | 'channelCreated' 34 | | 'channelDeleted' 35 | | 'channelRenamed' 36 | | 'teamRenamed'; 37 | 38 | /** 39 | * Type of team event. 40 | */ 41 | export interface ITeamEvent 42 | { 43 | /** Gets the event type. */ 44 | readonly eventType: TeamEventType; 45 | 46 | /** Gets the team for the tenant. */ 47 | readonly team: models.TeamInfo; 48 | 49 | /** Gets the tenant for the team. */ 50 | readonly tenant: models.TenantInfo; 51 | 52 | /** Gets the original activity. */ 53 | readonly turnContext: TurnContext; 54 | } 55 | 56 | /** 57 | * Channel created event arguments. 58 | */ 59 | export interface IChannelCreatedEvent extends ITeamEvent { 60 | /** Gets the event type - must be `channelCreated` */ 61 | readonly eventType: 'channelCreated'; 62 | 63 | /** Gets the created channel. */ 64 | readonly channel: models.ChannelInfo 65 | } 66 | 67 | /** 68 | * Channel deleted event arguments. 69 | */ 70 | export interface IChannelDeletedEvent extends ITeamEvent { 71 | /** Gets the event type - must be `channelDeleted` */ 72 | readonly eventType: 'channelDeleted'; 73 | 74 | /** Gets the deleted channel. */ 75 | readonly channel: models.ChannelInfo; 76 | } 77 | 78 | /** 79 | * Channel renamed event. 80 | */ 81 | export interface IChannelRenamedEvent extends ITeamEvent { 82 | /** Gets the event type - must be `channelRenamed` */ 83 | readonly eventType: 'channelRenamed'; 84 | 85 | /** Gets the renamed channel. */ 86 | readonly channel: models.ChannelInfo; 87 | } 88 | 89 | /** 90 | * Event arguments for members added event. 91 | */ 92 | export interface IMembersAddedEvent extends ITeamEvent { 93 | /** Gets the event type - must be `teamMembersAdded` */ 94 | readonly eventType: 'teamMembersAdded'; 95 | 96 | /** Gets the list of added members. */ 97 | readonly membersAdded: ChannelAccount[]; 98 | } 99 | 100 | /** 101 | * Event arguments for members removed event. 102 | */ 103 | export interface IMembersRemovedEvent extends ITeamEvent { 104 | /** Gets the event type - must be `teamMembersRemoved` */ 105 | readonly eventType: 'teamMembersRemoved'; 106 | 107 | /** Gets the list of removed members. */ 108 | readonly membersRemoved: ChannelAccount[]; 109 | } 110 | 111 | /** 112 | * Team renamed event. 113 | */ 114 | export interface ITeamRenamedEvent extends ITeamEvent { 115 | /** Gets the event type - must be `teamRenamed` */ 116 | readonly eventType: 'teamRenamed'; 117 | } 118 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/activityProcessor/teamsFactory.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import { CardFactory, Attachment, CardAction } from 'botbuilder'; 25 | import * as models from '../schema'; 26 | import * as ac from 'adaptivecards'; 27 | 28 | export type IAdaptiveCardAction = ac.IAdaptiveCard['actions'][0]; 29 | 30 | export interface ITaskModuleCardAction { 31 | title: string; 32 | value: {[key: string]: any}; 33 | toAction(): CardAction; 34 | toAdaptiveCardAction(): IAdaptiveCardAction; 35 | } 36 | 37 | /** 38 | * Teams factory class that extends `CardFacotry` to create Teams-specific cards and types 39 | */ 40 | export class TeamsFactory extends CardFactory { 41 | /** 42 | * List of content types for each Teams-specific card style. 43 | */ 44 | public static contentTypes: any = { 45 | ...CardFactory.contentTypes, 46 | o365ConnectorCard: 'application/vnd.microsoft.teams.card.o365connector', 47 | fileConsentCard: 'application/vnd.microsoft.teams.card.file.consent', 48 | fileDownloadInfo: 'application/vnd.microsoft.teams.file.download.info', 49 | fileInfoCard: 'application/vnd.microsoft.teams.card.file.info' 50 | }; 51 | 52 | /** 53 | * Type guard to identify this `attachment` is the type of `FileDownloadInfoAttachment` 54 | * @param attachment Generic attachment object. 55 | * @returns Returns true if `attachment` is type of `FileDownloadInfoAttachment` 56 | * (and will auto cast to this type in `if() {}` block) 57 | */ 58 | public static isFileDownloadInfoAttachment (attachment: Attachment): attachment is models.FileDownloadInfoAttachment { 59 | return attachment.contentType === TeamsFactory.contentTypes.fileDownloadInfo; 60 | } 61 | 62 | /** 63 | * Returns an attachment for an O365 connector card. 64 | * @param content card payload 65 | */ 66 | public static o365ConnectorCard (content: models.O365ConnectorCard): Attachment { 67 | return { 68 | contentType: TeamsFactory.contentTypes.o365ConnectorCard, 69 | content 70 | }; 71 | } 72 | 73 | /** 74 | * Returns an attachment for an file consent card 75 | * 76 | * @remarks 77 | * The file consent card is used to send user a card to ask if he/she would like to receive the file 78 | * before bot sends out the file. If user accepts it then the `onFileConsent` invoke handler will be 79 | * triggered (defined in `ActivityProcessor.invokeActivityHandler`) where an upload URL comes with the 80 | * invoke request. Therefore bots can upload content there. 81 | * 82 | * @param fileName file name 83 | * @param content card payload 84 | */ 85 | public static fileConsentCard (fileName: string, content: models.FileConsentCard): Attachment { 86 | return { 87 | contentType: TeamsFactory.contentTypes.fileConsentCard, 88 | name: fileName, 89 | content 90 | }; 91 | } 92 | 93 | /** 94 | * Returns an attachment for an file info card 95 | * 96 | * @remarks 97 | * After user accepts consents and bot uploads file to the URL endpoint, then bot can send out this 98 | * type of card to inform user that the file is ready to download. 99 | * 100 | * @param fileName file name 101 | * @param contentUrl the content URL to notify user to download the file 102 | * @param content card payload 103 | */ 104 | public static fileInfoCard (fileName: string, contentUrl: string, content: models.FileInfoCard): Attachment { 105 | return { 106 | contentType: TeamsFactory.contentTypes.fileInfoCard, 107 | name: fileName, 108 | contentUrl, 109 | content 110 | }; 111 | } 112 | 113 | public static adaptiveCard(card: ac.IAdaptiveCard): Attachment { 114 | return CardFactory.adaptiveCard(card); 115 | } 116 | 117 | public static adaptiveCardAction(action: CardAction | string | undefined): IAdaptiveCardAction { 118 | const obj = TeamsFactory.adaptiveCardActions([action]); 119 | return (obj && obj[0]) || undefined; 120 | } 121 | 122 | public static adaptiveCardActions(actions: (CardAction | string)[] | undefined): IAdaptiveCardAction[] { 123 | let botBuilderBtns = CardFactory.actions(actions); 124 | let acActions: IAdaptiveCardAction[] = []; 125 | for (let i = 0; i < botBuilderBtns.length; ++i) { 126 | let btn = botBuilderBtns[i]; 127 | let adapterBtn: IAdaptiveCardAction = { 128 | id: undefined, 129 | type: 'Action.Submit', 130 | title: btn.title, 131 | data: {}, 132 | }; 133 | delete btn.title; 134 | adapterBtn.data[ 'msteams' ] = btn; 135 | acActions.push(adapterBtn); 136 | } 137 | return acActions; 138 | } 139 | 140 | public static taskModuleAction(title: string, value: {[key: string]: any} = {}): ITaskModuleCardAction { 141 | let adaptorObj: CardAction = { 142 | type: 'invoke', 143 | title, 144 | value 145 | }; 146 | 147 | let toAction = (): CardAction => { 148 | let valJson = (typeof adaptorObj.value === 'string') ? JSON.parse(adaptorObj.value) : adaptorObj.value; 149 | valJson.type = 'task/fetch'; 150 | adaptorObj.value = JSON.stringify(valJson); 151 | return adaptorObj; 152 | }; 153 | 154 | let toAdaptiveCardAction = (): IAdaptiveCardAction => { 155 | let btn = toAction(); 156 | return TeamsFactory.adaptiveCardAction(btn); 157 | }; 158 | 159 | return { 160 | ...adaptorObj, 161 | toAction, 162 | toAdaptiveCardAction 163 | }; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/activityProcessor/teamsInvoke.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import { TurnContext, InvokeResponse, Activity } from 'botbuilder'; 25 | import { 26 | MessagingExtensionQuery, 27 | MessagingExtensionResponse, 28 | O365ConnectorCardActionQuery, 29 | SigninStateVerificationQuery, 30 | FileConsentCardResponse, 31 | TaskModuleRequest, 32 | TaskModuleResponse, 33 | MessagingExtensionAction, 34 | MessagingExtensionActionResponse, 35 | AppBasedLinkQuery 36 | } from '../schema'; 37 | import { JSDOM } from 'jsdom'; 38 | 39 | /** 40 | * Typed invoke request activity, inherited from `Activity` 41 | * @typeparam T the type to define invoke `value` free payload 42 | */ 43 | export interface InvokeRequestActivity extends Activity { 44 | value: T; 45 | } 46 | 47 | /** 48 | * Typed invoke response, inherited from `InvokeResponse` 49 | * @typeparam T the type to define `body` free payload in response 50 | */ 51 | export interface InvokeResponseTyped extends InvokeResponse { 52 | body: T; 53 | } 54 | 55 | export type InvokeType = keyof typeof InvokeActivity.definitions; 56 | export type InvokeValueTypeOf = (typeof InvokeActivity.definitions)[N]['value']; 57 | export type InvokeResponseUnsafeTypeOf = (typeof InvokeActivity.definitions)[N]['response']; 58 | export type InvokeResponseTypeOf = InvokeResponseUnsafeTypeOf extends InvokeResponse ? InvokeResponseUnsafeTypeOf: InvokeResponse; 59 | 60 | /** 61 | * Define type-binding event handlers for Teams invoke activities. 62 | * 63 | * @remarks 64 | * This type definition defines a list of interfaces for type-binding invoke handlers, 65 | * with typed interface name, invoke value (request), and invoke response whose definitions are populated from `InvokeActivity.definitions` 66 | * This is also the base class of `ITeamsInvokeActivityHandler` 67 | */ 68 | export type InvokeTypedHandler = { 69 | [name in InvokeType]?: (turnContext: TurnContext, invokeValue: InvokeValueTypeOf) => Promise>; 70 | }; 71 | 72 | /** 73 | * Event handlers for Teams invoke activities. 74 | * 75 | * @remarks 76 | * This class extends the type of `InvokeTypedHandler` that defines type-binding invoke request and response 77 | * where the definitions are given by `InvokeActivity.definitions`. In consequence, `ITeamsInvokeActivityHandler` 78 | * will actually define a list of interface for type-binding invoke handlers, plus a generic `onInvoke` handler 79 | */ 80 | export interface ITeamsInvokeActivityHandler extends InvokeTypedHandler { 81 | /** 82 | * Handles generic invoke request. This handler will be triggered only when none of type-binding invoke handlers is applied. 83 | * @param turnContext Current turn context. 84 | */ 85 | onInvoke? (turnContext: TurnContext): Promise; 86 | } 87 | 88 | /** 89 | * Helper class for invoke activity 90 | */ 91 | export class InvokeActivity { 92 | /** 93 | * List of type-binding invoke handler definitions 94 | * @remarks 95 | * The key used in definitions will turn out to be the name of invoke handler. Similarly, for the values: 96 | * `name` defines the name of invoke to trigger this handler; 97 | * `value` defines the invoke request value type; 98 | * `response` by default it's `InvokeResponse` (no type-binding response). If response body is typed, it must inherit from `InvokeResponse` 99 | * an easier way is to use `InvokeResponseTyped` to wrap `T` into typed invoke response. Note that if arbitrary type assigned 100 | * without inheriting `InvokeResponse`, it'll be overwritten to be `InvokeResponse` by our auto type deductions. 101 | */ 102 | public static readonly definitions = { 103 | onO365CardAction: { 104 | name: 'actionableMessage/executeAction', 105 | value: {}, 106 | response: {} 107 | }, 108 | 109 | onSigninStateVerification: { 110 | name: 'signin/verifyState', 111 | value: {}, 112 | response: {} 113 | }, 114 | 115 | onFileConsent: { 116 | name: 'fileConsent/invoke', 117 | value: {}, 118 | response: {} 119 | }, 120 | 121 | onMessagingExtensionQuery: { 122 | name: 'composeExtension/query', 123 | value: {}, 124 | response: > {} 125 | }, 126 | 127 | onAppBasedLinkQuery: { 128 | name: 'composeExtension/queryLink', 129 | value: {}, 130 | response: > {} 131 | }, 132 | 133 | onMessagingExtensionFetchTask: { 134 | name: 'composeExtension/fetchTask', 135 | value: {}, 136 | response: > {} 137 | }, 138 | 139 | onMessagingExtensionSubmitAction: { 140 | name: 'composeExtension/submitAction', 141 | value: {}, 142 | response: > {} 143 | }, 144 | 145 | onTaskModuleFetch: { 146 | name: 'task/fetch', 147 | value: {}, 148 | response: > {} 149 | }, 150 | 151 | onTaskModuleSubmit: { 152 | name: 'task/submit', 153 | value: {}, 154 | response: > {} 155 | } 156 | }; 157 | 158 | /** 159 | * Type guard for activity auto casting into one with typed "value" for invoke payload 160 | * @param {Activity} activity Activity 161 | * @param N InvokeType, i.e., the key names of InvokeActivity.definitions 162 | */ 163 | public static is(activity: Activity, N: N): 164 | activity is InvokeRequestActivity> { 165 | return activity.name === InvokeActivity.definitions[N].name; 166 | } 167 | 168 | /** 169 | * Dispatch invoke event to corresponding invoke handlers 170 | * @param handler the handler set of invoke events 171 | * @param turnContext Current turn context. 172 | */ 173 | public static async dispatchHandler(handler: ITeamsInvokeActivityHandler, turnContext: TurnContext) { 174 | if (handler) { 175 | const activity = turnContext.activity; 176 | 177 | if (handler.onO365CardAction && InvokeActivity.is(activity, 'onO365CardAction')) { 178 | return await handler.onO365CardAction(turnContext, activity.value); 179 | } 180 | 181 | if (handler.onSigninStateVerification && InvokeActivity.is(activity, 'onSigninStateVerification')) { 182 | return await handler.onSigninStateVerification(turnContext, activity.value); 183 | } 184 | 185 | if (handler.onFileConsent && InvokeActivity.is(activity, 'onFileConsent')) { 186 | return await handler.onFileConsent(turnContext, activity.value); 187 | } 188 | 189 | if (handler.onMessagingExtensionQuery && InvokeActivity.is(activity, 'onMessagingExtensionQuery')) { 190 | return await handler.onMessagingExtensionQuery(turnContext, activity.value); 191 | } 192 | 193 | if (handler.onAppBasedLinkQuery && InvokeActivity.is(activity, 'onAppBasedLinkQuery')) { 194 | return await handler.onAppBasedLinkQuery(turnContext, activity.value); 195 | } 196 | 197 | if (handler.onMessagingExtensionFetchTask && InvokeActivity.is(activity, 'onMessagingExtensionFetchTask')) { 198 | let messagingExtensionActionData = activity.value; 199 | messagingExtensionActionData.messagePayload.body.textContent = this.stripHtmlTag(messagingExtensionActionData.messagePayload.body.content); 200 | return await handler.onMessagingExtensionFetchTask(turnContext, messagingExtensionActionData); 201 | } 202 | 203 | if (handler.onMessagingExtensionSubmitAction && InvokeActivity.is(activity, 'onMessagingExtensionSubmitAction')) { 204 | let messagingExtensionActionData = activity.value; 205 | messagingExtensionActionData.messagePayload.body.textContent = this.stripHtmlTag(messagingExtensionActionData.messagePayload.body.content); 206 | return await handler.onMessagingExtensionSubmitAction(turnContext, messagingExtensionActionData); 207 | } 208 | 209 | if (handler.onTaskModuleFetch && InvokeActivity.is(activity, 'onTaskModuleFetch')) { 210 | return await handler.onTaskModuleFetch(turnContext, activity.value); 211 | } 212 | 213 | if (handler.onTaskModuleSubmit && InvokeActivity.is(activity, 'onTaskModuleSubmit')) { 214 | return await handler.onTaskModuleSubmit(turnContext, activity.value); 215 | } 216 | 217 | if (handler.onInvoke) { 218 | return await handler.onInvoke(turnContext); 219 | } 220 | } 221 | } 222 | 223 | private static stripHtmlTag(content: string): string { 224 | let dom = new JSDOM(content); 225 | const textRestrictedHtmlTags = new Set(["AT", "ATTACHMENT"]); 226 | return this.stripHtmlTagHelper(dom.window.document.body, textRestrictedHtmlTags); 227 | } 228 | 229 | private static stripHtmlTagHelper(node: HTMLElement, tags: Set): string { 230 | let result = ''; 231 | if (tags.has(node.tagName)) { 232 | result += node.outerHTML; 233 | } else { 234 | node.childNodes.forEach(childNode => { 235 | if (childNode.nodeType === Node.TEXT_NODE) { 236 | result += childNode.nodeValue; 237 | } else { 238 | result += this.stripHtmlTagHelper(childNode, tags); 239 | } 240 | }); 241 | } 242 | return result; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/connector/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | export * from './teamsConnectorClient'; 25 | export * from './teamsConnectorClientContext'; -------------------------------------------------------------------------------- /botbuilder-teams-js/src/connector/teamsConnectorClient.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | import * as msRest from '@azure/ms-rest-js'; 8 | import * as Models from '../schema/models'; 9 | import * as Mappers from '../schema/models/mappers'; 10 | import * as operations from '../schema/operations'; 11 | import { TeamsConnectorClientContext } from './teamsConnectorClientContext'; 12 | 13 | class TeamsConnectorClient extends TeamsConnectorClientContext { 14 | // Operation groups 15 | teams: operations.Teams; 16 | 17 | /** 18 | * Initializes a new instance of the TeamsConnectorClient class. 19 | * @param credentials Subscription credentials which uniquely identify client subscription. 20 | * @param [options] The parameter options 21 | */ 22 | constructor(credentials: msRest.ServiceClientCredentials, options?: Models.TeamsConnectorClientOptions) { 23 | super(credentials, options); 24 | this.teams = new operations.Teams(this); 25 | } 26 | } 27 | 28 | // Operation Specifications 29 | 30 | export { 31 | TeamsConnectorClient, 32 | TeamsConnectorClientContext, 33 | Models as TeamsConnectorModels, 34 | Mappers as TeamsConnectorMappers 35 | }; 36 | export * from '../schema/operations'; 37 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/connector/teamsConnectorClientContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | import * as msRest from '@azure/ms-rest-js'; 8 | import * as Models from '../schema/models'; 9 | 10 | export class TeamsConnectorClientContext extends msRest.ServiceClient { 11 | credentials: msRest.ServiceClientCredentials; 12 | 13 | /** 14 | * Initializes a new instance of the TeamsConnectorClientContext class. 15 | * @param credentials Subscription credentials which uniquely identify client subscription. 16 | * @param [options] The parameter options 17 | */ 18 | constructor(credentials: msRest.ServiceClientCredentials, options?: Models.TeamsConnectorClientOptions) { 19 | 20 | if (!options) { 21 | options = {}; 22 | } 23 | 24 | super(credentials, options); 25 | 26 | this.baseUri = options.baseUri || this.baseUri || 'https://api.botframework.com'; 27 | this.requestContentType = 'application/json; charset=utf-8'; 28 | this.credentials = credentials; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | export * from './activityProcessor'; 25 | export * from './connector'; 26 | export * from './middlewares'; 27 | export * from './stateStorage'; 28 | export * from './schema'; 29 | export * from './teamsContext'; 30 | export * from './teamsAdapter'; -------------------------------------------------------------------------------- /botbuilder-teams-js/src/middlewares/dropChannelActivitiesMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | import { TeamsChannelData } from '../schema/models'; 26 | 27 | /** 28 | * Automatically drops all messages from channels. 29 | */ 30 | export class DropChannelActivitiesMiddleware implements builder.Middleware { 31 | 32 | /** 33 | * When implemented in middleware, process an incoming activity. 34 | * 35 | * @remarks 36 | * Middleware calls the `next` delegate to pass control to 37 | * the next middleware in the pipeline. If middleware doesn’t call the next delegate, 38 | * the adapter does not call any of the subsequent middleware’s request handlers or the 39 | * bot’s receive handler, and the pipeline short circuits. 40 | * The `context` provides information about the 41 | * incoming activity, and other data needed to process the activity. 42 | * 43 | * @param context The context object for this turn. 44 | * @param next The delegate to call to continue the bot middleware pipeline. 45 | */ 46 | public async onTurn(context: builder.TurnContext, next: () => Promise): Promise { 47 | // only non-channel activities can pass through 48 | const teamsChannelData: TeamsChannelData = context.activity.channelData; 49 | if (!teamsChannelData.team) { 50 | return next(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/middlewares/dropChatActivitiesMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | 26 | /** 27 | * Automatically drops all one on one and group chat messages. 28 | */ 29 | export class DropChatActivitiesMiddleware implements builder.Middleware { 30 | 31 | /** 32 | * When implemented in middleware, process an incoming activity. 33 | * 34 | * @remarks 35 | * Middleware calls the `next` delegate to pass control to 36 | * the next middleware in the pipeline. If middleware doesn’t call the next delegate, 37 | * the adapter does not call any of the subsequent middleware’s request handlers or the 38 | * bot’s receive handler, and the pipeline short circuits. 39 | * The `context` provides information about the 40 | * incoming activity, and other data needed to process the activity. 41 | * 42 | * @param context The context object for this turn. 43 | * @param next The delegate to call to continue the bot middleware pipeline. 44 | */ 45 | public async onTurn(context: builder.TurnContext, next: () => Promise): Promise { 46 | const convType = context.activity.conversation && context.activity.conversation.conversationType; 47 | const isChat = !!convType && (convType === 'personal' || convType == 'groupChat'); 48 | // only non-channel activities can pass through 49 | if (!isChat) { 50 | return next(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/middlewares/dropNonTeamsActivitiesMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | 26 | /** 27 | * Automatically drop all messages received from any channel except Microsoft Teams. 28 | */ 29 | export class DropNonTeamsActivitiesMiddleware implements builder.Middleware { 30 | 31 | /** 32 | * When implemented in middleware, process an incoming activity. 33 | * 34 | * @remarks 35 | * Middleware calls the `next` delegate to pass control to 36 | * the next middleware in the pipeline. If middleware doesn’t call the next delegate, 37 | * the adapter does not call any of the subsequent middleware’s request handlers or the 38 | * bot’s receive handler, and the pipeline short circuits. 39 | * The `context` provides information about the 40 | * incoming activity, and other data needed to process the activity. 41 | * 42 | * @param context The context object for this turn. 43 | * @param next The delegate to call to continue the bot middleware pipeline. 44 | */ 45 | public async onTurn(context: builder.TurnContext, next: () => Promise): Promise { 46 | // only 'msteams' activities can pass through 47 | if (context.activity.channelId === 'msteams') { 48 | return next(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | export * from './teamsMiddleware'; 25 | export * from './dropNonTeamsActivitiesMiddleware'; 26 | export * from './dropChannelActivitiesMiddleware'; 27 | export * from './dropChatActivitiesMiddleware'; -------------------------------------------------------------------------------- /botbuilder-teams-js/src/middlewares/teamsMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | import { MicrosoftAppCredentials } from 'botframework-connector'; 26 | import { TeamsConnectorClient, TeamsContext } from '../'; 27 | 28 | /** 29 | * Teams Middleware. This middleware needs to be registered in the Middleware pipeline at the time of Adapter initialization. 30 | */ 31 | export class TeamsMiddleware implements builder.Middleware { 32 | 33 | /** 34 | * When implemented in middleware, process an incoming activity. 35 | * 36 | * @remarks 37 | * Middleware calls the `next` delegate to pass control to 38 | * the next middleware in the pipeline. If middleware doesn’t call the next delegate, 39 | * the adapter does not call any of the subsequent middleware’s request handlers or the 40 | * bot’s receive handler, and the pipeline short circuits. 41 | * The `context` provides information about the 42 | * incoming activity, and other data needed to process the activity. 43 | * 44 | * @param context The context object for this turn. 45 | * @param next The delegate to call to continue the bot middleware pipeline. 46 | */ 47 | public async onTurn(context: builder.TurnContext, next: () => Promise): Promise { 48 | const credentials: MicrosoftAppCredentials = context.adapter && (context.adapter as any).credentials; 49 | if (context.activity.channelId === 'msteams' && !!credentials) { 50 | const serviceUrl = context.activity && context.activity.serviceUrl; 51 | const teamsConnectorClient = new TeamsConnectorClient(credentials, { baseUri: serviceUrl }); 52 | context.turnState.set(TeamsContext.stateKey, new TeamsContext(context, teamsConnectorClient)); 53 | } 54 | return next(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/extension/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | import * as teams from '../models'; 26 | 27 | /** 28 | * Defines values for Type. 29 | * Possible values include: 'ViewAction', 'OpenUri', 'HttpPOST', 'ActionCard' 30 | * @readonly 31 | * @enum {string} 32 | */ 33 | export type O365ConnectorCardActionType = 'ViewAction' | 'OpenUri' | 'HttpPOST' | 'ActionCard'; 34 | 35 | /** 36 | * @interface 37 | * An interface representing O365ConnectorCardActionBase. 38 | * O365 connector card action base 39 | * 40 | */ 41 | export interface O365ConnectorCardActionBase { 42 | /** 43 | * @member {Type} [type] Type of the action. Possible values include: 44 | * 'ViewAction', 'OpenUri', 'HttpPOST', 'ActionCard' 45 | */ 46 | '@type'?: O365ConnectorCardActionType; 47 | /** 48 | * @member {string} [name] Name of the action that will be used as button 49 | * title 50 | */ 51 | name?: string; 52 | /** 53 | * @member {string} [id] Action Id 54 | */ 55 | '@id'?: string; 56 | } 57 | 58 | /** 59 | * Defines values for Type1. 60 | * Possible values include: 'textInput', 'dateInput', 'multichoiceInput' 61 | * @readonly 62 | * @enum {string} 63 | */ 64 | export type O365ConnectorCardInputType = 'textInput' | 'dateInput' | 'multichoiceInput'; 65 | 66 | /** 67 | * @interface 68 | * An interface representing O365ConnectorCardInputBase. 69 | * O365 connector card input for ActionCard action 70 | * 71 | */ 72 | export interface O365ConnectorCardInputBase { 73 | /** 74 | * @member {Type1} [type] Input type name. Possible values include: 75 | * 'textInput', 'dateInput', 'multichoiceInput' 76 | */ 77 | '@type'?: O365ConnectorCardInputType; 78 | /** 79 | * @member {string} [id] Input Id. It must be unique per entire O365 80 | * connector card. 81 | */ 82 | id?: string; 83 | /** 84 | * @member {boolean} [isRequired] Define if this input is a required field. 85 | * Default value is false. 86 | */ 87 | isRequired?: boolean; 88 | /** 89 | * @member {string} [title] Input title that will be shown as the placeholder 90 | */ 91 | title?: string; 92 | /** 93 | * @member {string} [value] Default value for this input field 94 | */ 95 | value?: string; 96 | } 97 | 98 | export interface TeamsAttachment extends builder.Attachment { 99 | content: ContentType; 100 | } 101 | 102 | export type FileDownloadInfoAttachment = TeamsAttachment; 103 | 104 | /** 105 | * @interface 106 | * An interface representing MessageActionsPayloadBody. 107 | * Plaintext/HTML representation of the content of the message. 108 | * 109 | */ 110 | export interface MessageActionsPayloadBody { 111 | /** 112 | * @member {ContentType} [contentType] Type of the content. Possible values 113 | * include: 'html', 'text' 114 | */ 115 | contentType?: teams.ContentType; 116 | /** 117 | * @member {string} [content] The content of the body. 118 | */ 119 | content?: string; 120 | /** 121 | * @member {string} [textContent] The text content of the body after 122 | * stripping HTML tags. 123 | */ 124 | textContent?: string; 125 | } 126 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | export * from './extension'; 25 | export * from './models'; -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/models/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | import * as teams from '../extension' 8 | import * as builder from 'botbuilder' 9 | import { ServiceClientOptions } from '@azure/ms-rest-js'; 10 | import * as msRest from '@azure/ms-rest-js'; 11 | 12 | 13 | /** 14 | * @interface 15 | * An interface representing ChannelInfo. 16 | * A channel info object which decribes the channel. 17 | * 18 | */ 19 | export interface ChannelInfo { 20 | /** 21 | * @member {string} [id] Unique identifier representing a channel 22 | */ 23 | id?: string; 24 | /** 25 | * @member {string} [name] Name of the channel 26 | */ 27 | name?: string; 28 | } 29 | 30 | /** 31 | * @interface 32 | * An interface representing ConversationList. 33 | * List of channels under a team 34 | * 35 | */ 36 | export interface ConversationList { 37 | /** 38 | * @member {ChannelInfo[]} [conversations] 39 | */ 40 | conversations?: ChannelInfo[]; 41 | } 42 | 43 | /** 44 | * @interface 45 | * An interface representing TeamDetails. 46 | * Details related to a team 47 | * 48 | */ 49 | export interface TeamDetails { 50 | /** 51 | * @member {string} [id] Unique identifier representing a team 52 | */ 53 | id?: string; 54 | /** 55 | * @member {string} [name] Name of team. 56 | */ 57 | name?: string; 58 | /** 59 | * @member {string} [aadGroupId] Azure Active Directory (AAD) Group Id for 60 | * the team. 61 | */ 62 | aadGroupId?: string; 63 | } 64 | 65 | /** 66 | * @interface 67 | * An interface representing TeamInfo. 68 | * Describes a team 69 | * 70 | */ 71 | export interface TeamInfo { 72 | /** 73 | * @member {string} [id] Unique identifier representing a team 74 | */ 75 | id?: string; 76 | /** 77 | * @member {string} [name] Name of team. 78 | */ 79 | name?: string; 80 | } 81 | 82 | /** 83 | * @interface 84 | * An interface representing NotificationInfo. 85 | * Specifies if a notification is to be sent for the mentions. 86 | * 87 | */ 88 | export interface NotificationInfo { 89 | /** 90 | * @member {boolean} [alert] true if notification is to be sent to the user, 91 | * false otherwise. 92 | */ 93 | alert?: boolean; 94 | } 95 | 96 | /** 97 | * @interface 98 | * An interface representing TenantInfo. 99 | * Describes a tenant 100 | * 101 | */ 102 | export interface TenantInfo { 103 | /** 104 | * @member {string} [id] Unique identifier representing a tenant 105 | */ 106 | id?: string; 107 | } 108 | 109 | /** 110 | * @interface 111 | * An interface representing TeamsChannelData. 112 | * Channel data specific to messages received in Microsoft Teams 113 | * 114 | */ 115 | export interface TeamsChannelData { 116 | /** 117 | * @member {ChannelInfo} [channel] Information about the channel in which the 118 | * message was sent 119 | */ 120 | channel?: ChannelInfo; 121 | /** 122 | * @member {string} [eventType] Type of event. 123 | */ 124 | eventType?: string; 125 | /** 126 | * @member {TeamInfo} [team] Information about the team in which the message 127 | * was sent 128 | */ 129 | team?: TeamInfo; 130 | /** 131 | * @member {NotificationInfo} [notification] Notification settings for the 132 | * message 133 | */ 134 | notification?: NotificationInfo; 135 | /** 136 | * @member {TenantInfo} [tenant] Information about the tenant in which the 137 | * message was sent 138 | */ 139 | tenant?: TenantInfo; 140 | } 141 | 142 | /** 143 | * @interface 144 | * An interface representing TeamsChannelAccount. 145 | * Teams channel account detailing user Azure Active Directory details. 146 | * 147 | * @extends builder.ChannelAccount 148 | */ 149 | export interface TeamsChannelAccount extends builder.ChannelAccount { 150 | /** 151 | * @member {string} [givenName] Given name part of the user name. 152 | */ 153 | givenName?: string; 154 | /** 155 | * @member {string} [surname] Surname part of the user name. 156 | */ 157 | surname?: string; 158 | /** 159 | * @member {string} [email] Email Id of the user. 160 | */ 161 | email?: string; 162 | /** 163 | * @member {string} [userPrincipalName] Unique user principal name 164 | */ 165 | userPrincipalName?: string; 166 | } 167 | 168 | /** 169 | * @interface 170 | * An interface representing O365ConnectorCardFact. 171 | * O365 connector card fact 172 | * 173 | */ 174 | export interface O365ConnectorCardFact { 175 | /** 176 | * @member {string} [name] Display name of the fact 177 | */ 178 | name?: string; 179 | /** 180 | * @member {string} [value] Display value for the fact 181 | */ 182 | value?: string; 183 | } 184 | 185 | /** 186 | * @interface 187 | * An interface representing O365ConnectorCardImage. 188 | * O365 connector card image 189 | * 190 | */ 191 | export interface O365ConnectorCardImage { 192 | /** 193 | * @member {string} [image] URL for the image 194 | */ 195 | image?: string; 196 | /** 197 | * @member {string} [title] Alternative text for the image 198 | */ 199 | title?: string; 200 | } 201 | 202 | /** 203 | * @interface 204 | * An interface representing O365ConnectorCardSection. 205 | * O365 connector card section 206 | * 207 | */ 208 | export interface O365ConnectorCardSection { 209 | /** 210 | * @member {string} [title] Title of the section 211 | */ 212 | title?: string; 213 | /** 214 | * @member {string} [text] Text for the section 215 | */ 216 | text?: string; 217 | /** 218 | * @member {string} [activityTitle] builder.Activity title 219 | */ 220 | activityTitle?: string; 221 | /** 222 | * @member {string} [activitySubtitle] builder.Activity subtitle 223 | */ 224 | activitySubtitle?: string; 225 | /** 226 | * @member {string} [activityText] builder.Activity text 227 | */ 228 | activityText?: string; 229 | /** 230 | * @member {string} [activityImage] builder.Activity image 231 | */ 232 | activityImage?: string; 233 | /** 234 | * @member {ActivityImageType} [activityImageType] Describes how builder.Activity 235 | * image is rendered. Possible values include: 'avatar', 'article' 236 | */ 237 | activityImageType?: ActivityImageType; 238 | /** 239 | * @member {boolean} [markdown] Use markdown for all text contents. Default 240 | * vaule is true. 241 | */ 242 | markdown?: boolean; 243 | /** 244 | * @member {O365ConnectorCardFact[]} [facts] Set of facts for the current 245 | * section 246 | */ 247 | facts?: O365ConnectorCardFact[]; 248 | /** 249 | * @member {O365ConnectorCardImage[]} [images] Set of images for the current 250 | * section 251 | */ 252 | images?: O365ConnectorCardImage[]; 253 | /** 254 | * @member {O365ConnectorCardActionBase[]} [potentialAction] Set of actions 255 | * for the current section 256 | */ 257 | potentialAction?: teams.O365ConnectorCardActionBase[]; 258 | } 259 | 260 | /** 261 | * @interface 262 | * An interface representing O365ConnectorCard. 263 | * O365 connector card 264 | * 265 | */ 266 | export interface O365ConnectorCard { 267 | /** 268 | * @member {string} [title] Title of the item 269 | */ 270 | title?: string; 271 | /** 272 | * @member {string} [text] Text for the card 273 | */ 274 | text?: string; 275 | /** 276 | * @member {string} [summary] Summary for the card 277 | */ 278 | summary?: string; 279 | /** 280 | * @member {string} [themeColor] Theme color for the card 281 | */ 282 | themeColor?: string; 283 | /** 284 | * @member {O365ConnectorCardSection[]} [sections] Set of sections for the 285 | * current card 286 | */ 287 | sections?: O365ConnectorCardSection[]; 288 | /** 289 | * @member {O365ConnectorCardActionBase[]} [potentialAction] Set of actions 290 | * for the current card 291 | */ 292 | potentialAction?: teams.O365ConnectorCardActionBase[]; 293 | } 294 | 295 | /** 296 | * @interface 297 | * An interface representing O365ConnectorCardViewAction. 298 | * O365 connector card ViewAction action 299 | * 300 | * @extends teams.O365ConnectorCardActionBase 301 | */ 302 | export interface O365ConnectorCardViewAction extends teams.O365ConnectorCardActionBase { 303 | /** 304 | * @member {string[]} [target] Target urls, only the first url effective for 305 | * card button 306 | */ 307 | target?: string[]; 308 | } 309 | 310 | /** 311 | * @interface 312 | * An interface representing O365ConnectorCardOpenUriTarget. 313 | * O365 connector card OpenUri target 314 | * 315 | */ 316 | export interface O365ConnectorCardOpenUriTarget { 317 | /** 318 | * @member {Os} [os] Target operating system. Possible values include: 319 | * 'default', 'iOS', 'android', 'windows' 320 | */ 321 | os?: Os; 322 | /** 323 | * @member {string} [uri] Target url 324 | */ 325 | uri?: string; 326 | } 327 | 328 | /** 329 | * @interface 330 | * An interface representing O365ConnectorCardOpenUri. 331 | * O365 connector card OpenUri action 332 | * 333 | * @extends teams.O365ConnectorCardActionBase 334 | */ 335 | export interface O365ConnectorCardOpenUri extends teams.O365ConnectorCardActionBase { 336 | /** 337 | * @member {O365ConnectorCardOpenUriTarget[]} [targets] Target os / urls 338 | */ 339 | targets?: O365ConnectorCardOpenUriTarget[]; 340 | } 341 | 342 | /** 343 | * @interface 344 | * An interface representing O365ConnectorCardHttpPOST. 345 | * O365 connector card HttpPOST action 346 | * 347 | * @extends teams.O365ConnectorCardActionBase 348 | */ 349 | export interface O365ConnectorCardHttpPOST extends teams.O365ConnectorCardActionBase { 350 | /** 351 | * @member {string} [body] Content to be posted back to bots via invoke 352 | */ 353 | body?: string; 354 | } 355 | 356 | /** 357 | * @interface 358 | * An interface representing O365ConnectorCardActionCard. 359 | * O365 connector card ActionCard action 360 | * 361 | * @extends teams.O365ConnectorCardActionBase 362 | */ 363 | export interface O365ConnectorCardActionCard extends teams.O365ConnectorCardActionBase { 364 | /** 365 | * @member {O365ConnectorCardInputBase[]} [inputs] Set of inputs contained in 366 | * this ActionCard whose each item can be in any subtype of 367 | * teams.O365ConnectorCardInputBase 368 | */ 369 | inputs?: teams.O365ConnectorCardInputBase[]; 370 | /** 371 | * @member {O365ConnectorCardActionBase[]} [actions] Set of actions contained 372 | * in this ActionCard whose each item can be in any subtype of 373 | * teams.O365ConnectorCardActionBase except O365ConnectorCardActionCard, as nested 374 | * ActionCard is forbidden. 375 | */ 376 | actions?: teams.O365ConnectorCardActionBase[]; 377 | } 378 | 379 | /** 380 | * @interface 381 | * An interface representing O365ConnectorCardTextInput. 382 | * O365 connector card text input 383 | * 384 | * @extends teams.O365ConnectorCardInputBase 385 | */ 386 | export interface O365ConnectorCardTextInput extends teams.O365ConnectorCardInputBase { 387 | /** 388 | * @member {boolean} [isMultiline] Define if text input is allowed for 389 | * multiple lines. Default value is false. 390 | */ 391 | isMultiline?: boolean; 392 | /** 393 | * @member {number} [maxLength] Maximum length of text input. Default value 394 | * is unlimited. 395 | */ 396 | maxLength?: number; 397 | } 398 | 399 | /** 400 | * @interface 401 | * An interface representing O365ConnectorCardDateInput. 402 | * O365 connector card date input 403 | * 404 | * @extends teams.O365ConnectorCardInputBase 405 | */ 406 | export interface O365ConnectorCardDateInput extends teams.O365ConnectorCardInputBase { 407 | /** 408 | * @member {boolean} [includeTime] Include time input field. Default value 409 | * is false (date only). 410 | */ 411 | includeTime?: boolean; 412 | } 413 | 414 | /** 415 | * @interface 416 | * An interface representing O365ConnectorCardMultichoiceInputChoice. 417 | * O365O365 connector card multiple choice input item 418 | * 419 | */ 420 | export interface O365ConnectorCardMultichoiceInputChoice { 421 | /** 422 | * @member {string} [display] The text rednered on ActionCard. 423 | */ 424 | display?: string; 425 | /** 426 | * @member {string} [value] The value received as results. 427 | */ 428 | value?: string; 429 | } 430 | 431 | /** 432 | * @interface 433 | * An interface representing O365ConnectorCardMultichoiceInput. 434 | * O365 connector card multiple choice input 435 | * 436 | * @extends teams.O365ConnectorCardInputBase 437 | */ 438 | export interface O365ConnectorCardMultichoiceInput extends teams.O365ConnectorCardInputBase { 439 | /** 440 | * @member {O365ConnectorCardMultichoiceInputChoice[]} [choices] Set of 441 | * choices whose each item can be in any subtype of 442 | * O365ConnectorCardMultichoiceInputChoice. 443 | */ 444 | choices?: O365ConnectorCardMultichoiceInputChoice[]; 445 | /** 446 | * @member {Style} [style] Choice item rendering style. Default valud is 447 | * 'compact'. Possible values include: 'compact', 'expanded' 448 | */ 449 | style?: Style; 450 | /** 451 | * @member {boolean} [isMultiSelect] Define if this input field allows 452 | * multiple selections. Default value is false. 453 | */ 454 | isMultiSelect?: boolean; 455 | } 456 | 457 | /** 458 | * @interface 459 | * An interface representing O365ConnectorCardActionQuery. 460 | * O365 connector card HttpPOST invoke query 461 | * 462 | */ 463 | export interface O365ConnectorCardActionQuery { 464 | /** 465 | * @member {string} [body] The results of body string defined in 466 | * IO365ConnectorCardHttpPOST with substituted input values 467 | */ 468 | body?: string; 469 | /** 470 | * @member {string} [actionId] Action Id associated with the HttpPOST action 471 | * button triggered, defined in teams.O365ConnectorCardActionBase. 472 | */ 473 | actionId?: string; 474 | } 475 | 476 | /** 477 | * @interface 478 | * An interface representing SigninStateVerificationQuery. 479 | * Signin state (part of signin action auth flow) verification invoke query 480 | * 481 | */ 482 | export interface SigninStateVerificationQuery { 483 | /** 484 | * @member {string} [state] The state string originally received when the 485 | * signin web flow is finished with a state posted back to client via tab SDK 486 | * microsoftTeams.authentication.notifySuccess(state) 487 | */ 488 | state?: string; 489 | } 490 | 491 | /** 492 | * @interface 493 | * An interface representing MessagingExtensionQueryOptions. 494 | * Messaging extension query options 495 | * 496 | */ 497 | export interface MessagingExtensionQueryOptions { 498 | /** 499 | * @member {number} [skip] Number of entities to skip 500 | */ 501 | skip?: number; 502 | /** 503 | * @member {number} [count] Number of entities to fetch 504 | */ 505 | count?: number; 506 | } 507 | 508 | /** 509 | * @interface 510 | * An interface representing MessagingExtensionParameter. 511 | * Messaging extension query parameters 512 | * 513 | */ 514 | export interface MessagingExtensionParameter { 515 | /** 516 | * @member {string} [name] Name of the parameter 517 | */ 518 | name?: string; 519 | /** 520 | * @member {any} [value] Value of the parameter 521 | */ 522 | value?: any; 523 | } 524 | 525 | /** 526 | * @interface 527 | * An interface representing MessagingExtensionQuery. 528 | * Messaging extension query 529 | * 530 | */ 531 | export interface MessagingExtensionQuery { 532 | /** 533 | * @member {string} [commandId] Id of the command assigned by Bot 534 | */ 535 | commandId?: string; 536 | /** 537 | * @member {MessagingExtensionParameter[]} [parameters] Parameters for the 538 | * query 539 | */ 540 | parameters?: MessagingExtensionParameter[]; 541 | /** 542 | * @member {MessagingExtensionQueryOptions} [queryOptions] 543 | */ 544 | queryOptions?: MessagingExtensionQueryOptions; 545 | /** 546 | * @member {string} [state] State parameter passed back to the bot after 547 | * authentication/configuration flow 548 | */ 549 | state?: string; 550 | } 551 | 552 | /** 553 | * @interface 554 | * An interface representing MessageActionsPayloadUser. 555 | * Represents a user entity. 556 | * 557 | */ 558 | export interface MessageActionsPayloadUser { 559 | /** 560 | * @member {UserIdentityType} [userIdentityType] The identity type of the 561 | * user. Possible values include: 'aadUser', 'onPremiseAadUser', 562 | * 'anonymousGuest', 'federatedUser' 563 | */ 564 | userIdentityType?: UserIdentityType; 565 | /** 566 | * @member {string} [id] The id of the user. 567 | */ 568 | id?: string; 569 | /** 570 | * @member {string} [displayName] The plaintext display name of the user. 571 | */ 572 | displayName?: string; 573 | } 574 | 575 | /** 576 | * @interface 577 | * An interface representing MessageActionsPayloadApp. 578 | * Represents an application entity. 579 | * 580 | */ 581 | export interface MessageActionsPayloadApp { 582 | /** 583 | * @member {ApplicationIdentityType} [applicationIdentityType] The type of 584 | * application. Possible values include: 'aadApplication', 'bot', 585 | * 'tenantBot', 'office365Connector', 'webhook' 586 | */ 587 | applicationIdentityType?: ApplicationIdentityType; 588 | /** 589 | * @member {string} [id] The id of the application. 590 | */ 591 | id?: string; 592 | /** 593 | * @member {string} [displayName] The plaintext display name of the 594 | * application. 595 | */ 596 | displayName?: string; 597 | } 598 | 599 | /** 600 | * @interface 601 | * An interface representing MessageActionsPayloadConversation. 602 | * Represents a team or channel entity. 603 | * 604 | */ 605 | export interface MessageActionsPayloadConversation { 606 | /** 607 | * @member {ConversationIdentityType} [conversationIdentityType] The type of 608 | * conversation, whether a team or channel. Possible values include: 'team', 609 | * 'channel' 610 | */ 611 | conversationIdentityType?: ConversationIdentityType; 612 | /** 613 | * @member {string} [id] The id of the team or channel. 614 | */ 615 | id?: string; 616 | /** 617 | * @member {string} [displayName] The plaintext display name of the team or 618 | * channel entity. 619 | */ 620 | displayName?: string; 621 | } 622 | 623 | /** 624 | * @interface 625 | * An interface representing MessageActionsPayloadFrom. 626 | * Represents a user, application, or conversation type that either sent or was 627 | * referenced in a message. 628 | * 629 | */ 630 | export interface MessageActionsPayloadFrom { 631 | /** 632 | * @member {MessageActionsPayloadUser} [user] Represents details of the user. 633 | */ 634 | user?: MessageActionsPayloadUser; 635 | /** 636 | * @member {MessageActionsPayloadApp} [application] Represents details of the 637 | * app. 638 | */ 639 | application?: MessageActionsPayloadApp; 640 | /** 641 | * @member {MessageActionsPayloadConversation} [conversation] Represents 642 | * details of the converesation. 643 | */ 644 | conversation?: MessageActionsPayloadConversation; 645 | } 646 | 647 | /** 648 | * @interface 649 | * An interface representing MessageActionsPayloadAttachment. 650 | * Represents the attachment in a message. 651 | * 652 | */ 653 | export interface MessageActionsPayloadAttachment { 654 | /** 655 | * @member {string} [id] The id of the attachment. 656 | */ 657 | id?: string; 658 | /** 659 | * @member {string} [contentType] The type of the attachment. 660 | */ 661 | contentType?: string; 662 | /** 663 | * @member {string} [contentUrl] The url of the attachment, in case of a 664 | * external link. 665 | */ 666 | contentUrl?: string; 667 | /** 668 | * @member {any} [content] The content of the attachment, in case of a code 669 | * snippet, email, or file. 670 | */ 671 | content?: any; 672 | /** 673 | * @member {string} [name] The plaintext display name of the attachment. 674 | */ 675 | name?: string; 676 | /** 677 | * @member {string} [thumbnailUrl] The url of a thumbnail image that might be 678 | * embedded in the attachment, in case of a card. 679 | */ 680 | thumbnailUrl?: string; 681 | } 682 | 683 | /** 684 | * @interface 685 | * An interface representing MessageActionsPayloadMention. 686 | * Represents the entity that was mentioned in the message. 687 | * 688 | */ 689 | export interface MessageActionsPayloadMention { 690 | /** 691 | * @member {number} [id] The id of the mentioned entity. 692 | */ 693 | id?: number; 694 | /** 695 | * @member {string} [mentionText] The plaintext display name of the mentioned 696 | * entity. 697 | */ 698 | mentionText?: string; 699 | /** 700 | * @member {MessageActionsPayloadFrom} [mentioned] Provides more details on 701 | * the mentioned entity. 702 | */ 703 | mentioned?: MessageActionsPayloadFrom; 704 | } 705 | 706 | /** 707 | * @interface 708 | * An interface representing MessageActionsPayloadReaction. 709 | * Represents the reaction of a user to a message. 710 | * 711 | */ 712 | export interface MessageActionsPayloadReaction { 713 | /** 714 | * @member {ReactionType} [reactionType] The type of reaction given to the 715 | * message. Possible values include: 'like', 'heart', 'laugh', 'surprised', 716 | * 'sad', 'angry' 717 | */ 718 | reactionType?: ReactionType; 719 | /** 720 | * @member {string} [createdDateTime] Timestamp of when the user reacted to 721 | * the message. 722 | */ 723 | createdDateTime?: string; 724 | /** 725 | * @member {MessageActionsPayloadFrom} [user] The user with which the 726 | * reaction is associated. 727 | */ 728 | user?: MessageActionsPayloadFrom; 729 | } 730 | 731 | /** 732 | * @interface 733 | * An interface representing MessageActionsPayload. 734 | * Represents the individual message within a chat or channel where a message 735 | * actions is taken. 736 | * 737 | */ 738 | export interface MessageActionsPayload { 739 | /** 740 | * @member {string} [id] Unique id of the message. 741 | */ 742 | id?: string; 743 | /** 744 | * @member {string} [replyToId] Id of the parent/root message of the thread. 745 | */ 746 | replyToId?: string; 747 | /** 748 | * @member {MessageType} [messageType] Type of message - automatically set to 749 | * message. Possible values include: 'message' 750 | */ 751 | messageType?: MessageType; 752 | /** 753 | * @member {string} [createdDateTime] Timestamp of when the message was 754 | * created. 755 | */ 756 | createdDateTime?: string; 757 | /** 758 | * @member {string} [lastModifiedDateTime] Timestamp of when the message was 759 | * edited or updated. 760 | */ 761 | lastModifiedDateTime?: string; 762 | /** 763 | * @member {boolean} [deleted] Indicates whether a message has been soft 764 | * deleted. 765 | */ 766 | deleted?: boolean; 767 | /** 768 | * @member {string} [subject] Subject line of the message. 769 | */ 770 | subject?: string; 771 | /** 772 | * @member {string} [summary] Summary text of the message that could be used 773 | * for notifications. 774 | */ 775 | summary?: string; 776 | /** 777 | * @member {Importance} [importance] The importance of the message. Possible 778 | * values include: 'normal', 'high', 'urgent' 779 | */ 780 | importance?: Importance; 781 | /** 782 | * @member {string} [locale] Locale of the message set by the client. 783 | */ 784 | locale?: string; 785 | /** 786 | * @member {MessageActionsPayloadFrom} [from] Sender of the message. 787 | */ 788 | from?: MessageActionsPayloadFrom; 789 | /** 790 | * @member {MessageActionsPayloadBody} [body] Plaintext/HTML representation 791 | * of the content of the message. 792 | */ 793 | body?: teams.MessageActionsPayloadBody; 794 | /** 795 | * @member {string} [attachmentLayout] How the attachment(s) are displayed in 796 | * the message. 797 | */ 798 | attachmentLayout?: string; 799 | /** 800 | * @member {MessageActionsPayloadAttachment[]} [attachments] Attachments in 801 | * the message - card, image, file, etc. 802 | */ 803 | attachments?: MessageActionsPayloadAttachment[]; 804 | /** 805 | * @member {MessageActionsPayloadMention[]} [mentions] List of entities 806 | * mentioned in the message. 807 | */ 808 | mentions?: MessageActionsPayloadMention[]; 809 | /** 810 | * @member {MessageActionsPayloadReaction[]} [reactions] Reactions for the 811 | * message. 812 | */ 813 | reactions?: MessageActionsPayloadReaction[]; 814 | } 815 | 816 | /** 817 | * @interface 818 | * An interface representing TaskModuleRequest. 819 | * Task module invoke request value payload 820 | * 821 | */ 822 | export interface TaskModuleRequest { 823 | /** 824 | * @member {any} [data] User input data. Free payload with key-value pairs. 825 | */ 826 | data?: any; 827 | /** 828 | * @member {TaskModuleRequestContext} [context] Current user context, i.e., 829 | * the current theme 830 | */ 831 | context?: TaskModuleRequestContext; 832 | } 833 | 834 | /** 835 | * @interface 836 | * An interface representing MessagingExtensionAction. 837 | * Messaging extension action 838 | * 839 | * @extends TaskModuleRequest 840 | */ 841 | export interface MessagingExtensionAction extends TaskModuleRequest { 842 | /** 843 | * @member {string} [commandId] Id of the command assigned by Bot 844 | */ 845 | commandId?: string; 846 | /** 847 | * @member {CommandContext} [commandContext] The context from which the 848 | * command originates. Possible values include: 'message', 'compose', 849 | * 'commandbox' 850 | */ 851 | commandContext?: CommandContext; 852 | /** 853 | * @member {BotMessagePreviewAction} [botMessagePreviewAction] Bot message 854 | * preview action taken by user. Possible values include: 'edit', 'send' 855 | */ 856 | botMessagePreviewAction?: BotMessagePreviewAction; 857 | /** 858 | * @member {Activity[]} [botActivityPreview] 859 | */ 860 | botActivityPreview?: builder.Activity[]; 861 | /** 862 | * @member {MessageActionsPayload} [messagePayload] Message content sent as 863 | * part of the command request. 864 | */ 865 | messagePayload?: MessageActionsPayload; 866 | } 867 | 868 | /** 869 | * @interface 870 | * An interface representing TaskModuleResponseBase. 871 | * Base class for Task Module responses 872 | * 873 | */ 874 | export interface TaskModuleResponseBase { 875 | /** 876 | * @member {Type2} [type] Choice of action options when responding to the 877 | * task/submit message. Possible values include: 'message', 'continue' 878 | */ 879 | type?: Type2; 880 | } 881 | 882 | /** 883 | * @interface 884 | * An interface representing MessagingExtensionAttachment. 885 | * Messaging extension attachment. 886 | * 887 | * @extends builder.Attachment 888 | */ 889 | export interface MessagingExtensionAttachment extends builder.Attachment { 890 | /** 891 | * @member {Attachment} [preview] 892 | */ 893 | preview?: builder.Attachment; 894 | } 895 | 896 | /** 897 | * @interface 898 | * An interface representing MessagingExtensionSuggestedAction. 899 | * Messaging extension Actions (Only when type is auth or config) 900 | * 901 | */ 902 | export interface MessagingExtensionSuggestedAction { 903 | /** 904 | * @member {CardAction[]} [actions] Actions 905 | */ 906 | actions?: builder.CardAction[]; 907 | } 908 | 909 | /** 910 | * @interface 911 | * An interface representing MessagingExtensionResult. 912 | * Messaging extension result 913 | * 914 | */ 915 | export interface MessagingExtensionResult { 916 | /** 917 | * @member {AttachmentLayout} [attachmentLayout] Hint for how to deal with 918 | * multiple attachments. Possible values include: 'list', 'grid' 919 | */ 920 | attachmentLayout?: AttachmentLayout; 921 | /** 922 | * @member {Type3} [type] The type of the result. Possible values include: 923 | * 'result', 'auth', 'config', 'message', 'botMessagePreview' 924 | */ 925 | type?: Type3; 926 | /** 927 | * @member {MessagingExtensionAttachment[]} [attachments] (Only when type is 928 | * result) Attachments 929 | */ 930 | attachments?: MessagingExtensionAttachment[]; 931 | /** 932 | * @member {MessagingExtensionSuggestedAction} [suggestedActions] 933 | */ 934 | suggestedActions?: MessagingExtensionSuggestedAction; 935 | /** 936 | * @member {string} [text] (Only when type is message) Text 937 | */ 938 | text?: string; 939 | /** 940 | * @member {Activity} [activityPreview] (Only when type is botMessagePreview) 941 | * Message activity to preview 942 | */ 943 | activityPreview?: builder.Activity; 944 | } 945 | 946 | /** 947 | * @interface 948 | * An interface representing MessagingExtensionActionResponse. 949 | * Response of messaging extension action 950 | * 951 | */ 952 | export interface MessagingExtensionActionResponse { 953 | /** 954 | * @member {TaskModuleResponseBase} [task] The JSON for the Adaptive card to 955 | * appear in the task module. 956 | */ 957 | task?: TaskModuleResponseBase; 958 | /** 959 | * @member {MessagingExtensionResult} [composeExtension] 960 | */ 961 | composeExtension?: MessagingExtensionResult; 962 | } 963 | 964 | /** 965 | * @interface 966 | * An interface representing MessagingExtensionResponse. 967 | * Messaging extension response 968 | * 969 | */ 970 | export interface MessagingExtensionResponse { 971 | /** 972 | * @member {MessagingExtensionResult} [composeExtension] 973 | */ 974 | composeExtension?: MessagingExtensionResult; 975 | } 976 | 977 | /** 978 | * @interface 979 | * An interface representing FileConsentCard. 980 | * File consent card attachment. 981 | * 982 | */ 983 | export interface FileConsentCard { 984 | /** 985 | * @member {string} [description] File description. 986 | */ 987 | description?: string; 988 | /** 989 | * @member {number} [sizeInBytes] Size of the file to be uploaded in Bytes. 990 | */ 991 | sizeInBytes?: number; 992 | /** 993 | * @member {any} [acceptContext] Context sent back to the Bot if user 994 | * consented to upload. This is free flow schema and is sent back in Value 995 | * field of builder.Activity. 996 | */ 997 | acceptContext?: any; 998 | /** 999 | * @member {any} [declineContext] Context sent back to the Bot if user 1000 | * declined. This is free flow schema and is sent back in Value field of 1001 | * builder.Activity. 1002 | */ 1003 | declineContext?: any; 1004 | } 1005 | 1006 | /** 1007 | * @interface 1008 | * An interface representing FileDownloadInfo. 1009 | * File download info attachment. 1010 | * 1011 | */ 1012 | export interface FileDownloadInfo { 1013 | /** 1014 | * @member {string} [downloadUrl] File download url. 1015 | */ 1016 | downloadUrl?: string; 1017 | /** 1018 | * @member {string} [uniqueId] Unique Id for the file. 1019 | */ 1020 | uniqueId?: string; 1021 | /** 1022 | * @member {string} [fileType] Type of file. 1023 | */ 1024 | fileType?: string; 1025 | /** 1026 | * @member {any} [etag] ETag for the file. 1027 | */ 1028 | etag?: any; 1029 | } 1030 | 1031 | /** 1032 | * @interface 1033 | * An interface representing FileInfoCard. 1034 | * File info card. 1035 | * 1036 | */ 1037 | export interface FileInfoCard { 1038 | /** 1039 | * @member {string} [uniqueId] Unique Id for the file. 1040 | */ 1041 | uniqueId?: string; 1042 | /** 1043 | * @member {string} [fileType] Type of file. 1044 | */ 1045 | fileType?: string; 1046 | /** 1047 | * @member {any} [etag] ETag for the file. 1048 | */ 1049 | etag?: any; 1050 | } 1051 | 1052 | /** 1053 | * @interface 1054 | * An interface representing FileUploadInfo. 1055 | * Information about the file to be uploaded. 1056 | * 1057 | */ 1058 | export interface FileUploadInfo { 1059 | /** 1060 | * @member {string} [name] Name of the file. 1061 | */ 1062 | name?: string; 1063 | /** 1064 | * @member {string} [uploadUrl] URL to an upload session that the bot can use 1065 | * to set the file contents. 1066 | */ 1067 | uploadUrl?: string; 1068 | /** 1069 | * @member {string} [contentUrl] URL to file. 1070 | */ 1071 | contentUrl?: string; 1072 | /** 1073 | * @member {string} [uniqueId] ID that uniquely identifies the file. 1074 | */ 1075 | uniqueId?: string; 1076 | /** 1077 | * @member {string} [fileType] Type of the file. 1078 | */ 1079 | fileType?: string; 1080 | } 1081 | 1082 | /** 1083 | * @interface 1084 | * An interface representing FileConsentCardResponse. 1085 | * Represents the value of the invoke activity sent when the user acts on a 1086 | * file consent card 1087 | * 1088 | */ 1089 | export interface FileConsentCardResponse { 1090 | /** 1091 | * @member {Action} [action] The action the user took. Possible values 1092 | * include: 'accept', 'decline' 1093 | */ 1094 | action?: Action; 1095 | /** 1096 | * @member {any} [context] The context associated with the action. 1097 | */ 1098 | context?: any; 1099 | /** 1100 | * @member {FileUploadInfo} [uploadInfo] If the user accepted the file, 1101 | * contains information about the file to be uploaded. 1102 | */ 1103 | uploadInfo?: FileUploadInfo; 1104 | } 1105 | 1106 | /** 1107 | * @interface 1108 | * An interface representing TaskModuleTaskInfo. 1109 | * Metadata for a Task Module. 1110 | * 1111 | */ 1112 | export interface TaskModuleTaskInfo { 1113 | /** 1114 | * @member {string} [title] Appears below the app name and to the right of 1115 | * the app icon. 1116 | */ 1117 | title?: string; 1118 | /** 1119 | * @member {any} [height] This can be a number, representing the task 1120 | * module's height in pixels, or a string, one of: small, medium, large. 1121 | */ 1122 | height?: any; 1123 | /** 1124 | * @member {any} [width] This can be a number, representing the task module's 1125 | * width in pixels, or a string, one of: small, medium, large. 1126 | */ 1127 | width?: any; 1128 | /** 1129 | * @member {string} [url] The URL of what is loaded as an iframe inside the 1130 | * task module. One of url or card is required. 1131 | */ 1132 | url?: string; 1133 | /** 1134 | * @member {Attachment} [card] The JSON for the Adaptive card to appear in 1135 | * the task module. 1136 | */ 1137 | card?: builder.Attachment; 1138 | /** 1139 | * @member {string} [fallbackUrl] If a client does not support the task 1140 | * module feature, this URL is opened in a browser tab. 1141 | */ 1142 | fallbackUrl?: string; 1143 | /** 1144 | * @member {string} [completionBotId] If a client does not support the task 1145 | * module feature, this URL is opened in a browser tab. 1146 | */ 1147 | completionBotId?: string; 1148 | } 1149 | 1150 | /** 1151 | * @interface 1152 | * An interface representing TaskModuleContinueResponse. 1153 | * Task Module Response with continue action. 1154 | * 1155 | * @extends TaskModuleResponseBase 1156 | */ 1157 | export interface TaskModuleContinueResponse extends TaskModuleResponseBase { 1158 | /** 1159 | * @member {TaskModuleTaskInfo} [value] The JSON for the Adaptive card to 1160 | * appear in the task module. 1161 | */ 1162 | value?: TaskModuleTaskInfo; 1163 | } 1164 | 1165 | /** 1166 | * @interface 1167 | * An interface representing TaskModuleMessageResponse. 1168 | * Task Module response with message action. 1169 | * 1170 | * @extends TaskModuleResponseBase 1171 | */ 1172 | export interface TaskModuleMessageResponse extends TaskModuleResponseBase { 1173 | /** 1174 | * @member {string} [value] Teams will display the value of value in a popup 1175 | * message box. 1176 | */ 1177 | value?: string; 1178 | } 1179 | 1180 | /** 1181 | * @interface 1182 | * An interface representing TaskModuleResponse. 1183 | * Envelope for Task Module Response. 1184 | * 1185 | */ 1186 | export interface TaskModuleResponse { 1187 | /** 1188 | * @member {TaskModuleResponseBase} [task] The JSON for the Adaptive card to 1189 | * appear in the task module. 1190 | */ 1191 | task?: TaskModuleResponseBase; 1192 | } 1193 | 1194 | /** 1195 | * @interface 1196 | * An interface representing TaskModuleRequestContext. 1197 | * Current user context, i.e., the current theme 1198 | * 1199 | */ 1200 | export interface TaskModuleRequestContext { 1201 | /** 1202 | * @member {string} [theme] 1203 | */ 1204 | theme?: string; 1205 | } 1206 | 1207 | /** 1208 | * @interface 1209 | * An interface representing AppBasedLinkQuery. 1210 | * Invoke request body type for app-based link query. 1211 | * 1212 | */ 1213 | export interface AppBasedLinkQuery { 1214 | /** 1215 | * @member {string} [url] Url queried by user 1216 | */ 1217 | url?: string; 1218 | } 1219 | 1220 | /** 1221 | * @interface 1222 | * An interface representing TeamsConnectorClientOptions. 1223 | * @extends ServiceClientOptions 1224 | */ 1225 | export interface TeamsConnectorClientOptions extends ServiceClientOptions { 1226 | /** 1227 | * @member {string} [baseUri] 1228 | */ 1229 | baseUri?: string; 1230 | } 1231 | 1232 | /** 1233 | * Defines values for Type. 1234 | * Possible values include: 'ViewAction', 'OpenUri', 'HttpPOST', 'ActionCard' 1235 | * @readonly 1236 | * @enum {string} 1237 | */ 1238 | export type Type = 'ViewAction' | 'OpenUri' | 'HttpPOST' | 'ActionCard'; 1239 | 1240 | /** 1241 | * Defines values for ActivityImageType. 1242 | * Possible values include: 'avatar', 'article' 1243 | * @readonly 1244 | * @enum {string} 1245 | */ 1246 | export type ActivityImageType = 'avatar' | 'article'; 1247 | 1248 | /** 1249 | * Defines values for Os. 1250 | * Possible values include: 'default', 'iOS', 'android', 'windows' 1251 | * @readonly 1252 | * @enum {string} 1253 | */ 1254 | export type Os = 'default' | 'iOS' | 'android' | 'windows'; 1255 | 1256 | /** 1257 | * Defines values for Type1. 1258 | * Possible values include: 'textInput', 'dateInput', 'multichoiceInput' 1259 | * @readonly 1260 | * @enum {string} 1261 | */ 1262 | export type Type1 = 'textInput' | 'dateInput' | 'multichoiceInput'; 1263 | 1264 | /** 1265 | * Defines values for Style. 1266 | * Possible values include: 'compact', 'expanded' 1267 | * @readonly 1268 | * @enum {string} 1269 | */ 1270 | export type Style = 'compact' | 'expanded'; 1271 | 1272 | /** 1273 | * Defines values for UserIdentityType. 1274 | * Possible values include: 'aadUser', 'onPremiseAadUser', 'anonymousGuest', 'federatedUser' 1275 | * @readonly 1276 | * @enum {string} 1277 | */ 1278 | export type UserIdentityType = 'aadUser' | 'onPremiseAadUser' | 'anonymousGuest' | 'federatedUser'; 1279 | 1280 | /** 1281 | * Defines values for ApplicationIdentityType. 1282 | * Possible values include: 'aadApplication', 'bot', 'tenantBot', 'office365Connector', 'webhook' 1283 | * @readonly 1284 | * @enum {string} 1285 | */ 1286 | export type ApplicationIdentityType = 'aadApplication' | 'bot' | 'tenantBot' | 'office365Connector' | 'webhook'; 1287 | 1288 | /** 1289 | * Defines values for ConversationIdentityType. 1290 | * Possible values include: 'team', 'channel' 1291 | * @readonly 1292 | * @enum {string} 1293 | */ 1294 | export type ConversationIdentityType = 'team' | 'channel'; 1295 | 1296 | /** 1297 | * Defines values for ContentType. 1298 | * Possible values include: 'html', 'text' 1299 | * @readonly 1300 | * @enum {string} 1301 | */ 1302 | export type ContentType = 'html' | 'text'; 1303 | 1304 | /** 1305 | * Defines values for ReactionType. 1306 | * Possible values include: 'like', 'heart', 'laugh', 'surprised', 'sad', 'angry' 1307 | * @readonly 1308 | * @enum {string} 1309 | */ 1310 | export type ReactionType = 'like' | 'heart' | 'laugh' | 'surprised' | 'sad' | 'angry'; 1311 | 1312 | /** 1313 | * Defines values for MessageType. 1314 | * Possible values include: 'message' 1315 | * @readonly 1316 | * @enum {string} 1317 | */ 1318 | export type MessageType = 'message'; 1319 | 1320 | /** 1321 | * Defines values for Importance. 1322 | * Possible values include: 'normal', 'high', 'urgent' 1323 | * @readonly 1324 | * @enum {string} 1325 | */ 1326 | export type Importance = 'normal' | 'high' | 'urgent'; 1327 | 1328 | /** 1329 | * Defines values for CommandContext. 1330 | * Possible values include: 'message', 'compose', 'commandbox' 1331 | * @readonly 1332 | * @enum {string} 1333 | */ 1334 | export type CommandContext = 'message' | 'compose' | 'commandbox'; 1335 | 1336 | /** 1337 | * Defines values for BotMessagePreviewAction. 1338 | * Possible values include: 'edit', 'send' 1339 | * @readonly 1340 | * @enum {string} 1341 | */ 1342 | export type BotMessagePreviewAction = 'edit' | 'send'; 1343 | 1344 | /** 1345 | * Defines values for Type2. 1346 | * Possible values include: 'message', 'continue' 1347 | * @readonly 1348 | * @enum {string} 1349 | */ 1350 | export type Type2 = 'message' | 'continue'; 1351 | 1352 | /** 1353 | * Defines values for AttachmentLayout. 1354 | * Possible values include: 'list', 'grid' 1355 | * @readonly 1356 | * @enum {string} 1357 | */ 1358 | export type AttachmentLayout = 'list' | 'grid'; 1359 | 1360 | /** 1361 | * Defines values for Type3. 1362 | * Possible values include: 'result', 'auth', 'config', 'message', 'botMessagePreview' 1363 | * @readonly 1364 | * @enum {string} 1365 | */ 1366 | export type Type3 = 'result' | 'auth' | 'config' | 'message' | 'botMessagePreview'; 1367 | 1368 | /** 1369 | * Defines values for Action. 1370 | * Possible values include: 'accept', 'decline' 1371 | * @readonly 1372 | * @enum {string} 1373 | */ 1374 | export type Action = 'accept' | 'decline'; 1375 | 1376 | /** 1377 | * Contains response data for the fetchChannelList operation. 1378 | */ 1379 | export type TeamsFetchChannelListResponse = ConversationList & { 1380 | /** 1381 | * The underlying HTTP response. 1382 | */ 1383 | _response: msRest.HttpResponse & { 1384 | /** 1385 | * The response body as text (string format) 1386 | */ 1387 | bodyAsText: string; 1388 | /** 1389 | * The response body as parsed JSON or XML 1390 | */ 1391 | parsedBody: ConversationList; 1392 | }; 1393 | }; 1394 | 1395 | /** 1396 | * Contains response data for the fetchTeamDetails operation. 1397 | */ 1398 | export type TeamsFetchTeamDetailsResponse = TeamDetails & { 1399 | /** 1400 | * The underlying HTTP response. 1401 | */ 1402 | _response: msRest.HttpResponse & { 1403 | /** 1404 | * The response body as text (string format) 1405 | */ 1406 | bodyAsText: string; 1407 | /** 1408 | * The response body as parsed JSON or XML 1409 | */ 1410 | parsedBody: TeamDetails; 1411 | }; 1412 | }; 1413 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/models/parameters.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | import * as msRest from '@azure/ms-rest-js'; 8 | 9 | export const teamId: msRest.OperationURLParameter = { 10 | parameterPath: 'teamId', 11 | mapper: { 12 | required: true, 13 | serializedName: 'teamId', 14 | type: { 15 | name: 'String' 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/models/teamsMappers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | export { 8 | ConversationList, 9 | ChannelInfo, 10 | TeamDetails 11 | } from '../models/mappers'; 12 | 13 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/operations/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | export * from './teams'; 8 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/schema/operations/teams.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Code generated by Microsoft (R) AutoRest Code Generator. 3 | * Changes may cause incorrect behavior and will be lost if the code is 4 | * regenerated. 5 | */ 6 | 7 | import * as msRest from '@azure/ms-rest-js'; 8 | import * as Models from '../models'; 9 | import * as Mappers from '../models/teamsMappers'; 10 | import * as Parameters from '../models/parameters'; 11 | import { TeamsConnectorClientContext } from '../../'; 12 | 13 | /** Class representing a Teams. */ 14 | export class Teams { 15 | private readonly client: TeamsConnectorClientContext; 16 | 17 | /** 18 | * Create a Teams. 19 | * @param {TeamsConnectorClientContext} client Reference to the service client. 20 | */ 21 | constructor(client: TeamsConnectorClientContext) { 22 | this.client = client; 23 | } 24 | 25 | /** 26 | * Fetch the channel list. 27 | * @summary Fetches channel list for a given team 28 | * @param teamId Team Id 29 | * @param [options] The optional parameters 30 | * @returns Promise 31 | */ 32 | fetchChannelList(teamId: string, options?: msRest.RequestOptionsBase): Promise; 33 | /** 34 | * @param teamId Team Id 35 | * @param callback The callback 36 | */ 37 | fetchChannelList(teamId: string, callback: msRest.ServiceCallback): void; 38 | /** 39 | * @param teamId Team Id 40 | * @param options The optional parameters 41 | * @param callback The callback 42 | */ 43 | fetchChannelList(teamId: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; 44 | fetchChannelList(teamId: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { 45 | return this.client.sendOperationRequest( 46 | { 47 | teamId, 48 | options 49 | }, 50 | fetchChannelListOperationSpec, 51 | callback) as Promise; 52 | } 53 | 54 | /** 55 | * Fetch details for a team 56 | * @summary Fetches details related to a team 57 | * @param teamId Team Id 58 | * @param [options] The optional parameters 59 | * @returns Promise 60 | */ 61 | fetchTeamDetails(teamId: string, options?: msRest.RequestOptionsBase): Promise; 62 | /** 63 | * @param teamId Team Id 64 | * @param callback The callback 65 | */ 66 | fetchTeamDetails(teamId: string, callback: msRest.ServiceCallback): void; 67 | /** 68 | * @param teamId Team Id 69 | * @param options The optional parameters 70 | * @param callback The callback 71 | */ 72 | fetchTeamDetails(teamId: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; 73 | fetchTeamDetails(teamId: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { 74 | return this.client.sendOperationRequest( 75 | { 76 | teamId, 77 | options 78 | }, 79 | fetchTeamDetailsOperationSpec, 80 | callback) as Promise; 81 | } 82 | } 83 | 84 | // Operation Specifications 85 | const serializer = new msRest.Serializer(Mappers); 86 | const fetchChannelListOperationSpec: msRest.OperationSpec = { 87 | httpMethod: 'GET', 88 | path: 'v3/teams/{teamId}/conversations', 89 | urlParameters: [ 90 | Parameters.teamId 91 | ], 92 | responses: { 93 | 200: { 94 | bodyMapper: Mappers.ConversationList 95 | }, 96 | default: {} 97 | }, 98 | serializer 99 | }; 100 | 101 | const fetchTeamDetailsOperationSpec: msRest.OperationSpec = { 102 | httpMethod: 'GET', 103 | path: 'v3/teams/{teamId}', 104 | urlParameters: [ 105 | Parameters.teamId 106 | ], 107 | responses: { 108 | 200: { 109 | bodyMapper: Mappers.TeamDetails 110 | }, 111 | default: {} 112 | }, 113 | serializer 114 | }; 115 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/stateStorage/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | export * from './teamSpecificConversationState'; 25 | -------------------------------------------------------------------------------- /botbuilder-teams-js/src/stateStorage/teamSpecificConversationState.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | import { TeamsChannelData } from '../schema/models'; 26 | 27 | /** 28 | * Teams specific conversation state management. 29 | */ 30 | export class TeamSpecificConversationState extends builder.BotState { 31 | /** 32 | * Initializes a new instance of the `TeamSpecificConversationState` 33 | * @param storage The storage provider to use. 34 | */ 35 | constructor (storage: builder.Storage) { 36 | super(storage, (turnContext) => this.getStorageKey(turnContext)); 37 | } 38 | 39 | /** 40 | * Gets the key to use when reading and writing state to and from storage. 41 | * @param turnContext The context object for this turn. 42 | * @returns The storage key. 43 | */ 44 | private getStorageKey (turnContext: builder.TurnContext): Promise { 45 | const teamsChannelData: TeamsChannelData = turnContext.activity.channelData; 46 | if (teamsChannelData.team && teamsChannelData.team.id) { 47 | return Promise.resolve(`team/${turnContext.activity.channelId}/${teamsChannelData.team.id}`); 48 | } else { 49 | return Promise.resolve(`chat/${turnContext.activity.channelId}/${turnContext.activity.conversation.id}`); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /botbuilder-teams-js/src/teamsAdapter.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import { BotFrameworkAdapter, ConversationReference, TurnContext, ConversationParameters, Activity, ConversationAccount, ResourceResponse, ActivityTypes } from 'botbuilder'; 25 | import { ConnectorClient } from 'botframework-connector'; 26 | import { TeamsChannelData, TeamsChannelAccount } from './schema'; 27 | import { TeamsContext } from './teamsContext'; 28 | 29 | /** 30 | * A Teams bot adapter inheriting from BotFrameworkAdapter that connects your bot to Bot Framework channels. 31 | * 32 | * @remarks 33 | * Similar to BotFrameworkAdapter, Use this adapter to connect your bot to the Bot Framework service. 34 | * As inheriting from BotFrameworkAdapter, TeamsAdapter not only provides all functionalities of a regular bot adapter, 35 | * but also provides with additional Teams-binding operations and types. 36 | * 37 | * The following example shows the typical adapter setup: 38 | * 39 | * ```JavaScript 40 | * const { TeamsAdapter } = require('botbuilder-teams'); 41 | * 42 | * const adapter = new TeamsAdapter({ 43 | * appId: process.env.MICROSOFT_APP_ID, 44 | * appPassword: process.env.MICROSOFT_APP_PASSWORD 45 | * }); 46 | * ``` 47 | */ 48 | export class TeamsAdapter extends BotFrameworkAdapter { 49 | /** 50 | * Lists the members of a given activity as specified in a TurnContext. 51 | * 52 | * @remarks 53 | * Returns an array of ChannelAccount objects representing the users involved in a given activity. 54 | * 55 | * This is different from `getConversationMembers()` in that it will return only those users 56 | * directly involved in the activity, not all members of the conversation. 57 | * @param context Context for the current turn of conversation with the user. 58 | * @param [activityId] (Optional) activity ID to enumerate. If not specified the current activities ID will be used. 59 | * @returns an array of TeamsChannelAccount for the given context and/or activityId 60 | */ 61 | public async getActivityMembers(context: TurnContext, activityId?: string): Promise { 62 | const members = await super.getActivityMembers(context, activityId) 63 | .then((res: any) => 64 | this.merge_objectId_and_aadObjectId(JSON.parse(res._response.bodyAsText))); 65 | if (TeamsContext.isTeamsChannelAccounts(members)) { 66 | return members; 67 | } else { 68 | throw new Error('Members are not TeamsChannelAccount[]'); 69 | } 70 | } 71 | 72 | /** 73 | * Lists the members of the current conversation as specified in a TurnContext. 74 | * 75 | * @remarks 76 | * Returns an array of ChannelAccount objects representing the users currently involved in the conversation 77 | * in which an activity occured. 78 | * 79 | * This is different from `getActivityMembers()` in that it will return all 80 | * members of the conversation, not just those directly involved in the activity. 81 | * @param context Context for the current turn of conversation with the user. 82 | * @returns an array of TeamsChannelAccount for the given context 83 | */ 84 | public async getConversationMembers(context: TurnContext): Promise { 85 | const members = await super.getConversationMembers(context) 86 | .then((res: any) => 87 | this.merge_objectId_and_aadObjectId(JSON.parse(res._response.bodyAsText))); 88 | if (TeamsContext.isTeamsChannelAccounts(members)) { 89 | return members; 90 | } else { 91 | throw new Error('Members are not TeamsChannelAccount[]'); 92 | } 93 | } 94 | 95 | /** 96 | * Starts a new conversation with a user. This is typically used to Direct Message (DM) a member 97 | * of a group. 98 | * 99 | * @remarks 100 | * This function creates a new conversation between the bot and a single user, as specified by 101 | * the ConversationReference passed in. In multi-user chat environments, this typically means 102 | * starting a 1:1 direct message conversation with a single user. If called on a reference 103 | * already representing a 1:1 conversation, the new conversation will continue to be 1:1. 104 | * 105 | * * In order to use this method, a ConversationReference must first be extracted from an incoming 106 | * activity. This reference can be stored in a database and used to resume the conversation at a later time. 107 | * The reference can be created from any incoming activity using `TurnContext.getConversationReference(context.activity)`. 108 | * 109 | * The processing steps for this method are very similar to [processActivity()](#processactivity) 110 | * in that a `TurnContext` will be created which is then routed through the adapters middleware 111 | * before calling the passed in logic handler. The key difference is that since an activity 112 | * wasn't actually received from outside, it has to be created by the bot. The created activity will have its address 113 | * related fields populated but will have a `context.activity.type === undefined`.. 114 | * 115 | * ```JavaScript 116 | * // Get group members conversation reference 117 | * const reference = TurnContext.getConversationReference(context.activity); 118 | * 119 | * // Start a new conversation with the user 120 | * await adapter.createConversation(reference, async (ctx) => { 121 | * await ctx.sendActivity(`Hi (in private)`); 122 | * }); 123 | * ``` 124 | * @param reference A `ConversationReference` of the user to start a new conversation with. 125 | * @param logic A function handler that will be called to perform the bot's logic after the the adapters middleware has been run. 126 | */ 127 | public async createTeamsConversation(reference: Partial, tenantIdOrTurnContext: string | TurnContext, logic?: (context: TurnContext) => Promise): Promise { 128 | if (!reference.serviceUrl) { throw new Error(`TeamsAdapter.createTeamsConversation(): missing serviceUrl.`); } 129 | 130 | // Create conversation 131 | const tenantId: string = (tenantIdOrTurnContext instanceof TurnContext) 132 | ? TeamsContext.from(tenantIdOrTurnContext).tenant.id || undefined 133 | : tenantIdOrTurnContext; 134 | const channelData: TeamsChannelData = { tenant: {id: tenantId} }; 135 | const parameters: ConversationParameters = { bot: reference.bot, members: [reference.user], channelData } as ConversationParameters; 136 | const client: ConnectorClient = this.createConnectorClient(reference.serviceUrl); 137 | const response = await client.conversations.createConversation(parameters); 138 | 139 | // Initialize request and copy over new conversation ID and updated serviceUrl. 140 | const request: Partial = TurnContext.applyConversationReference( 141 | { type: 'event', name: 'createConversation' }, 142 | reference, 143 | true 144 | ); 145 | request.conversation = { id: response.id } as ConversationAccount; 146 | if (response.serviceUrl) { request.serviceUrl = response.serviceUrl; } 147 | 148 | // Create context and run middleware 149 | const context: TurnContext = this.createContext(request); 150 | await this.runMiddleware(context, logic as any); 151 | } 152 | 153 | /** 154 | * Create a new reply chain in the current team context. 155 | * 156 | * @remarks 157 | * If a bot receives messages and wants to reply it as a new reply chain rather than reply it in the same thread, 158 | * then this method can be used. This method uses `turnContext.activity` as the conversation reference to figure out 159 | * the channel id in the current context and sends out activities as new messages in this team channel, so new reply 160 | * chain is created and it won't reply to the original thread. The usages looks like: 161 | * 162 | * ```JavaScript 163 | * await adapter.createReplyChain(ctx, [ 164 | * { text: 'New reply chain' } 165 | * ]); 166 | * ``` 167 | * @param turnContext current TurnContext 168 | * @param activities One or more activities to send to the team as new reply chain. 169 | * @param [inGeneralChannel] (optional) set it true if you want to create new reply chain in the general channel 170 | */ 171 | public createReplyChain(turnContext: TurnContext, activities: Partial[], inGeneralChannel?: boolean): Promise { 172 | let sentNonTraceActivity: boolean = false; 173 | const teamsCtx = TeamsContext.from(turnContext); 174 | const ref: Partial = TurnContext.getConversationReference(turnContext.activity); 175 | const output: Partial[] = activities.map((a: Partial) => { 176 | const o: Partial = TurnContext.applyConversationReference({...a}, ref); 177 | try { 178 | o.conversation.id = inGeneralChannel 179 | ? teamsCtx.getGeneralChannel().id 180 | : teamsCtx.channel.id; 181 | } catch (e) { 182 | // do nothing for fields fetching error 183 | } 184 | if (!o.type) { o.type = ActivityTypes.Message; } 185 | if (o.type !== ActivityTypes.Trace) { sentNonTraceActivity = true; } 186 | return o; 187 | }); 188 | 189 | return turnContext['emit'](turnContext['_onSendActivities'], output, () => { 190 | return super.sendActivities(turnContext, output) 191 | .then((responses: ResourceResponse[]) => { 192 | // Set responded flag 193 | if (sentNonTraceActivity) { turnContext.responded = true; } 194 | return responses; 195 | }); 196 | }); 197 | } 198 | 199 | /** 200 | * SMBA sometimes returns `objectId` and sometimes returns `aadObjectId`. 201 | * Use this function to unify them into `aadObjectId` that is defined by schema. 202 | * @param members raw members array 203 | */ 204 | private merge_objectId_and_aadObjectId(members: any[]): any[] { 205 | if (members) { 206 | members.forEach(m => { 207 | if (!m.aadObjectId && m.objectId) { 208 | m.aadObjectId = m.objectId; 209 | } 210 | delete m.objectId; 211 | }); 212 | } 213 | return members; 214 | } 215 | } -------------------------------------------------------------------------------- /botbuilder-teams-js/src/teamsContext.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // All rights reserved. 3 | // 4 | // MIT License: 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | import * as builder from 'botbuilder'; 25 | import { TeamsConnectorClient } from './'; 26 | import * as models from './schema/models'; 27 | 28 | /** 29 | * The class to represent Teams context 30 | * 31 | * @remarks 32 | * This needs to be working with `TeamsMiddleware`: 33 | * ```JavaScript 34 | * const { TeamsMiddleware } = require('botbuilder-teams'); 35 | * adapter.use(new TeamsMiddleware()); 36 | * ``` 37 | * With properly configured middleware, you may access the teams context in your `onTurn()` by fetching it from 38 | * current TurnContext, i.e., in your bot code: 39 | * ```JavaScript 40 | * const { TeamsContext } = require('botbuilder-teams'); 41 | * export class Bot { 42 | * async onTurn(turnContext) { 43 | * const teamsCtx = TeamsContext.from(ctx); 44 | * } 45 | * } 46 | * ``` 47 | */ 48 | export class TeamsContext { 49 | public static readonly stateKey: symbol = Symbol('TeamsContextCacheKey'); 50 | 51 | constructor ( 52 | public readonly turnContext: builder.TurnContext, 53 | public readonly teamsConnectorClient: TeamsConnectorClient 54 | ) { 55 | } 56 | 57 | public static from(turnContext: builder.TurnContext): TeamsContext { 58 | return turnContext.turnState.get(TeamsContext.stateKey); 59 | } 60 | 61 | /** 62 | * Gets the type of event 63 | */ 64 | public get eventType(): string { 65 | return this.getTeamsChannelData().eventType; 66 | } 67 | 68 | /** 69 | * Gets info about the team in which this activity fired. 70 | */ 71 | public get team(): models.TeamInfo { 72 | return this.getTeamsChannelData().team; 73 | } 74 | 75 | /** 76 | * Gets info about the channel in which this activity fired. 77 | */ 78 | public get channel(): models.ChannelInfo { 79 | return this.getTeamsChannelData().channel; 80 | } 81 | 82 | /** 83 | * Gets tenant info for the activity. 84 | */ 85 | public get tenant(): models.TenantInfo { 86 | return this.getTeamsChannelData().tenant; 87 | } 88 | 89 | /** 90 | * Gets the general channel for a team. 91 | * @returns Channel data for general channel. 92 | */ 93 | public getGeneralChannel(): models.ChannelInfo { 94 | const channelData = this.getTeamsChannelData(); 95 | if (channelData && channelData.team) { 96 | return { 97 | id: channelData.team.id 98 | }; 99 | } 100 | throw new Error('Failed to process channel data in Activity. ChannelData is missing Team property.'); 101 | } 102 | 103 | /** 104 | * Gets the Teams channel data associated with the current activity. 105 | * @returns Teams channel data for current activity. 106 | */ 107 | public getTeamsChannelData(): models.TeamsChannelData { 108 | const channelData = this.turnContext.activity.channelData; 109 | if (!channelData) { 110 | throw new Error('ChannelData missing Activity'); 111 | } else { 112 | return channelData as models.TeamsChannelData; 113 | } 114 | } 115 | 116 | /** 117 | * Gets the activity text without mentions. 118 | * @returns Text without mentions. 119 | */ 120 | public getActivityTextWithoutMentions(): string { 121 | const activity = this.turnContext.activity; 122 | if (activity.entities && activity.entities.length === 0) { 123 | return activity.text; 124 | } 125 | 126 | const recvBotId = activity.recipient.id; 127 | const recvBotMentioned = activity.entities.filter(e => 128 | (e.type === 'mention') && (e as builder.Mention).mentioned.id === recvBotId 129 | ); 130 | 131 | if (recvBotMentioned.length === 0) { 132 | return activity.text; 133 | } 134 | 135 | let strippedText = activity.text; 136 | recvBotMentioned.forEach(m => m.text && (strippedText = strippedText.replace(m.text, ''))); 137 | return strippedText.trim(); 138 | } 139 | 140 | /** 141 | * Gets the conversation parameters for create or get direct conversation. 142 | * @param user The user to create conversation with. 143 | * @returns Conversation parameters to get or create direct conversation (1on1) between bot and user. 144 | */ 145 | public getConversationParametersForCreateOrGetDirectConversation(user: builder.ChannelAccount): builder.ConversationParameters { 146 | const channelData = {} as models.TeamsChannelData; 147 | if (this.tenant && this.tenant.id) { 148 | channelData.tenant = { 149 | id: this.tenant.id 150 | }; 151 | } 152 | 153 | return { 154 | bot: this.turnContext.activity.recipient, 155 | channelData, 156 | members: [user] 157 | }; 158 | } 159 | 160 | /** 161 | * Adds the mention text to an existing activity. 162 | * 163 | * @remarks 164 | * > [TODO] need to resolve schema problems in botframework-connector where Activity only supports Entity but not Mention. 165 | * > (entity will be dropped out before sending out due to schema checking) 166 | * 167 | * @param activity The activity. 168 | * @param mentionedEntity The mentioned entity. 169 | * @param mentionText The mention text. This is how you want to mention the entity. 170 | * @returns Activity with added mention. 171 | */ 172 | public static addMentionToText(activity: T, mentionedEntity: builder.ChannelAccount, mentionText?: string): T { 173 | if (!mentionedEntity || !mentionedEntity.id) { 174 | throw new Error('Mentioned entity and entity ID cannot be null'); 175 | } 176 | 177 | if (!mentionedEntity.name && !mentionText) { 178 | throw new Error('Either mentioned name or mentionText must have a value'); 179 | } 180 | 181 | if (!!mentionText) { 182 | mentionedEntity.name = mentionText; 183 | } 184 | 185 | const mentionEntityText = `${mentionedEntity.name}`; 186 | activity.text += ` ${mentionEntityText}`; 187 | activity.entities = [ 188 | ...(activity.entities || []), 189 | { 190 | type: 'mention', 191 | text: mentionEntityText, 192 | mentioned: mentionedEntity 193 | } as builder.Mention 194 | ]; 195 | 196 | return activity; 197 | } 198 | 199 | /** 200 | * Notifies the user in direct conversation. 201 | * @param activity The reply activity. 202 | * @typeparam T Type of message activity. 203 | * @returns Modified activity. 204 | */ 205 | public static notifyUser(activity: T): T { 206 | let channelData: models.TeamsChannelData = activity.channelData || {}; 207 | channelData.notification = { 208 | alert: true 209 | }; 210 | activity.channelData = channelData; 211 | return activity; 212 | } 213 | 214 | /** 215 | * Type guard to identify if `channelAccount` is the type of `TeamsChannelAccount` 216 | * @param channelAccount the channel account or free payload 217 | * @returns true or false. If returns true then `channelAccount` will be auto casting to `TeamsChannelAccount` in `if () {...}` block 218 | */ 219 | public static isTeamsChannelAccount(channelAccount: builder.ChannelAccount | any[]): channelAccount is models.TeamsChannelAccount { 220 | const o = channelAccount as models.TeamsChannelAccount; 221 | return !!o 222 | &&(!!o.id && !!o.name) 223 | && (!!o.aadObjectId || !!o.email || !!o.givenName || !!o.surname || !!o.userPrincipalName); 224 | } 225 | 226 | /** 227 | * Type guard to identify if `channelAccount` is the array type of `TeamsChannelAccount[]` 228 | * @param channelAccount the array of the channel account or free array 229 | * @returns true if all elements are `TeamsChannelAccount`. If returns true then `channelAccount` will be auto casting to `TeamsChannelAccount[]` in `if () {...}` block 230 | */ 231 | public static isTeamsChannelAccounts(channelAccount: builder.ChannelAccount[] | any[]): channelAccount is models.TeamsChannelAccount[] { 232 | return Array.isArray(channelAccount) && channelAccount.every(x => this.isTeamsChannelAccount(x)); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /botbuilder-teams-js/swagger/build.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let fsExt = require('fs-extra'); 3 | let childProc = require('child_process'); 4 | let rimraf = require("rimraf"); 5 | let replace = require('replace'); 6 | 7 | const out = process.argv[2] || './out'; 8 | 9 | async function autorest() { 10 | rimraf.sync(out); 11 | const args = [ 12 | `--typescript`, 13 | `--input-file=./teamsAPI.json`, 14 | `--output-folder=${out}`, 15 | `--override-client-name=TeamsConnectorClient` 16 | ].join(' '); 17 | await childProc.execSync(`node ../node_modules/autorest/dist/app.js ${args}`, {stdio: 'inherit'}) 18 | } 19 | 20 | function replaceStrings() { 21 | const runReplace= (regex, replacement) => 22 | replace({ 23 | regex, 24 | replacement, 25 | paths: ['.'], 26 | recursive: true, 27 | silent: true, 28 | include: '*.ts' 29 | }); 30 | 31 | runReplace(`"`, `'`); 32 | runReplace(`from '../teamsConnectorClientContext'`, `from '../../'`); 33 | } 34 | 35 | function insertImportLines (lines, insertLines = []) { 36 | const firstImportLine = lines.findIndex(l => l.startsWith('import ')); 37 | lines.splice(firstImportLine, 0, ...insertLines); 38 | } 39 | 40 | function deleteClassDeclaration (lines, className) { 41 | const lId = lines.findIndex(l => l.startsWith(`export interface ${className}`)); 42 | if (lId >= 0) { 43 | let [begId, endId] = [lId, lId]; 44 | while (--begId >= 0) { 45 | if (lines[begId].startsWith('/**')) { 46 | break; 47 | } 48 | } 49 | 50 | while (++endId < lines.length) { 51 | if (lines[endId].startsWith('}')) { 52 | break; 53 | } 54 | } 55 | 56 | if (begId >= 0 && endId < lines.length) { 57 | lines.splice(begId, endId - begId + 2); // delete 1 more empty line below 58 | } 59 | } 60 | } 61 | 62 | function imposeExtBaseClass (lines, extNSOfBotbuilder = 'builder', extNSOfTeams = 'teams', descriptorFile = './extension.json') { 63 | const json = JSON.parse(fs.readFileSync(descriptorFile).toString()); 64 | const { extBotBuilderTypes, extTeamsTypes } = json; 65 | const extTypes = [...extBotBuilderTypes, ...extTeamsTypes]; 66 | 67 | // 1. delete all existing classes 68 | extTypes.forEach(clsName => { 69 | deleteClassDeclaration(lines, clsName); 70 | }); 71 | 72 | // 2. replace to external classes 73 | const replace = (l, clsName, ns) => { 74 | const old = l; 75 | l = l.replace(new RegExp(` ${clsName}(?!\\w)`, 'g'), ` ${ns}.${clsName}`); 76 | (old !== l) && console.log(`replace:\n${old}\n${l}\n\n`); 77 | return l; 78 | }; 79 | 80 | lines.forEach((l, lId) => { 81 | extBotBuilderTypes.forEach(clsName => l = replace(l, clsName, extNSOfBotbuilder)); 82 | extTeamsTypes.forEach(clsName => l = replace(l, clsName, extNSOfTeams)); 83 | lines[lId] = l; 84 | }); 85 | } 86 | 87 | function processExtTypes() { 88 | const modelsFile = `${out}/lib/models/index.ts`; 89 | let lines = fs.readFileSync(modelsFile).toString().split('\n'); 90 | insertImportLines(lines, [ 91 | `import * as teams from '../extension'`, 92 | `import * as builder from 'botbuilder'` 93 | ]); 94 | imposeExtBaseClass(lines, 'builder', 'teams', './extension.json'); 95 | fs.writeFileSync(modelsFile, lines.join('\n')); 96 | } 97 | 98 | function releaseAndcleanup() { 99 | rimraf.sync(`../src/schema/models`); 100 | rimraf.sync(`../src/schema/operations`); 101 | fsExt.moveSync(`${out}/lib/models`, `../src/schema/models`); 102 | fsExt.moveSync(`${out}/lib/operations`, `../src/schema/operations`); 103 | rimraf.sync(out); 104 | } 105 | 106 | async function build() { 107 | await autorest(); 108 | replaceStrings(); 109 | processExtTypes(); 110 | releaseAndcleanup(); 111 | } 112 | 113 | build(); -------------------------------------------------------------------------------- /botbuilder-teams-js/swagger/extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "extBotBuilderTypes": ["ChannelAccount", "CardImage", "CardAction", "Attachment", "Activity"], 3 | "extTeamsTypes": ["O365ConnectorCardActionBase", "O365ConnectorCardInputBase", "MessageActionsPayloadBody"] 4 | } 5 | -------------------------------------------------------------------------------- /botbuilder-teams-js/swagger/generate.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | set OUT=out 3 | node build.js %OUT% 4 | -------------------------------------------------------------------------------- /botbuilder-teams-js/swagger/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OUT='./out' 4 | node build.js $OUT 5 | -------------------------------------------------------------------------------- /botbuilder-teams-js/tests/teamsContext.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { TeamsContext } = require('../lib'); 3 | const { TurnContext } = require('botbuilder'); 4 | 5 | describe('TeamsContext', () => { 6 | it('fetch context from turn state', (done) => { 7 | const teamsCtx = new TeamsContext(); 8 | const turnCtx = new TurnContext(null); 9 | turnCtx.turnState.set(TeamsContext.stateKey, teamsCtx); 10 | assert(TeamsContext.from(turnCtx) === teamsCtx); 11 | done(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /botbuilder-teams-js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./lib", 8 | "rootDir": "./src", 9 | "types" : ["node"] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ] 14 | } -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Steps to run samples 2 | 3 | Our samples are running along with the library version against **current repo** instead of NPM release library, so you may preview the sample code for several pre-released features. (So that's why you'll see `"botbuilder-teams": "file:../../botbuilder-teams-js"` in `package.json` in sample folders) 4 | 5 | 1. **Build library:** go to lib path `~/BotBuilder-MicrosoftTeams-node/botbuilder-teams-js`: 6 | 1. install and build: `npm i` 7 | 2. for post-install and you just wanna build or re-build: `npm run build` 8 | 9 | 2. **Set up credentials**: in sample folders find out bot file (usually named `bot-file.json`) and set up your Microsoft Bot ID (App ID) and its associated password for `appId` and `appPassword` fields respectively. 10 | 11 | 3. **Run up samples**: then follow the steps to run up bot: 12 | 1. `npm i` 13 | 2. `npm start` 14 | 15 | 4. **Use bot in Teams as sideloaded app**: to use the bot as a sideloaded app in Microsoft Teams, please follow the steps: 16 | 1. Modify `manifest.json` to assign a random GUID for `id` as Microsoft Teams App ID 17 | 2. Assign `bots.botId` and `composeExtensions.botId` where the bot ID should be the one assigned in step 2 in `bot-file.json` 18 | 3. Zip `manifest.json`, `icon-color.png` and `icon-outline.png` as an archived file. 19 | 4. Follow [instructions](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/apps/apps-upload) to sideload your bot into Teams. 20 | 5. More instruction can be found [here](https://docs.microsoft.com/en-us/microsoftteams/platform/get-started/get-started-nodejs-app-studio) -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/.env: -------------------------------------------------------------------------------- 1 | botFilePath=bot-file.json 2 | botFileSecret= 3 | -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/bot-file.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echobot-with-counter", 3 | "services": [ 4 | { 5 | "type": "endpoint", 6 | "name": "development", 7 | "endpoint": "http://localhost:3978/api/messages", 8 | "appId": "", 9 | "appPassword": "", 10 | "id": "1" 11 | } 12 | ], 13 | "padlock": "", 14 | "version": "2.0" 15 | } 16 | -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-teams-js-teams-bot", 3 | "version": "0.0.1", 4 | "description": "Microsoft Teams Sample Bot for BotBuilder V4", 5 | "author": "Microsoft Corp", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "npm run build:live", 9 | "build:live": "nodemon --ignore './data' --exec node --inspect=5566 -r ts-node/register ./src/app.ts" 10 | }, 11 | "dependencies": { 12 | "botbuilder-teams": "file:../../botbuilder-teams-js", 13 | "botframework-config": "^4.2.0", 14 | "dotenv": "^6.2.0", 15 | "nodemon": "^1.18.9", 16 | "restify": "^7.6.0", 17 | "restify-plugins": "^1.6.0", 18 | "ts-node": "^7.0.1" 19 | }, 20 | "devDependencies": { 21 | "@types/restify": "^7.2.7", 22 | "typescript": "^3.2.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/src/app.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import { config } from 'dotenv'; 5 | import * as path from 'path'; 6 | import * as restify from 'restify'; 7 | 8 | // Import required bot services. 9 | // See https://aka.ms/bot-services to learn more about the different parts of a bot. 10 | import { BotFrameworkAdapter, MemoryStorage } from 'botbuilder-teams/node_modules/botbuilder'; 11 | import * as teams from 'botbuilder-teams'; 12 | 13 | // Import required bot configuration. 14 | import { BotConfiguration, IEndpointService } from 'botframework-config'; 15 | 16 | import { EchoBot } from './bot'; 17 | 18 | // Read botFilePath and botFileSecret from .env file 19 | // Note: Ensure you have a .env file and include botFilePath and botFileSecret. 20 | const ENV_FILE = path.join(__dirname, '..', '.env'); 21 | const loadFromEnv = config({path: ENV_FILE}); 22 | 23 | // Get the .bot file path 24 | // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. 25 | const BOT_FILE = path.join(__dirname, '..', (process.env.botFilePath || '')); 26 | let botConfig: BotConfiguration; 27 | try { 28 | // read bot configuration from .bot file. 29 | botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); 30 | } catch (err) { 31 | console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); 32 | console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); 33 | console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.`); 34 | console.error(`\n - See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration.\n\n`); 35 | process.exit(); 36 | } 37 | 38 | // For local development configuration as defined in .bot file. 39 | const DEV_ENVIRONMENT = 'development'; 40 | 41 | // Define name of the endpoint configuration section from the .bot file. 42 | const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); 43 | 44 | // Get bot endpoint configuration by service name. 45 | // Bot configuration as defined in .bot file. 46 | const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION) as IEndpointService; 47 | 48 | // Create adapter. 49 | // See https://aka.ms/about-bot-adapter to learn more about to learn more about bot adapter. 50 | const adapter = new BotFrameworkAdapter({ 51 | appId: endpointConfig.appId || process.env.microsoftAppID, 52 | appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword, 53 | }); 54 | 55 | // use Teams middleware 56 | adapter.use(new teams.TeamsMiddleware()); 57 | 58 | // Catch-all for any unhandled errors in your bot. 59 | adapter.onTurnError = async (context, error) => { 60 | // This check writes out errors to console log .vs. app insights. 61 | console.error(`\n [onTurnError]: ${ error }`); 62 | // Send a message to the user. 63 | await context.sendActivity(`Oops. Something went wrong!`); 64 | // Clear out state 65 | await conversationState.delete(context); 66 | }; 67 | 68 | // Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. 69 | // A bot requires a state store to persist the dialog and user state between messages. 70 | let conversationState: teams.TeamSpecificConversationState; 71 | 72 | // For local development, in-memory storage is used. 73 | // CAUTION: The Memory Storage used here is for local bot debugging only. When the bot 74 | // is restarted, anything stored in memory will be gone. 75 | const memoryStorage = new MemoryStorage(); 76 | conversationState = new teams.TeamSpecificConversationState(memoryStorage); 77 | 78 | // CAUTION: You must ensure your product environment has the NODE_ENV set 79 | // to use the Azure Blob storage or Azure Cosmos DB providers. 80 | // import { BlobStorage } from 'botbuilder-azure'; 81 | // Storage configuration name or ID from .bot file 82 | // const STORAGE_CONFIGURATION_ID = ''; 83 | // // Default container name 84 | // const DEFAULT_BOT_CONTAINER = ''; 85 | // // Get service configuration 86 | // const blobStorageConfig = botConfig.findServiceByNameOrId(STORAGE_CONFIGURATION_ID); 87 | // const blobStorage = new BlobStorage({ 88 | // containerName: (blobStorageConfig.container || DEFAULT_BOT_CONTAINER), 89 | // storageAccountOrConnectionString: blobStorageConfig.connectionString, 90 | // }); 91 | // conversationState = new ConversationState(blobStorage); 92 | 93 | // Create the EchoBot. 94 | const bot = new EchoBot(conversationState); 95 | 96 | // Create HTTP server 97 | const server = restify.createServer(); 98 | server.listen(process.env.port || process.env.PORT || 3978, () => { 99 | console.log(`\n${ server.name } listening to ${ server.url }`); 100 | console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator.`); 101 | console.log(`\nTo talk to your bot, open echobot-with-counter.bot file in the Emulator.`); 102 | }); 103 | 104 | // Listen for incoming activities and route them to your bot for processing. 105 | server.post('/api/messages', (req, res) => { 106 | adapter.processActivity(req, res, async (turnContext) => { 107 | // Call bot.onTurn() to handle all incoming messages. 108 | await bot.onTurn(turnContext); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/src/bot.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import { ActivityTypes, BotState, StatePropertyAccessor, TurnContext } from 'botbuilder-teams/node_modules/botbuilder'; 5 | 6 | // Turn counter property 7 | const TURN_COUNTER = 'turnCounterProperty'; 8 | 9 | export class EchoBot { 10 | 11 | private readonly countAccessor: StatePropertyAccessor; 12 | private readonly conversationState: BotState; 13 | 14 | /** 15 | * 16 | * @param {BotState} conversation state object 17 | */ 18 | constructor(conversationState: BotState) { 19 | // Create a new state accessor property. 20 | // See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors. 21 | this.countAccessor = conversationState.createProperty(TURN_COUNTER); 22 | this.conversationState = conversationState; 23 | } 24 | 25 | /** 26 | * Use onTurn to handle an incoming activity, received from a user, process it, and reply as needed 27 | * 28 | * @param {TurnContext} context on turn context object. 29 | */ 30 | public async onTurn(turnContext: TurnContext) { 31 | // Handle message activity type. User's responses via text or speech or card interactions flow back to the bot as Message activity. 32 | // Message activities may contain text, speech, interactive cards, and binary or unknown attachments. 33 | // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types 34 | if (turnContext.activity.type === ActivityTypes.Message) { 35 | // read from state. 36 | let count = await this.countAccessor.get(turnContext); 37 | count = count === undefined ? 1 : ++count; 38 | await turnContext.sendActivity(`${ count }: You said "${ turnContext.activity.text }"`); 39 | // increment and set turn counter. 40 | await this.countAccessor.set(turnContext, count); 41 | } else { 42 | // Generic handler for all other activity types. 43 | await turnContext.sendActivity(`[${turnContext.activity.type} event detected]`); 44 | } 45 | // Save state changes 46 | await this.conversationState.saveChanges(turnContext); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/teams-app-manifest/icon-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/echo-bot-with-counter/teams-app-manifest/icon-color.png -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/teams-app-manifest/icon-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/echo-bot-with-counter/teams-app-manifest/icon-outline.png -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/teams-app-manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.7/MicrosoftTeams.schema.json", 3 | "manifestVersion": "1.7", 4 | "version": "1.0", 5 | "id": "{YOUR_APP_ID}", 6 | "packageName": "com.microsoft.teams.samples.v4bot", 7 | "developer": { 8 | "name": "Microsoft Corp", 9 | "websiteUrl": "https://example.azurewebsites.net", 10 | "privacyUrl": "https://example.azurewebsites.net/privacy", 11 | "termsOfUseUrl": "https://example.azurewebsites.net/termsofuse" 12 | }, 13 | "name": { 14 | "short": "V4 Sample", 15 | "full": "Microsoft Teams V4 Sample Bot" 16 | }, 17 | "description": { 18 | "short": "Sample bot using V4 SDK", 19 | "full": "Sample bot using V4 Bot Builder SDK and V4 Microsoft Teams Extension SDK" 20 | }, 21 | "icons": { 22 | "outline": "icon-outline.png", 23 | "color": "icon-color.png" 24 | }, 25 | "accentColor": "#abcdef", 26 | "bots": [ 27 | { 28 | "botId": "{YOUR_BOT_ID}", 29 | "scopes": ["personal", "team"] 30 | } 31 | ], 32 | "validDomains": ["*.azurewebsites.net"] 33 | } 34 | -------------------------------------------------------------------------------- /samples/echo-bot-with-counter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "declaration": true, 5 | "target": "es2016", 6 | "module": "commonjs", 7 | "outDir": "./lib", 8 | "rootDir": "./src", 9 | "sourceMap": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/file-bot/.env: -------------------------------------------------------------------------------- 1 | microsoftAppID= 2 | microsoftAppPassword= 3 | -------------------------------------------------------------------------------- /samples/file-bot/files/teams-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/file-bot/files/teams-logo.png -------------------------------------------------------------------------------- /samples/file-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-teams-js-teams-bot", 3 | "version": "0.0.1", 4 | "description": "Microsoft Teams Sample Bot for BotBuilder V4", 5 | "author": "Microsoft Corp", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "npm run build:live", 9 | "build:live": "nodemon --ignore './data' --exec node --inspect=5566 -r ts-node/register ./src/app.ts" 10 | }, 11 | "dependencies": { 12 | "botbuilder-teams": "file:../../botbuilder-teams-js", 13 | "botframework-config": "^4.2.0", 14 | "dotenv": "^6.2.0", 15 | "nodemon": "^1.18.9", 16 | "request": "^2.88.0", 17 | "restify": "^7.6.0", 18 | "restify-plugins": "^1.6.0", 19 | "ts-node": "^7.0.1" 20 | }, 21 | "devDependencies": { 22 | "@types/request": "^2.48.1", 23 | "@types/restify": "^7.2.7", 24 | "typescript": "^3.2.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/file-bot/src/app.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as restify from 'restify'; 5 | import * as teams from 'botbuilder-teams'; 6 | import { BotFrameworkAdapterSettings } from 'botbuilder-teams/node_modules/botbuilder'; 7 | import { FileBot } from './bot'; 8 | import * as path from 'path'; 9 | import { config } from 'dotenv'; 10 | 11 | // Read botFilePath and botFileSecret from .env file 12 | // Note: Ensure you have a .env file and include botFilePath and botFileSecret. 13 | const ENV_FILE = path.join(__dirname, '..', '.env'); 14 | const loadFromEnv = config({path: ENV_FILE}); 15 | 16 | // // Create adapter. 17 | // See https://aka.ms/about-bot-adapter to learn more about to learn more about bot adapter. 18 | const botSetting: Partial = { 19 | appId: process.env.microsoftAppID, 20 | appPassword: process.env.microsoftAppPassword 21 | }; 22 | 23 | const adapter = new teams.TeamsAdapter(botSetting); 24 | 25 | adapter.use(new teams.TeamsMiddleware()); 26 | 27 | // Catch-all for any unhandled errors in your bot. 28 | adapter.onTurnError = async (turnContext, error) => { 29 | // This check writes out errors to console log .vs. app insights. 30 | console.error(`\n [onTurnError]: ${ error }`); 31 | }; 32 | 33 | // Create the EchoBot. 34 | const bot = new FileBot(); 35 | 36 | // Create HTTP server 37 | let server = restify.createServer(); 38 | server.listen(process.env.port || process.env.PORT || 3978, function() { 39 | console.log(`\n${ server.name } listening to ${ server.url }`); 40 | console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator.`); 41 | console.log(`\nTo talk to your bot, open echobot-with-counter.bot file in the Emulator.`); 42 | }); 43 | 44 | // Listen for incoming activities and route them to your bot for processing. 45 | server.use(require('restify-plugins').bodyParser()); 46 | server.post('/api/messages', (req, res) => { 47 | adapter.processActivity(req, res, async (turnContext) => { 48 | // Call bot.onTurn() to handle all incoming messages. 49 | await bot.onTurn(turnContext); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /samples/file-bot/src/bot.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import { TurnContext } from 'botbuilder-teams/node_modules/botbuilder'; 5 | import * as teams from 'botbuilder-teams'; 6 | import * as request from 'request'; 7 | import * as fs from 'fs'; 8 | 9 | /** 10 | * Define data type for accept context 11 | */ 12 | interface ConsentContext { 13 | filename: string; 14 | } 15 | 16 | export class FileBot { 17 | private readonly fileFolder = './files'; 18 | private readonly activityProc = new teams.TeamsActivityProcessor(); 19 | 20 | constructor () { 21 | this.setupHandlers(); 22 | } 23 | 24 | /** 25 | * Use onTurn to handle an incoming activity, received from a user, process it, and reply as needed 26 | * 27 | * @param {TurnContext} context on turn context object. 28 | */ 29 | public async onTurn(turnContext: TurnContext) { 30 | await this.activityProc.processIncomingActivity(turnContext); 31 | } 32 | 33 | /** 34 | * Set up all activity handlers 35 | */ 36 | private setupHandlers () { 37 | this.activityProc.messageActivityHandler = { 38 | onMessage: async (ctx) => { 39 | const filename = 'teams-logo.png'; 40 | const fileinfo = fs.statSync(`${this.fileFolder}/${filename}`); 41 | await this.sendFileCard(ctx, filename, fileinfo.size); 42 | }, 43 | 44 | onMessageWithFileDownloadInfo: async (ctx, file) => { 45 | await ctx.sendActivity({ textFormat: 'xml', text: `Received File
${JSON.stringify(file, null, 2)}
`}); 46 | let filename: string; 47 | const err = await new Promise((resolve, reject) => { 48 | let r = request(file.downloadUrl); 49 | r.on('response', (res) => { 50 | const regexp = /filename=\"(.*)\"/gi; 51 | filename = regexp.exec( res.headers['content-disposition'] )[1]; 52 | res.pipe(fs.createWriteStream(`${this.fileFolder}/${filename}`)); 53 | }); 54 | r.on('error', (err) => resolve(err)); 55 | r.on('complete', (res) => resolve()); 56 | }); 57 | if (!err && !!filename) { 58 | await ctx.sendActivity({ textFormat: 'xml', text: `Complete downloading ${filename}` }); 59 | } 60 | } 61 | }; 62 | 63 | this.activityProc.invokeActivityHandler = { 64 | onFileConsent: async (ctx: TurnContext, query: teams.FileConsentCardResponse) => { 65 | await ctx.sendActivity({ textFormat: 'xml', text: `Received user's consent
${JSON.stringify(query, null, 2)}
`}); 66 | 67 | const context: ConsentContext = query.context; 68 | 69 | // 'Accepted' case 70 | if (query.action === 'accept') { 71 | const fname = `${this.fileFolder}/${context.filename}`; 72 | const fileInfo = fs.statSync(fname); 73 | const file = new Buffer(fs.readFileSync(fname, 'binary'), 'binary'); 74 | await ctx.sendActivity({ textFormat: 'xml', text: `Uploading ${context.filename}`}); 75 | const r = new Promise((resolve, reject) => { 76 | request.put({ 77 | uri: query.uploadInfo.uploadUrl, 78 | headers: { 79 | 'Content-Length': fileInfo.size, 80 | 'Content-Range': `bytes 0-${fileInfo.size-1}/${fileInfo.size}` 81 | }, 82 | encoding: null, 83 | body: file 84 | }, async (err, res) => { 85 | if (err) { 86 | reject(err); 87 | } else { 88 | const data = Buffer.from(res.body, 'binary').toString('utf8'); 89 | resolve(JSON.parse(data)); 90 | } 91 | }); 92 | }); 93 | 94 | return r.then(async res => { 95 | await this.fileUploadCompleted(ctx, query, res); 96 | return { status: 200 } 97 | }).catch(async err => { 98 | await this.fileUploadFailed(ctx, err); 99 | return { status: 500, body: `File upload failed: ${JSON.stringify(err)}` } 100 | }); 101 | } 102 | 103 | // 'Declined' case 104 | if (query.action === 'decline') { 105 | await ctx.sendActivity({ textFormat: 'xml', text: `Declined. We won't upload file ${context.filename}.`} ); 106 | } 107 | 108 | return { status: 200 }; 109 | } 110 | }; 111 | } 112 | 113 | private async sendFileCard (ctx: TurnContext, filename: string, filesize: number) { 114 | const fileCard = teams.TeamsFactory.fileConsentCard( 115 | filename, 116 | { 117 | 'description': 'This is the file I want to send you', 118 | 'sizeInBytes': filesize, 119 | 'acceptContext': { 120 | filename 121 | }, 122 | 'declineContext': { 123 | filename 124 | } 125 | } 126 | ); 127 | await ctx.sendActivities([ { attachments: [fileCard] } ]); 128 | } 129 | 130 | private async fileUploadCompleted (ctx: TurnContext, query: teams.FileConsentCardResponse, response: any) { 131 | const downloadCard = teams.TeamsFactory.fileInfoCard( 132 | query.uploadInfo.name, 133 | query.uploadInfo.contentUrl, 134 | { 135 | 'uniqueId': query.uploadInfo.uniqueId, 136 | 'fileType': query.uploadInfo.fileType 137 | } 138 | ); 139 | await ctx.sendActivities([ 140 | { 141 | textFormat: 'xml', 142 | text: `File Upload Completed
${JSON.stringify(response, null, 2)}
` 143 | }, 144 | { 145 | textFormat: 'xml', 146 | text: `Your file ${query.context.filename} is ready to download`, 147 | attachments: [downloadCard] 148 | } 149 | ]); 150 | } 151 | 152 | private async fileUploadFailed (ctx: TurnContext, error: any) { 153 | await ctx.sendActivity({ 154 | textFormat: 'xml', 155 | text: `File Upload Failed
${JSON.stringify(error, null, 2)}
` 156 | }); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /samples/file-bot/teams-app-manifest/icon-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/file-bot/teams-app-manifest/icon-color.png -------------------------------------------------------------------------------- /samples/file-bot/teams-app-manifest/icon-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/file-bot/teams-app-manifest/icon-outline.png -------------------------------------------------------------------------------- /samples/file-bot/teams-app-manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.3/MicrosoftTeams.schema.json", 3 | "manifestVersion": "1.3", 4 | "version": "1.0", 5 | "id": "{YOUR_APP_ID}", 6 | "packageName": "com.microsoft.teams.samples.v4bot", 7 | "developer": { 8 | "name": "Microsoft Corp", 9 | "websiteUrl": "https://example.azurewebsites.net", 10 | "privacyUrl": "https://example.azurewebsites.net/privacy", 11 | "termsOfUseUrl": "https://example.azurewebsites.net/termsofuse" 12 | }, 13 | "name": { 14 | "short": "V4 Sample", 15 | "full": "Microsoft Teams V4 Sample Bot" 16 | }, 17 | "description": { 18 | "short": "Sample bot using V4 SDK", 19 | "full": "Sample bot using V4 Bot Builder SDK and V4 Microsoft Teams Extension SDK" 20 | }, 21 | "icons": { 22 | "outline": "icon-outline.png", 23 | "color": "icon-color.png" 24 | }, 25 | "accentColor": "#abcdef", 26 | "bots": [ 27 | { 28 | "botId": "{YOUR_BOT_ID}", 29 | "supportsFiles": true, 30 | "scopes": ["personal", "team"] 31 | } 32 | ], 33 | "validDomains": ["*.azurewebsites.net"] 34 | } 35 | -------------------------------------------------------------------------------- /samples/file-bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./lib", 8 | "rootDir": "./src", 9 | "types" : ["node"] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ] 14 | } -------------------------------------------------------------------------------- /samples/message-extension-bot/.env: -------------------------------------------------------------------------------- 1 | botFilePath=bot-file.json 2 | botFileSecret= 3 | host=https://{MY_BOT_URL_FOR_SIGNIN_AUTH} 4 | -------------------------------------------------------------------------------- /samples/message-extension-bot/bot-file.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echobot-with-counter", 3 | "services": [ 4 | { 5 | "type": "endpoint", 6 | "name": "development", 7 | "endpoint": "http://localhost:3978/api/messages", 8 | "appId": "", 9 | "appPassword": "", 10 | "id": "1" 11 | } 12 | ], 13 | "padlock": "", 14 | "version": "2.0" 15 | } 16 | -------------------------------------------------------------------------------- /samples/message-extension-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-teams-js-messaging-ext-bot", 3 | "version": "0.0.1", 4 | "description": "Microsoft Teams Sample Bot for BotBuilder V4", 5 | "author": "Microsoft Corp", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "npm run build:live", 9 | "build:live": "nodemon --ignore './data' --exec node --inspect=5566 -r ts-node/register ./src/app.ts" 10 | }, 11 | "dependencies": { 12 | "botbuilder-teams": "file:../../botbuilder-teams-js", 13 | "botframework-config": "^4.2.0", 14 | "dotenv": "^6.2.0", 15 | "nodemon": "^1.18.9", 16 | "restify": "^7.6.0", 17 | "restify-plugins": "^1.6.0", 18 | "ts-node": "^7.0.1" 19 | }, 20 | "devDependencies": { 21 | "@types/restify": "^7.2.7", 22 | "typescript": "^3.2.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/message-extension-bot/src/app.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as restify from 'restify'; 5 | import * as path from 'path'; 6 | import { config } from 'dotenv'; 7 | 8 | // Import required bot services. See https://aka.ms/bot-services to learn more about the different parts of a bot. 9 | import { MemoryStorage, BotFrameworkAdapterSettings } from 'botbuilder-teams/node_modules/botbuilder'; 10 | import * as teams from 'botbuilder-teams'; 11 | 12 | // Import required bot configuration. 13 | import { BotConfiguration, IEndpointService } from 'botframework-config'; 14 | 15 | import { TeamsBot } from './bot'; 16 | 17 | // Read botFilePath and botFileSecret from .env file 18 | // Note: Ensure you have a .env file and include botFilePath and botFileSecret. 19 | const ENV_FILE = path.join(__dirname, '..', '.env'); 20 | const loadFromEnv = config({path: ENV_FILE}); 21 | 22 | // Get the .bot file path 23 | // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. 24 | const BOT_FILE = path.join(__dirname, '..', (process.env.botFilePath || '')); 25 | let botConfig; 26 | try { 27 | // read bot configuration from .bot file. 28 | botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); 29 | } catch (err) { 30 | console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); 31 | console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); 32 | console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.`); 33 | console.error(`\n - See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration.\n\n`); 34 | process.exit(); 35 | } 36 | 37 | // For local development configuration as defined in .bot file. 38 | const DEV_ENVIRONMENT = 'development'; 39 | 40 | // Define name of the endpoint configuration section from the .bot file. 41 | const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); 42 | 43 | // Get bot endpoint configuration by service name. 44 | // Bot configuration as defined in .bot file. 45 | const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION); 46 | 47 | // // Create adapter. 48 | // See https://aka.ms/about-bot-adapter to learn more about to learn more about bot adapter. 49 | const botSetting: Partial = { 50 | appId: endpointConfig.appId || process.env.microsoftAppID, 51 | appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword, 52 | }; 53 | 54 | const adapter = new teams.TeamsAdapter(botSetting); 55 | 56 | // Use Teams middleware 57 | adapter.use(new teams.TeamsMiddleware()); 58 | 59 | // Catch-all for any unhandled errors in your bot. 60 | adapter.onTurnError = async (turnContext, error) => { 61 | // This check writes out errors to console log .vs. app insights. 62 | console.error(`\n [onTurnError]: ${ error }`); 63 | // Send a message to the user. 64 | turnContext.sendActivity(`Oops. Something went wrong!`); 65 | // Clear out state and save changes so the user is not stuck in a bad state. 66 | await conversationState.clear(turnContext); 67 | await conversationState.saveChanges(turnContext); 68 | }; 69 | 70 | // For local development, in-memory storage is used. 71 | // CAUTION: The Memory Storage used here is for local bot debugging only. When the bot 72 | // is restarted, anything stored in memory will be gone. 73 | const memoryStorage = new MemoryStorage(); 74 | 75 | 76 | // Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. 77 | // A bot requires a state store to persist the dialog and user state between messages. 78 | // let conversationState = new ConversationState(memoryStorage); 79 | let conversationState = new teams.TeamSpecificConversationState(memoryStorage); 80 | 81 | // CAUTION: You must ensure your product environment has the NODE_ENV set 82 | // to use the Azure Blob storage or Azure Cosmos DB providers. 83 | // import { BlobStorage } from 'botbuilder-azure'; 84 | // Storage configuration name or ID from .bot file 85 | // const STORAGE_CONFIGURATION_ID = ''; 86 | // // Default container name 87 | // const DEFAULT_BOT_CONTAINER = ''; 88 | // // Get service configuration 89 | // const blobStorageConfig = botConfig.findServiceByNameOrId(STORAGE_CONFIGURATION_ID); 90 | // const blobStorage = new BlobStorage({ 91 | // containerName: (blobStorageConfig.container || DEFAULT_BOT_CONTAINER), 92 | // storageAccountOrConnectionString: blobStorageConfig.connectionString, 93 | // }); 94 | // conversationState = new ConversationState(blobStorage); 95 | 96 | // Create the TeamsBot. 97 | const bot = new TeamsBot(conversationState); 98 | 99 | // Create HTTP server 100 | let server = restify.createServer(); 101 | server.listen(process.env.port || process.env.PORT || 3978, function() { 102 | console.log(`\n${ server.name } listening to ${ server.url }`); 103 | console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator.`); 104 | console.log(`\nTo talk to your bot, open teams-bot.bot file in the Emulator.`); 105 | }); 106 | 107 | // Listen for incoming activities and route them to your bot for processing. 108 | server.use(require('restify-plugins').bodyParser()); 109 | server.post('/api/messages', (req, res) => { 110 | adapter.processActivity(req, res, async (turnContext) => { 111 | // Call bot.onTurn() to handle all incoming messages. 112 | await bot.onTurn(turnContext); 113 | }); 114 | }); 115 | 116 | server.get('/auth/:state', (req, res) => { 117 | const body = onAuthResultBody(true, req.params.state); 118 | ( res).writeHead(200, { 119 | 'Content-Length': Buffer.byteLength(body), 120 | 'Content-Type': 'text/html' 121 | }); 122 | res.write(body); 123 | res.end(); 124 | }); 125 | 126 | const onAuthResultBody = (succeeded: boolean, state?: string, teamsSdk: string = 'https://statics.teams.cdn.office.net/sdk/v1.5.2/js/MicrosoftTeams.min.js') => 127 | { 128 | return succeeded ? ` 129 | 130 | 131 | 132 | 133 | 134 | 141 | 142 | 143 | 144 | ` : ` 145 | 146 | 147 | 148 | 149 | 150 | 154 | 155 | 156 | 157 | `}; 158 | -------------------------------------------------------------------------------- /samples/message-extension-bot/src/bot.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import { StatePropertyAccessor, TurnContext, CardFactory, BotState, Activity, ActionTypes, Attachment } from 'botbuilder-teams/node_modules/botbuilder'; 5 | import * as teams from 'botbuilder-teams'; 6 | 7 | // Turn counter property 8 | const TURN_COUNTER = 'turnCounterProperty'; 9 | 10 | export class TeamsBot { 11 | private readonly countAccessor: StatePropertyAccessor; 12 | private readonly conversationState: BotState; 13 | private readonly activityProc = new teams.TeamsActivityProcessor(); 14 | 15 | /** 16 | * 17 | * @param {ConversationState} conversation state object 18 | */ 19 | constructor (conversationState: BotState) { 20 | // Create a new state accessor property. See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors. 21 | this.countAccessor = conversationState.createProperty(TURN_COUNTER); 22 | this.conversationState = conversationState; 23 | this.setupHandlers(); 24 | } 25 | 26 | /** 27 | * Use onTurn to handle an incoming activity, received from a user, process it, and reply as needed 28 | * 29 | * @param {TurnContext} context on turn context object. 30 | */ 31 | async onTurn(turnContext: TurnContext) { 32 | await this.activityProc.processIncomingActivity(turnContext); 33 | } 34 | 35 | /** 36 | * Set up all activity handlers 37 | */ 38 | private setupHandlers () { 39 | this.activityProc.messageActivityHandler = { 40 | onMessage: async (ctx: TurnContext) => { 41 | const teamsCtx = teams.TeamsContext.from(ctx); 42 | const text = teamsCtx.getActivityTextWithoutMentions() || ''; 43 | 44 | switch (text.toLowerCase()) { 45 | case 'cards': 46 | await this.sendCards(ctx); 47 | break; 48 | 49 | default: 50 | let count = await this.countAccessor.get(ctx); 51 | count = count === undefined ? 1 : ++count; 52 | await this.countAccessor.set(ctx, count); 53 | 54 | let activity: Partial = { 55 | textFormat: 'xml', 56 | text: `${ count }: You said "${ ctx.activity.text }"` 57 | }; 58 | await ctx.sendActivity(activity); 59 | await this.conversationState.saveChanges(ctx); 60 | } 61 | } 62 | }; 63 | 64 | this.activityProc.invokeActivityHandler = { 65 | onMessagingExtensionQuery: async (ctx: TurnContext, query: teams.MessagingExtensionQuery) => { 66 | type R = teams.InvokeResponseTypeOf<'onMessagingExtensionQuery'>; 67 | 68 | let preview = CardFactory.thumbnailCard('Search Item Card', 'This is to show the search result'); 69 | let heroCard = CardFactory.heroCard('Result Card', '
This card mocks the CE results
'); 70 | let response: R = { 71 | status: 200, 72 | body: { 73 | composeExtension: { 74 | type: 'result', 75 | attachmentLayout: 'list', 76 | attachments: [ 77 | { ...heroCard, preview } 78 | ] 79 | } 80 | } 81 | }; 82 | 83 | return Promise.resolve(response); 84 | }, 85 | 86 | onMessagingExtensionFetchTask: async (ctx: TurnContext, query: teams.MessagingExtensionAction) => { 87 | type R = teams.InvokeResponseTypeOf<'onMessagingExtensionFetchTask'>; 88 | return Promise.resolve( { 89 | status: 200, 90 | body: { 91 | task: this.taskModuleResponse(query, false) 92 | } 93 | }); 94 | }, 95 | 96 | onMessagingExtensionSubmitAction: async (ctx: TurnContext, query: teams.MessagingExtensionAction) => { 97 | type R = teams.InvokeResponseTypeOf<'onMessagingExtensionSubmitAction'>; 98 | let body: R['body']; 99 | let data = query.data; 100 | if (data && data.done) { 101 | let sharedMessage = (query.commandId === 'shareMessage' && query.commandContext === 'message') 102 | ? `Shared message:
${JSON.stringify(query.messagePayload)}

` 103 | : ''; 104 | let preview = CardFactory.thumbnailCard('Created Card', `Your input: ${data.userText}`); 105 | let heroCard = CardFactory.heroCard('Created Card', `${sharedMessage}Your input:
${data.userText}
`); 106 | body = { 107 | composeExtension: { 108 | type: 'result', 109 | attachmentLayout: 'list', 110 | attachments: [ 111 | { ...heroCard, preview } 112 | ] 113 | } 114 | } 115 | } else if (query.commandId === 'createWithPreview' || query.botMessagePreviewAction) { 116 | if (!query.botMessagePreviewAction) { 117 | body = { 118 | composeExtension: { 119 | type: 'botMessagePreview', 120 | activityPreview: { 121 | attachments: [ 122 | this.taskModuleResponseCard(query) 123 | ] 124 | } 125 | } 126 | } 127 | } else { 128 | let userEditActivities = query.botActivityPreview; 129 | let card = userEditActivities 130 | && userEditActivities[0] 131 | && userEditActivities[0].attachments 132 | && userEditActivities[0].attachments[0]; 133 | if (!card) { 134 | body = { 135 | task: { 136 | type: 'message', 137 | value: 'Missing user edit card. Something wrong on Teams client.' 138 | } 139 | } 140 | } else if (query.botMessagePreviewAction === 'send') { 141 | body = undefined; 142 | await ctx.sendActivities([ 143 | { attachments: [card] } 144 | ]); 145 | } else if (query.botMessagePreviewAction === 'edit') { 146 | body = { 147 | task: { 148 | type: 'continue', 149 | value: { 150 | card: card 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } else { 157 | body = { 158 | task: this.taskModuleResponse(query, false) 159 | } 160 | } 161 | return Promise.resolve({ status: 200, body }); 162 | }, 163 | 164 | onTaskModuleFetch: async (ctx: TurnContext, query: teams.TaskModuleRequest) => { 165 | type R = teams.InvokeResponseTypeOf<'onTaskModuleFetch'>; 166 | const response: R = { 167 | status: 200, 168 | body: { 169 | task: this.taskModuleResponse(query, false) 170 | } 171 | }; 172 | return Promise.resolve(response); 173 | }, 174 | 175 | onTaskModuleSubmit: async (ctx: TurnContext, query: teams.TaskModuleRequest) => { 176 | type R = teams.InvokeResponseTypeOf<'onTaskModuleSubmit'>; 177 | const data = query.data; 178 | const response: R = { 179 | status: 200, 180 | body: { 181 | task: this.taskModuleResponse(query, !!data.done) 182 | } 183 | }; 184 | return Promise.resolve(response); 185 | }, 186 | 187 | onAppBasedLinkQuery: async (ctx: TurnContext, query: teams.AppBasedLinkQuery) => { 188 | type R = teams.InvokeResponseTypeOf<'onAppBasedLinkQuery'>; 189 | let previewImg = CardFactory.images([{ 190 | url: 'https://assets.pokemon.com/assets/cms2/img/pokedex/full/025.png' 191 | }]); 192 | let preview = CardFactory.thumbnailCard('Preview Card', `Your query URL: ${query.url}`, previewImg); 193 | let heroCard = CardFactory.heroCard('Preview Card', `Your query URL:
${query.url}
`, previewImg); 194 | const response: R = { 195 | status: 200, 196 | body: { 197 | composeExtension: { 198 | type: 'result', 199 | attachmentLayout: 'list', 200 | attachments: [ 201 | { ...heroCard, preview } 202 | ] 203 | } 204 | } 205 | }; 206 | return Promise.resolve(response); 207 | }, 208 | 209 | onInvoke: async (ctx: TurnContext) => { 210 | await ctx.sendActivity({ textFormat: 'xml', text: `[General onInvoke]
${JSON.stringify(ctx.activity, null, 2)}
`}); 211 | return { status: 200, body: { composeExtensions: {} } }; 212 | } 213 | }; 214 | } 215 | 216 | private async sendCards (ctx: TurnContext) { 217 | let adaptiveCard = teams.TeamsFactory.adaptiveCard({ 218 | version: '1.0.0', 219 | type: 'AdaptiveCard', 220 | body: [{ 221 | type: 'TextBlock', 222 | text: 'Bot Builder actions', 223 | size: 'large', 224 | weight: 'bolder' 225 | }], 226 | actions: [ 227 | teams.TeamsFactory.adaptiveCardAction({ 228 | type: ActionTypes.ImBack, 229 | title: 'imBack', 230 | value: 'text' 231 | }), 232 | teams.TeamsFactory.adaptiveCardAction({ 233 | type: ActionTypes.MessageBack, 234 | title: 'message back', 235 | value: { key: 'value' }, 236 | text: 'text received by bots', 237 | displayText: 'text display to users', 238 | }), 239 | teams.TeamsFactory.adaptiveCardAction({ 240 | type: 'invoke', 241 | title: 'invoke', 242 | value: { key: 'value' } 243 | }), 244 | teams.TeamsFactory.adaptiveCardAction({ 245 | type: ActionTypes.Signin, 246 | title: 'signin', 247 | value: process.env.host + '/auth/teams-test-auth-state' 248 | }) 249 | ] 250 | }); 251 | 252 | let taskModuleCard1 = teams.TeamsFactory.adaptiveCard({ 253 | version: '1.0.0', 254 | type: 'AdaptiveCard', 255 | body: [{ 256 | type: 'TextBlock', 257 | text: 'Task Module Adaptive Card', 258 | size: 'large', 259 | weight: 'bolder' 260 | }], 261 | actions: [ 262 | teams.TeamsFactory 263 | .taskModuleAction('Launch Task Module', {hiddenKey: 'hidden value from task module launcher'}) 264 | .toAdaptiveCardAction() 265 | ] 266 | }); 267 | 268 | let taskModuleCard2 = teams.TeamsFactory.heroCard('Task Moddule Hero Card', undefined, [ 269 | teams.TeamsFactory 270 | .taskModuleAction('Launch Task Module', {hiddenKey: 'hidden value from task module launcher'}) 271 | .toAction() 272 | ]); 273 | 274 | await ctx.sendActivities([ 275 | { attachments: [adaptiveCard] }, 276 | { attachments: [taskModuleCard1] }, 277 | { attachments: [taskModuleCard2] } 278 | ]); 279 | } 280 | 281 | private taskModuleResponse (query: any, done: boolean): teams.TaskModuleResponseBase { 282 | if (done) { 283 | return { 284 | type: 'message', 285 | value: 'Thanks for your inputs!' 286 | } 287 | } else { 288 | return { 289 | type: 'continue', 290 | value: { 291 | title: 'More Page', 292 | card: this.taskModuleResponseCard(query, (query.data && query.data.userText) || undefined) 293 | } 294 | }; 295 | } 296 | } 297 | 298 | private taskModuleResponseCard (data: any, textValue?: string): Attachment { 299 | return teams.TeamsFactory.adaptiveCard({ 300 | version: '1.0.0', 301 | type: 'AdaptiveCard', 302 | body: [ 303 | { 304 | type: 'TextBlock', 305 | text: `Your request:`, 306 | size: 'large', 307 | weight: 'bolder' 308 | }, 309 | { 310 | type: 'Container', 311 | style: 'emphasis', 312 | items: [ 313 | { 314 | type: 'TextBlock', 315 | text: JSON.stringify(data), 316 | wrap: true 317 | } 318 | ] 319 | }, 320 | { 321 | type: 'Input.Text', 322 | id: 'userText', 323 | placeholder: 'Type text here...', 324 | value: textValue 325 | } 326 | ], 327 | actions: [ 328 | { 329 | type: 'Action.Submit', 330 | title: 'Next', 331 | data: { 332 | done: false 333 | } 334 | }, 335 | { 336 | type: 'Action.Submit', 337 | title: 'Submit', 338 | data: { 339 | done: true 340 | } 341 | } 342 | ] 343 | }) 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /samples/message-extension-bot/teams-app-manifest/icon-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/message-extension-bot/teams-app-manifest/icon-color.png -------------------------------------------------------------------------------- /samples/message-extension-bot/teams-app-manifest/icon-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/message-extension-bot/teams-app-manifest/icon-outline.png -------------------------------------------------------------------------------- /samples/message-extension-bot/teams-app-manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://github.com/OfficeDev/microsoft-teams-app-schema/blob/preview/DevPreview/MicrosoftTeams.schema.json", 3 | "manifestVersion": "devPreview", 4 | "version": "1.0", 5 | "id": "{YOUR_APP_ID}", 6 | "packageName": "com.microsoft.teams.samples.v4bot", 7 | "developer": { 8 | "name": "Microsoft Corp", 9 | "websiteUrl": "https://example.azurewebsites.net", 10 | "privacyUrl": "https://example.azurewebsites.net/privacy", 11 | "termsOfUseUrl": "https://example.azurewebsites.net/termsofuse" 12 | }, 13 | "name": { 14 | "short": "V4 Sample", 15 | "full": "Microsoft Teams V4 Sample Bot" 16 | }, 17 | "description": { 18 | "short": "Sample bot using V4 SDK", 19 | "full": "Sample bot using V4 Bot Builder SDK and V4 Microsoft Teams Extension SDK" 20 | }, 21 | "icons": { 22 | "outline": "icon-outline.png", 23 | "color": "icon-color.png" 24 | }, 25 | "accentColor": "#abcdef", 26 | "bots": [ 27 | { 28 | "botId": "{YOUR_BOT_ID}", 29 | "scopes": ["personal", "team"] 30 | } 31 | ], 32 | "composeExtensions": [ 33 | { 34 | "botId": "{YOUR_BOT_ID}", 35 | "commands": [ 36 | { 37 | "id": "queryCards", 38 | "description": "Test command to run query", 39 | "title": "Query cards", 40 | "parameters": [ 41 | { 42 | "name": "queryText", 43 | "title": "Query parameter", 44 | "description": "Query parameter" 45 | } 46 | ] 47 | }, 48 | { 49 | "id": "createCard", 50 | "type": "action", 51 | "description": "Test command to run action to create a card", 52 | "title": "Create cards", 53 | "fetchTask": true, 54 | "parameters": [ 55 | { 56 | "name": "dummy", 57 | "title": "Dummy parameter", 58 | "description": "Dummy parameter" 59 | } 60 | ] 61 | }, 62 | { 63 | "id": "createWithPreview", 64 | "type": "action", 65 | "description": "Test command to run action to create a card with preview before sending", 66 | "title": "Create cards with preview", 67 | "fetchTask": true, 68 | "parameters": [ 69 | { 70 | "name": "dummy", 71 | "title": "Dummy parameter", 72 | "description": "Dummy parameter" 73 | } 74 | ] 75 | }, 76 | { 77 | "id": "shareMessage", 78 | "type": "action", 79 | "context": [ "message" ], 80 | "description": "Test command to run action on message context (message sharing)", 81 | "title": "Create cards", 82 | "fetchTask": true, 83 | "parameters": [ 84 | { 85 | "name": "dummy", 86 | "title": "Dummy parameter", 87 | "description": "Dummy parameter" 88 | } 89 | ] 90 | } 91 | ], 92 | "messageHandlers": [ 93 | { 94 | "type": "link", 95 | "value": { 96 | "domains": [ 97 | "*.azurewebsites.net", 98 | "*.example.com" 99 | ] 100 | } 101 | } 102 | ] 103 | } 104 | ], 105 | "validDomains": [ 106 | "*.azurewebsites.net", 107 | "*.example.com" 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /samples/message-extension-bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./lib", 8 | "rootDir": "./src", 9 | "types" : ["node"] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ] 14 | } -------------------------------------------------------------------------------- /samples/teams-bot/.env: -------------------------------------------------------------------------------- 1 | botFilePath=bot-file.json 2 | botFileSecret= 3 | -------------------------------------------------------------------------------- /samples/teams-bot/bot-file.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echobot-with-counter", 3 | "services": [ 4 | { 5 | "type": "endpoint", 6 | "name": "development", 7 | "endpoint": "http://localhost:3978/api/messages", 8 | "appId": "", 9 | "appPassword": "", 10 | "id": "1" 11 | } 12 | ], 13 | "padlock": "", 14 | "version": "2.0" 15 | } 16 | -------------------------------------------------------------------------------- /samples/teams-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-teams-js-teams-bot", 3 | "version": "0.0.1", 4 | "description": "Microsoft Teams Sample Bot for BotBuilder V4", 5 | "author": "Microsoft Corp", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "npm run build:live", 9 | "build:live": "nodemon --ignore './data' --exec node --inspect=5566 -r ts-node/register ./src/app.ts" 10 | }, 11 | "dependencies": { 12 | "botbuilder-teams": "file:../../botbuilder-teams-js", 13 | "botframework-config": "^4.2.0", 14 | "dotenv": "^6.2.0", 15 | "nodemon": "^1.18.9", 16 | "restify": "^7.6.0", 17 | "restify-plugins": "^1.6.0", 18 | "ts-node": "^7.0.1" 19 | }, 20 | "devDependencies": { 21 | "@types/restify": "^7.2.7", 22 | "typescript": "^3.2.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/teams-bot/src/app.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as restify from 'restify'; 5 | import * as path from 'path'; 6 | import { config } from 'dotenv'; 7 | 8 | // Import required bot services. See https://aka.ms/bot-services to learn more about the different parts of a bot. 9 | import { MemoryStorage, BotFrameworkAdapterSettings } from 'botbuilder-teams/node_modules/botbuilder'; 10 | import * as teams from 'botbuilder-teams'; 11 | 12 | // Import required bot configuration. 13 | import { BotConfiguration, IEndpointService } from 'botframework-config'; 14 | 15 | import { TeamsBot } from './bot'; 16 | 17 | // Read botFilePath and botFileSecret from .env file 18 | // Note: Ensure you have a .env file and include botFilePath and botFileSecret. 19 | const ENV_FILE = path.join(__dirname, '..', '.env'); 20 | const loadFromEnv = config({path: ENV_FILE}); 21 | 22 | // Get the .bot file path 23 | // See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. 24 | const BOT_FILE = path.join(__dirname, '..', (process.env.botFilePath || '')); 25 | let botConfig; 26 | try { 27 | // read bot configuration from .bot file. 28 | botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret); 29 | } catch (err) { 30 | console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`); 31 | console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`); 32 | console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.`); 33 | console.error(`\n - See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration.\n\n`); 34 | process.exit(); 35 | } 36 | 37 | // For local development configuration as defined in .bot file. 38 | const DEV_ENVIRONMENT = 'development'; 39 | 40 | // Define name of the endpoint configuration section from the .bot file. 41 | const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT); 42 | 43 | // Get bot endpoint configuration by service name. 44 | // Bot configuration as defined in .bot file. 45 | const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION); 46 | 47 | // // Create adapter. 48 | // See https://aka.ms/about-bot-adapter to learn more about to learn more about bot adapter. 49 | const botSetting: Partial = { 50 | appId: endpointConfig.appId || process.env.microsoftAppID, 51 | appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword, 52 | }; 53 | 54 | const adapter = new teams.TeamsAdapter(botSetting); 55 | 56 | // Use Teams middleware 57 | adapter.use(new teams.TeamsMiddleware()); 58 | 59 | // Catch-all for any unhandled errors in your bot. 60 | adapter.onTurnError = async (turnContext, error) => { 61 | // This check writes out errors to console log .vs. app insights. 62 | console.error(`\n [onTurnError]: ${ error }`); 63 | // Send a message to the user. 64 | turnContext.sendActivity(`Oops. Something went wrong!`); 65 | // Clear out state and save changes so the user is not stuck in a bad state. 66 | await conversationState.clear(turnContext); 67 | await conversationState.saveChanges(turnContext); 68 | }; 69 | 70 | // For local development, in-memory storage is used. 71 | // CAUTION: The Memory Storage used here is for local bot debugging only. When the bot 72 | // is restarted, anything stored in memory will be gone. 73 | const memoryStorage = new MemoryStorage(); 74 | 75 | 76 | // Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. 77 | // A bot requires a state store to persist the dialog and user state between messages. 78 | // let conversationState = new ConversationState(memoryStorage); 79 | let conversationState = new teams.TeamSpecificConversationState(memoryStorage); 80 | 81 | // CAUTION: You must ensure your product environment has the NODE_ENV set 82 | // to use the Azure Blob storage or Azure Cosmos DB providers. 83 | // import { BlobStorage } from 'botbuilder-azure'; 84 | // Storage configuration name or ID from .bot file 85 | // const STORAGE_CONFIGURATION_ID = ''; 86 | // // Default container name 87 | // const DEFAULT_BOT_CONTAINER = ''; 88 | // // Get service configuration 89 | // const blobStorageConfig = botConfig.findServiceByNameOrId(STORAGE_CONFIGURATION_ID); 90 | // const blobStorage = new BlobStorage({ 91 | // containerName: (blobStorageConfig.container || DEFAULT_BOT_CONTAINER), 92 | // storageAccountOrConnectionString: blobStorageConfig.connectionString, 93 | // }); 94 | // conversationState = new ConversationState(blobStorage); 95 | 96 | // Create the TeamsBot. 97 | const bot = new TeamsBot(conversationState); 98 | 99 | // Create HTTP server 100 | let server = restify.createServer(); 101 | server.listen(process.env.port || process.env.PORT || 3978, function() { 102 | console.log(`\n${ server.name } listening to ${ server.url }`); 103 | console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator.`); 104 | console.log(`\nTo talk to your bot, open teams-bot.bot file in the Emulator.`); 105 | }); 106 | 107 | // Listen for incoming activities and route them to your bot for processing. 108 | server.use(require('restify-plugins').bodyParser()); 109 | server.post('/api/messages', (req, res) => { 110 | adapter.processActivity(req, res, async (turnContext) => { 111 | // Call bot.onTurn() to handle all incoming messages. 112 | await bot.onTurn(turnContext); 113 | }); 114 | }); 115 | 116 | server.get('/auth', (req, res) => { 117 | const body = onAuthResultBody(true, 'teams-auth-state-test'); 118 | ( res).writeHead(200, { 119 | 'Content-Length': Buffer.byteLength(body), 120 | 'Content-Type': 'text/html' 121 | }); 122 | res.write(body); 123 | res.end(); 124 | }); 125 | 126 | const onAuthResultBody = (succeeded: boolean, state?: string, teamsSdk: string = 'https://statics.teams.cdn.office.net/sdk/v1.5.2/js/MicrosoftTeams.min.js') => 127 | { 128 | return succeeded ? ` 129 | 130 | 131 | 132 | 133 | 134 | 141 | 142 | 143 | 144 | ` : ` 145 | 146 | 147 | 148 | 149 | 150 | 154 | 155 | 156 | 157 | `}; 158 | -------------------------------------------------------------------------------- /samples/teams-bot/src/bot.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import { StatePropertyAccessor, TurnContext, CardFactory, BotState, Activity } from 'botbuilder-teams/node_modules/botbuilder'; 5 | import * as teams from 'botbuilder-teams'; 6 | 7 | // Turn counter property 8 | const TURN_COUNTER = 'turnCounterProperty'; 9 | 10 | export class TeamsBot { 11 | private readonly countAccessor: StatePropertyAccessor; 12 | private readonly conversationState: BotState; 13 | private readonly activityProc = new teams.TeamsActivityProcessor(); 14 | 15 | /** 16 | * 17 | * @param {ConversationState} conversation state object 18 | */ 19 | constructor (conversationState: BotState) { 20 | // Create a new state accessor property. See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors. 21 | this.countAccessor = conversationState.createProperty(TURN_COUNTER); 22 | this.conversationState = conversationState; 23 | this.setupHandlers(); 24 | } 25 | 26 | /** 27 | * Use onTurn to handle an incoming activity, received from a user, process it, and reply as needed 28 | * 29 | * @param {TurnContext} context on turn context object. 30 | */ 31 | async onTurn(turnContext: TurnContext) { 32 | await this.activityProc.processIncomingActivity(turnContext); 33 | } 34 | 35 | /** 36 | * Set up all activity handlers 37 | */ 38 | private setupHandlers () { 39 | this.activityProc.messageActivityHandler = { 40 | onMessage: async (ctx: TurnContext) => { 41 | const teamsCtx = teams.TeamsContext.from(ctx); 42 | const text = teamsCtx.getActivityTextWithoutMentions(); 43 | const adapter = ctx.adapter as teams.TeamsAdapter; 44 | 45 | switch (text.toLowerCase()) { 46 | case 'cards': 47 | await this.sentCards(ctx); 48 | break; 49 | 50 | case 'reply-chain': 51 | await adapter.createReplyChain(ctx, [{ text: 'New reply chain' }]); 52 | break; 53 | 54 | case '1:1': 55 | // create 1:1 conversation 56 | const tenantId = teamsCtx.tenant.id; 57 | const ref = TurnContext.getConversationReference(ctx.activity); 58 | await adapter.createTeamsConversation(ref, tenantId, async (newCtx) => { 59 | await newCtx.sendActivity(`Hi (in private)`); 60 | }); 61 | break; 62 | 63 | case 'members': 64 | const members = await adapter.getConversationMembers(ctx); 65 | const actMembers = await adapter.getActivityMembers(ctx); 66 | await ctx.sendActivity({ 67 | textFormat: 'xml', 68 | text: ` 69 | Activity Members
70 |
${JSON.stringify(actMembers, null, 2)}
71 | Conversation Members
72 |
${JSON.stringify(members, null, 2)}
73 | `}); 74 | break; 75 | 76 | case 'team-info': 77 | const teamId = teamsCtx.team.id; 78 | const chList = await teamsCtx.teamsConnectorClient.teams.fetchChannelList(teamId); 79 | const tmDetails = await teamsCtx.teamsConnectorClient.teams.fetchTeamDetails(teamId); 80 | await ctx.sendActivity({ textFormat: 'xml', text: `
${JSON.stringify(chList, null, 2)}
`}); 81 | await ctx.sendActivity({ textFormat: 'xml', text: `
${JSON.stringify(tmDetails, null, 2)}
`}); 82 | break; 83 | 84 | default: 85 | let count = await this.countAccessor.get(ctx); 86 | count = count === undefined ? 1 : ++count; 87 | await this.countAccessor.set(ctx, count); 88 | 89 | let activity: Partial = { 90 | textFormat: 'xml', 91 | text: `${ count }: You said "${ ctx.activity.text }"` 92 | }; 93 | 94 | // activity = teamsCtx.addMentionToText(activity as Activity, turnContext.activity.from); 95 | activity = teams.TeamsContext.notifyUser(activity as Activity); 96 | 97 | await ctx.sendActivity(activity); 98 | await this.conversationState.saveChanges(ctx); 99 | } 100 | } 101 | }; 102 | 103 | this.activityProc.conversationUpdateActivityHandler = { 104 | onChannelRenamed: async (event) => { 105 | const e = { 106 | channel: event.channel, 107 | eventType: event.eventType, 108 | team: event.team, 109 | tenant: event.tenant 110 | } 111 | await event.turnContext.sendActivity({ textFormat: 'xml', text: `[Conversation Update]
${JSON.stringify(e, null, 2)}
`}) 112 | } 113 | }; 114 | 115 | this.activityProc.messageReactionActivityHandler = { 116 | onMessageReaction: async (ctx: TurnContext) => { 117 | const added = ctx.activity.reactionsAdded; 118 | const removed = ctx.activity.reactionsRemoved; 119 | await ctx.sendActivity({ 120 | textFormat: 'xml', 121 | text: ` 122 |

[Reaction Event]


123 | Added
124 |
${JSON.stringify(added, null, 2)}
125 | Removed
126 |
${JSON.stringify(removed, null, 2)}
127 | Activity
128 |
${JSON.stringify(ctx.activity, null, 2)}
129 | `}); 130 | 131 | const adapter = ctx.adapter as teams.TeamsAdapter; 132 | const members = await adapter.getConversationMembers(ctx); 133 | const fromAad = ctx.activity.from.aadObjectId; 134 | const member = members.find(m => m.aadObjectId === fromAad); 135 | const memberName = member && member.givenName; 136 | const isLike = added && added[0] && added[0].type === 'like'; 137 | if (memberName) { 138 | const text = isLike 139 | ? `${memberName}, thanks for liking my message! 😍😘` 140 | : `${memberName}, why don't you like what I said? 😭😢`; 141 | await ctx.sendActivity({ textFormat: 'xml', text }); 142 | } 143 | } 144 | }; 145 | 146 | this.activityProc.invokeActivityHandler = { 147 | onO365CardAction: async (ctx: TurnContext, query: teams.O365ConnectorCardActionQuery) => { 148 | let userName = ctx.activity.from.name; 149 | let body = JSON.parse(query.body); 150 | let msg: Partial = { 151 | summary: 'Thanks for your input!', 152 | textFormat: 'xml', 153 | text: `

Thanks, ${userName}!


Your input action ID:


${query.actionId}

Your input body:


${JSON.stringify(body, null, 2)}
` 154 | }; 155 | await ctx.sendActivity(msg); 156 | return { status: 200 }; 157 | }, 158 | 159 | onMessagingExtensionQuery: async (ctx: TurnContext, query: teams.MessagingExtensionQuery) => { 160 | type R = teams.InvokeResponseTypeOf<'onMessagingExtensionQuery'>; 161 | 162 | let preview = CardFactory.thumbnailCard('Search Item Card', 'This is to show the search result'); 163 | let heroCard = CardFactory.heroCard('Result Card', '
This card mocks the CE results
'); 164 | 165 | return Promise.resolve( { 166 | status: 200, 167 | 'body': { 168 | 'composeExtension': { 169 | 'type': 'result', 170 | 'attachmentLayout': 'list', 171 | 'attachments': [ 172 | { ...heroCard, preview } 173 | ] 174 | } 175 | } 176 | }); 177 | }, 178 | 179 | onInvoke: async (ctx: TurnContext) => { 180 | await ctx.sendActivity({ textFormat: 'xml', text: `[General onInvoke]
${JSON.stringify(ctx.activity, null, 2)}
`}); 181 | return { status: 200, body: { composeExtensions: {} } }; 182 | } 183 | }; 184 | } 185 | 186 | private async sentCards (ctx: TurnContext) { 187 | let heroCard = CardFactory.heroCard( 188 | 'Card title', 189 | undefined, 190 | CardFactory.actions([ 191 | { 192 | type: 'imBack', 193 | title: 'imBack', 194 | value: 'Test for imBack' 195 | }, 196 | { 197 | type: 'invoke', 198 | title: 'invoke', 199 | value: { key: 'invoke value' } 200 | }, 201 | { 202 | type: 'messageBack', 203 | title: 'messageBack', 204 | value: { key: 'messageback value' }, 205 | text: 'text seen by bot', 206 | displayText: 'text seen by users' 207 | } 208 | ])); 209 | 210 | let signinCard = CardFactory.signinCard('Signin', 'https://1355e2b4.ngrok.io/auth', 'Signin Card Test'); 211 | 212 | let o365Card = teams.TeamsFactory.o365ConnectorCard({ 213 | 'summary': 'a o365 card', 214 | 'themeColor': '#acd45f', 215 | 'title': 'O365 card', 216 | 'potentialAction': [ 217 | { 218 | '@type': 'HttpPOST', 219 | '@id': 'justSubmit', 220 | 'name': 'Http POST', 221 | 'body': JSON.stringify({ key: 'value' }) 222 | }, 223 | { 224 | '@type': 'ActionCard', 225 | '@id': 'actionCard', 226 | 'name': 'Show Card', 227 | 'inputs': [ 228 | { 229 | '@type': 'textInput', 230 | 'id': 'text-1', 231 | 'isMultiline': true 232 | } 233 | ], 234 | 'actions': [ 235 | { 236 | '@type': 'HttpPOST', 237 | '@id': 'submit', 238 | 'name': 'Http POST', 239 | 'body': JSON.stringify({ 240 | 'text-1': '{{text-1.value}}' 241 | }) 242 | } 243 | ] 244 | } 245 | ] 246 | }); 247 | 248 | await ctx.sendActivities([ 249 | { attachments: [heroCard] }, 250 | { attachments: [signinCard] }, 251 | { attachments: [o365Card] } 252 | ]); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /samples/teams-bot/teams-app-manifest/icon-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/teams-bot/teams-app-manifest/icon-color.png -------------------------------------------------------------------------------- /samples/teams-bot/teams-app-manifest/icon-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/BotBuilder-MicrosoftTeams-node/1f923ecd7e4ab0bae6827226bc7e6c764bf54947/samples/teams-bot/teams-app-manifest/icon-outline.png -------------------------------------------------------------------------------- /samples/teams-bot/teams-app-manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.3/MicrosoftTeams.schema.json", 3 | "manifestVersion": "1.3", 4 | "version": "1.0", 5 | "id": "{YOUR_APP_ID}", 6 | "packageName": "com.microsoft.teams.samples.v4bot", 7 | "developer": { 8 | "name": "Microsoft Corp", 9 | "websiteUrl": "https://example.azurewebsites.net", 10 | "privacyUrl": "https://example.azurewebsites.net/privacy", 11 | "termsOfUseUrl": "https://example.azurewebsites.net/termsofuse" 12 | }, 13 | "name": { 14 | "short": "V4 Sample", 15 | "full": "Microsoft Teams V4 Sample Bot" 16 | }, 17 | "description": { 18 | "short": "Sample bot using V4 SDK", 19 | "full": "Sample bot using V4 Bot Builder SDK and V4 Microsoft Teams Extension SDK" 20 | }, 21 | "icons": { 22 | "outline": "icon-outline.png", 23 | "color": "icon-color.png" 24 | }, 25 | "accentColor": "#abcdef", 26 | "bots": [ 27 | { 28 | "botId": "{YOUR_BOT_ID}", 29 | "scopes": ["personal", "team"] 30 | } 31 | ], 32 | "composeExtensions": [ 33 | { 34 | "botId": "{YOUR_BOT_ID}", 35 | "commands": [ 36 | { 37 | "id": "queryCards", 38 | "description": "Test command to run query", 39 | "title": "Query cards", 40 | "parameters": [ 41 | { 42 | "name": "queryText", 43 | "title": "Query parameter", 44 | "description": "Query parameter" 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | ], 51 | "validDomains": ["*.azurewebsites.net"] 52 | } 53 | -------------------------------------------------------------------------------- /samples/teams-bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./lib", 8 | "rootDir": "./src", 9 | "types" : ["node"] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ] 14 | } --------------------------------------------------------------------------------