diff --git a/libraries/botbuilder/src/teamsActivityHelpers.ts b/libraries/botbuilder/src/teamsActivityHelpers.ts index 72ffb28bcc..b7d35dcd4e 100644 --- a/libraries/botbuilder/src/teamsActivityHelpers.ts +++ b/libraries/botbuilder/src/teamsActivityHelpers.ts @@ -8,6 +8,12 @@ * Activity helper methods for Teams. */ +export function teamsGetChannelId(activity : object) { + const channelData = ('channelData' in activity) ? activity['channelData'] : null; + const channel = (validObject(channelData) && 'channel' in channelData) ? channelData['channel'] : null; + return (validObject(channel) && 'id' in channel) ? channel['id'] : null; +} + export function teamsGetTeamId(activity : object) { const channelData = ('channelData' in activity) ? activity['channelData'] : null; const team = (validObject(channelData) && 'team' in channelData) ? channelData['team'] : null; diff --git a/libraries/botbuilder/tests/teams/replyToChannel/.env b/libraries/botbuilder/tests/teams/replyToChannel/.env new file mode 100644 index 0000000000..660828e3e8 --- /dev/null +++ b/libraries/botbuilder/tests/teams/replyToChannel/.env @@ -0,0 +1,2 @@ +MicrosoftAppId= +MicrosoftAppPassword= \ No newline at end of file diff --git a/libraries/botbuilder/tests/teams/replyToChannel/package.json b/libraries/botbuilder/tests/teams/replyToChannel/package.json new file mode 100644 index 0000000000..0386916c7a --- /dev/null +++ b/libraries/botbuilder/tests/teams/replyToChannel/package.json @@ -0,0 +1,30 @@ +{ + "name": "reply-to-channel", + "version": "1.0.0", + "description": "", + "main": "./lib/index.js", + "scripts": { + "start": "tsc --build && node ./lib/index.js", + "build": "tsc --build", + "watch": "nodemon --watch ./src -e ts --exec \"npm run start\"" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "botbuilder": "file:../../../", + "dotenv": "^8.1.0", + "node-fetch": "^2.6.0", + "restify": "^8.4.0", + "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/node": "^12.7.1", + "@types/node-fetch": "^2.5.0", + "@types/request": "^2.48.1", + "@types/restify": "^7.2.7", + "nodemon": "^1.19.1", + "ts-node": "^7.0.1", + "typescript": "^3.2.4" + } +} diff --git a/libraries/botbuilder/tests/teams/replyToChannel/src/index.ts b/libraries/botbuilder/tests/teams/replyToChannel/src/index.ts new file mode 100644 index 0000000000..0e6f77efca --- /dev/null +++ b/libraries/botbuilder/tests/teams/replyToChannel/src/index.ts @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { config } from 'dotenv'; +import * as path from 'path'; +import * as restify from 'restify'; + +// Import required bot services. +// See https://aka.ms/bot-services to learn more about the different parts of a bot. +import { BotFrameworkAdapter, MemoryStorage } from 'botbuilder'; + +// This bot's main dialog. +import { ReplyToChannelBot } from './replyToChannelBot'; + +const ENV_FILE = path.join(__dirname, '..', '.env'); +config({ path: ENV_FILE }); + +// Create HTTP server. +const server = restify.createServer(); +server.listen(process.env.port || process.env.PORT || 3978, () => { + console.log(`\n${server.name} listening to ${server.url}`); + console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`); + console.log(`\nTo test your bot, see: https://aka.ms/debug-with-emulator`); +}); + +// Create adapter. +// See https://aka.ms/about-bot-adapter to learn more about adapters. +const adapter = new BotFrameworkAdapter({ + appId: process.env.MicrosoftAppId, + appPassword: process.env.MicrosoftAppPassword +}); + +// Catch-all for errors. +adapter.onTurnError = async (context, error) => { + // This check writes out errors to console log .vs. app insights. + console.error('[onTurnError]:'); + console.error(error); + // Send a message to the user + await context.sendActivity(`Oops. Something went wrong in the bot!\n ${error.message}`); +}; + +// Create the main dialog. +const myBot = new ReplyToChannelBot(); + +// Listen for incoming requests. +server.post('/api/messages', (req, res) => { + adapter.processActivity(req, res, async (context) => { + // Route to main dialog.5 + await myBot.run(context); + }); +}); diff --git a/libraries/botbuilder/tests/teams/replyToChannel/src/replyToChannelBot.ts b/libraries/botbuilder/tests/teams/replyToChannel/src/replyToChannelBot.ts new file mode 100644 index 0000000000..f8a12169c0 --- /dev/null +++ b/libraries/botbuilder/tests/teams/replyToChannel/src/replyToChannelBot.ts @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { + MessageFactory, + TeamsActivityHandler, + teamsCreateConversation, + teamsGetChannelId, + BotFrameworkAdapter, +} from 'botbuilder'; +import { basename } from 'path'; + +export class ReplyToChannelBot extends TeamsActivityHandler { + botId: string; + + constructor() { + super(); + + // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. + this.onMessage(async (context, next) => { + + const teamChannelId = teamsGetChannelId(context.activity); + const message = MessageFactory.text("good morning"); + const newConversation = await teamsCreateConversation(context, teamChannelId, message); + + const adapter = context.adapter as BotFrameworkAdapter; + + await adapter.continueConversation(newConversation[0], + async (t) => + { + await t.sendActivity(MessageFactory.text("good afternoon")); + await t.sendActivity(MessageFactory.text("good night")); + }); + + // By calling next() you ensure that the next BotHandler is run. + await next(); + }); + } +} diff --git a/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/icon-color.png b/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/icon-color.png new file mode 100644 index 0000000000..bd9928dfc8 Binary files /dev/null and b/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/icon-color.png differ diff --git a/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/icon-outline.png b/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/icon-outline.png new file mode 100644 index 0000000000..45e7a7c56e Binary files /dev/null and b/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/icon-outline.png differ diff --git a/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/manifest.json b/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/manifest.json new file mode 100644 index 0000000000..12c087df4b --- /dev/null +++ b/libraries/botbuilder/tests/teams/replyToChannel/teams-app-manifest/manifest.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.3/MicrosoftTeams.schema.json", + "manifestVersion": "1.3", + "version": "1.0.0", + "id": "", + "packageName": "com.teams.sample.replyToChannel", + "developer": { + "name": "ReplyToChannelBot", + "websiteUrl": "https://www.microsoft.com", + "privacyUrl": "https://www.teams.com/privacy", + "termsOfUseUrl": "https://www.teams.com/termsofuser" + }, + "icons": { + "color": "icon-color.png", + "outline": "icon-outline.png" + }, + "name": { + "short": "ReplyToChannelBot", + "full": "ReplyToChannelBot" + }, + "description": { + "short": "ReplyToChannelBot", + "full": "ReplyToChannelBot" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "", + "scopes": [ + "groupchat", + "team", + "personal" + ], + "supportsFiles": false, + "isNotificationOnly": false + } + ], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/libraries/botbuilder/tests/teams/replyToChannel/tsconfig.json b/libraries/botbuilder/tests/teams/replyToChannel/tsconfig.json new file mode 100644 index 0000000000..61761d2e32 --- /dev/null +++ b/libraries/botbuilder/tests/teams/replyToChannel/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2016", + "module": "commonjs", + "composite": true, + "declaration": true, + "sourceMap": true, + "outDir": "./lib", + "rootDir": "./src" + } +} \ No newline at end of file