14

I have a bot that is identical to the one demonstrated in the docs quickstart. It repeats back whatever the user says (for now).

It is currently running locally and exposed with ngrok. I've registered the bot with the Microsoft Bot Framework.

I have configured the Microsoft Teams channel in the Microsoft Bot Framework, and I've sideloaded my bot into Teams. My bot can receive messages from Teams users.

At present, the bot just repeats whatever it receives back to the user, but what I want it to do is post to a Microsoft Teams channel. I want it to post to a Teams channel - not a user - without being prompted first by a user. So for example given a certain condition (eg. triggered by some event such as time of day, a pull request, etc.) it posts a message in a channel.

I've read the documentation about sending proactive messages, and I gather that in order to send a message to a teams channel, the bot needs to know the "address" of the user. This information is stored in the session.message.address object, and it gets this from the current conversation. However, in my case I don't have a 'current conservation', because I don't want to just respond to a user, I want to post in a channel proactively.

So, how do I permanently set the necessary credentials/address/session-data for the Teams channel?

Things I've looked into:

  • Webhooks. I've configured a webhook in my Teams channel, and I can send it a message easily enough (using the webhook url) using curl. So I can send the Teams channel a simple message with just a url (no authentication required), but I'm not sure how I'd get this url into my bot.

  • How do we maintain different session for different users in Microsoft Bot Framework? I'm not sure that the answer here answers my question. My problem is that the bot is initiating the 'conversation', not a Teams user, so I need to be able to set the session data myself so the bot knows where to go.

App.js:

require('dotenv').config();
var restify = require('restify');
var builder = require('botbuilder');

// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
   console.log('%s listening to %s', server.name, server.url); 
});

// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD    
});

// Listen for messages from users 
server.post('/api/messages', connector.listen());

// Receive messages from the user and respond by echoing each message back (prefixed with 'You said:')
var bot = new builder.UniversalBot(connector, function (session) {
    session.send("You said: %s", session.message.text);
});
NDevBrad
  • 19
  • 8
andydavies
  • 3,081
  • 4
  • 29
  • 35

3 Answers3

8

For anyone who is wondering about the same for c#, here is the solution that worked for me:

    var channelData = context.Activity.GetChannelData<TeamsChannelData>();
    var message = Activity.CreateMessageActivity();
    message.Text = "Hello World";

    var conversationParameters = new ConversationParameters
    {
         IsGroup = true,
         ChannelData = new TeamsChannelData
          {
             Channel = new ChannelInfo(channelData.Channel.Id),
         },
         Activity = (Activity) message
    };

    var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl));
    var response = await 
    connectorClient.Conversations.CreateConversationAsync(conversationParameters);

Note: If you are calling this outside Bot's controller code then you need to call TrustServiceUrl on serviceUrl as shown here:

    MicrosoftAppCredentials.TrustServiceUrl(serviceUrl, DateTime.MaxValue);
    var connectorClient = new ConnectorClient(new Uri(serviceUrl));

Source of answer: https://github.com/OfficeDev/BotBuilder-MicrosoftTeams/issues/162

  • What is `IsGroup` for? I have tried to remove it and it works as expected. – SerjG Feb 15 '19 at 06:42
  • i am following github links but not able to find how to get Channel Information like ID etc? i know how to get incoming Web hook URL which is irrelevant here. – AbhishekS Oct 14 '19 at 11:59
  • Here's an example of how (where activity is Microsoft.Bot.Connector.Activity class): var channelData = activity.GetChannelData(); var id = channelData.Channel.Id; – redmondcoffehead Oct 15 '19 at 22:18
2

It is definitely possible. We call these proactive messages and it’s possible to proactively message both users and channels.

For the latter, see the sample at https://github.com/OfficeDev/microsoft-teams-sample-complete-node, specifically this file, ProactiveMsgToChannelDialog.ts.

To send proactive messages to channels, you need to use the Microsoft Teams SDK (as these samples do).

Last but not least, you need to add the bot to a team in order to send a message to one of the channels in the team, which requires a manifest.

andydavies
  • 3,081
  • 4
  • 29
  • 35
Bill Bliss - MSFT
  • 3,553
  • 1
  • 14
  • 17
  • Thanks Bill. So I gather that I set the "address/id" of the channel in `src/utils/DialogIds.ts` (ProactiveMsgToChannelDialogId)? Where do I find this ID in MS Teams? – andydavies Nov 28 '17 at 16:19
  • It has nothing to do with `src/utils/DialogIds.ts`. In `ProactiveMsgToChannelDialogId` the logic is that the user types the name of the channel, and then it calls `teamsChatConnector.fetchChannelList` to iterate through the list of channels for the team and finds the one whose name matches what the user types (if any) and then posts to that channel using its ID. Look at the logic for the callback function in `ProactiveMsgToChannelDialogId`, specifically lines 38-74. One of the arguments is `result` which is an array of Channel objects, including `.id` and `.name` - `.id` is what you want. – Bill Bliss - MSFT Nov 29 '17 at 17:43
  • @andydavies were you able to get this working? I'm trying to do similar. – Haymak3r Aug 21 '18 at 19:15
  • @Haymak3r no, I didn't manage it and then I was moved onto something else (it was a work task). But if the answer works for you let me know and I'll accept it as 'answered'. Or if you manage it another way put it on here and I'll mark it accepted. – andydavies Aug 22 '18 at 09:30
  • 2
    @BillBliss-MSFT This answer looks like it is reactive to a message sent by a user, rather than truly proactive. For example in step1 you need a session and for that to have a message (!isMessageFromChannel(session.message)) Step 2 also requires a session. Andy's question was how do you trigger from some non-messaging event: "(eg. triggered by some event such as time of day, a pull request, etc.) it posts a message in a channel". Is that possible? – Matt Cobb Dec 26 '19 at 20:48
  • I don't think it's possible up to now, there's a graph endpoint that does what's needed however it doesn't support application type of tokens, so the only way I found myself is to post a message on behalf of a user https://learn.microsoft.com/en-us/graph/api/chatmessage-post?view=graph-rest-beta&tabs=http – hdmiimdh Feb 25 '21 at 06:45
0

Hope this works for you.. below code proactively sends the message to session before initiating the chat.

 bot.on('conversationUpdate', function (message) {  
       if (message.membersAdded[0].id === message.address.bot.id) {             
             var reply = new builder.Message()    
                   .address(message.address)               
                   .text("Hello"");        
             bot.send(reply);    
       }
    });