Adding Copilot as a skill for Azure Bot Framework (and publish to Telegram)

Cloudatica
11 min readMay 31, 2024

--

Microsoft is heavily promoting Copilot Studio as the low code/ no code solution for creating intelligent chatbots. It’s a great tool but there are a few scenarios in which you may want to integrate Copilot studio with Azure Bot Framework (the pro code solution for building intelligent bots):

Scenario 1- Publish your bot to Telegram or other channels like that. Copilot Studio doesn’t have a seamless way to let you publish your bot to Telegram and many other channels but Azure Bot Framework makes it super easy. So, by adding Copilot Studio as a skill in your Azure Bot Framework, you can take advantage of low code capabilities of Copilot Studios and easily publish it to channels like Telegram.

Scenario 2- You may already have existing investment in Bots built using Azure Bot Framework. You may want to enhance those Bot using Low Code option but you also don’t want to throw away the entire existing investment and rebuild everything.

In this article, I’ll walk you through the steps to add Copilot Studio as a skill in Azure Bot Framework code and publish that bot to Telegram. This tutorial is like a flip side of the other popular tutorial we published for adding Azure Bot as a skill in Copilot Studio

Advanced warning — this tutorial needs fair bit of development skills. If you need professional development or training help in accomplishing this, reach out to us at hello@cloudatica.com

If you prefer video, here is a video series on the same topic:

High level steps

Following are the high level steps involved in accomplishing this:

  • Step 1: Create a Copilot using Copilot Studio and get the token endpoint for Telegram
  • Step 2: In Azure Bot Framework code, add Copilot as a skill by doing the following:
    (a) use your copilot token endpoint to get a token
    (b) start conversation by using the token obtained in the previous sub-step. Get the conversationId and new token to be used in subsequent message processing from the user
    (c) send the user message to the bot using the conversation id and token (d) get the response back from the bot and process it to show it to the user
  • Step 3: Deploy this Azure Bot as a web service (can be deployed anywhere, not necessarily Azure)
  • Step 4: Create a Telegram bot and get the access token to be used for configuration with Azure Bot Service
  • Step 5: Configure Azure Bot Service to connect to your Azure Bot Framework web service and publish to Telegram channel

Step 1: Create a Copilot using Copilot Studio and get the token endpoint for Telegram

This is the simplest step in this journey :) For this tutorial, we will create a very simple Copilot that gives you order status. So, we will create a topic with a few trigger phrases like “Order Details”, “Order Status”, “Order” and create a very silly logic like below (all it does it asks for your order id. if your order id is 10, it says order is shipped, else it says order will be shipped in 2 days. In real life, you’ll have it more sophisticated like calling an API or Power Automate to get actual order status but I implemented this silly logic just to quickly prototype Copilot Studio to Azure Bot connection):

Copilot Studio simple topic for getting order status

Once you have this topic created, go to publish this bot to a channel. Select the channel “Telegram” and you’ll see something like below. Copy this token endpoint. You’ll need this later in your Azure Bot Framework code.

Step 2: In Azure Bot Framework code, add Copilot as a skill

All the following code snippets are shown in nodejs. Same concepts can be applied using C# as well. Full code of this tutorial is available to our premier subscribers. If you are interested, reach out to us at hello@cloudatica.com. Even if you are not a premier subscriber, following code explanations and snippets should give you a decent idea to do it yourself.

In this code, I created a class CopilotSkills that handles most of the subsets involved in this process — getting token, starting conversation, and getting the response back from the bot. Let’s look at these substeps in detail:

(a) Use your copilot token endpoint to get a token

Following method takes the token endpoint as input and it calls this token endpoint to get the token. This token endpoint is the same one that you got from Copilot Studio in your previous step.

async fetchToken(TokenEndPoint) {
const response = await axios.get(TokenEndPoint);
console.log('Name:', this.name);
console.log('Token:', (response.data.token.length), ' ', response.data.token);
this.conversationId = response.data.conversationId;
this.token = response.data.token;
// ...

(b) start conversation by using the token obtained in the previous sub-step. Get the conversationId and new token to be used in subsequent message processing from the user

In the fetchToken method above, add a code to start the conversation. You have to pass the token you received in the previous sub-step. You’ll get a conversationId and a new token in the response. Save that as properties of the class to use in the next sub-steps (sending message and receiving response)

console.log('Token:', Bearer ${this.token});
let config = {
method: 'post',
maxBodyLength: Infinity,
url: 'https://directline.botframework.com/v3/directline/conversations',
headers: {
//this token is the token you got in the previous substep
Authorization: Bearer ${this.token}
}
};

// After getting initial token, makign the call to start the conversation and get the new token, streamurl for websockets, and conversationId
var response = await axios.request(config);
this.conversationId = response.data.conversationId;
this.token = response.data.token;

Now that you have necessary token and the conversationId, you are ready to send the user message to your Copilot Studio Bot and get the response back — next 2 subsets:

(c) send the user message to the bot using the conversation id and token

Write a method getBotResponse to send message and get response. This method is first checking if the token exists (i.e. it was received from previous substeps.) If the token doesn’t exist, it first makes the call to get the token and conversationId.

If token exists, this method makes the API call to send the user message. This message is the message that user typed in the bot. Below this code snippet, there is another code snippet shows how this method is called from the main Bot class

async getBotResponse(message) {
try {

if (!(this.token)) {
await this.fetchToken('<Token Endpoint of Copilot Studio- the one you got in first step from Copilot Studio>')
}

//await this.myInstance.fetchToken('https://default6989c022c3574fe9aae768a63f4db3.2c.environment.api.powerplatform.com/powervirtualagents/botsbyschema/cr74d_copilot2/directline/token?api-version=2022-03-01-preview');

let data = JSON.stringify({
"locale": "en-EN",
"type": "message",
"text": message
});

//send the activity including the message sent by the user using the conversation id that was received when the conversation was started and the token received from the same call
let config = {
method: 'post',
maxBodyLength: Infinity,
url: https://directline.botframework.com/v3/directline/conversations/${this.conversationId}/activities,
headers: {
Authorization: Bearer ${ this.token },
'Content-Type': 'application/json'
},
data: data
};

let response = await axios.request(config);
//save this userMessageId to use later to make sure the responses are filtered for the right user message
this.userMessageId = response.data.id;

//get the bot response for this conversataion id. There is a one second sleep implemented to wait for the bot to respond
//see code snippet of next sub step
//.....
// ....
}

Following is the code snippet from main Bot class to call this method (getBotResponse):

class MyBot extends ActivityHandler {
constructor() {
super();
this.myCopilotSkill = new CopilotSkillClass('welcome!');
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
this.onMessage(async (context, next) => {
console.log('Message recevied.', context);

let botResponse = '';
try {
botResponse = await this.myCopilotSkill.getBotResponse(context.activity.text);
} catch (error) {
console.error('Failed to retrieve token:', error);
}

// add code to filter and display the response to the user - see the code snippet of next section
// .....
// .....
});

(d) get the response back from the bot and process it to show it to the user

async getBotResponse(message) {
try {

// code of previous sub step - to send the user message
//....

//get the bot response for this conversataion id. There is a one second sleep implemented to wait for the bot to respond

config = {
method: 'get',
maxBodyLength: Infinity,
url: https://directline.botframework.com/v3/directline/conversations/${this.conversationId}/activities,
headers: {
Authorization: Bearer ${ this.token },
'Content-Type': 'application/json'
},
data: data
};


//sleep for a second to ensure all the responses are provided from Copilot
await new Promise(r => setTimeout(r, 1000));

response2 = await axios.request(config);

return response.data.activities;

} catch (error) {
console.error('Error getting bot response:', error);
}
}

}

Now that you have the response back from the Copilot Studio Bot, process it in the main Bot class to filter for the ones corresponding to this user’s message id and show it to the user.

class MyBot extends ActivityHandler {
constructor() {
// ...
//code to send the message to Copilot Studio Bot and get the response

//following code is for processing, filtering the response and showing it to the user
let replyText = '';
//slicing first one to filter out the original user message
botResponse.slice(1).forEach(item => {
if (item.replyToId == this.myCopilotSkill.userMessageId)
{
replyText += item.text + '\n';
};
});

await context.sendActivity(MessageFactory.text(replyText, replyText));

// By calling next() you ensure that the next BotHandler is run.
await next();
});

Now, your bot code is ready. Next step is deployment to Azure. For that you’ll need to set some environment values also so that Azure Bot Service can talk to your bot.

MicrosoftAppType=MutiTenant
MicrosoftAppId=<app id of your registed azure app>
MicrosoftAppPassword=<password of your registered application in azure>
MicrosoftAppTenantId=<tenant id of your azure bot service>

For getting above values like App Id, password, and tenant id, go to Azure, register an application, and note down above information. In this tutorial, I am not going into the detail of this step because this is a very well know step needed for many requirements. I covered this step in detail in my other tutorial for custom connector for PowerApps (you can skip to this step for the details).

Step 3: Deploy this Azure Bot as a web service (can be deployed anywhere, not necessarily Azure)

Now that your Azure Bot Framework code is ready, you have to make it accessible from internet so that Azure Bot Service and Telegram can talk to it. Deploying and managing this Azure Bot Framework is like deploying and managing any web service. You’ll see many articles in the internet on how to deploy this using Azure Web Service, but you don’t have to follow that method. You can very well use any cloud service or even your own VM to deploy this. Just make sure that after deployment, https://<your host name/ ip address>/api/messages is accessible from internet.

Note- While you can start small for deploying this web service, as your usage scales, you have to make sure your web service resources scale to continue providing great user experience to your users. That needs good attention to monitoring and managing your web service. You can either do it yourself or take advantage of a fully managed service by Cloudatica to host, monitor, and manage this web service. If you don’t want to spend your energy and resources in deploying, monitoring, and managing this web service, please reach out to us at hello@cloudatica.com. We’ll take care of this headache and free up your time and resource for more higher priority things.

For this tutorial, we will skip the detailed steps of deploying this service in a robust way. For testing purposes, we will use ngrok to make our bot accessible even when it’s running in your local computer as localhost (like we did in our previous tutorial on adding Azure Bot Framework as a skill in Copilot Studio). If you are unfamiliar with ngrok, you can learn more about this ngrok utility in this article.

So, just do `npm start` for your nodejs project, your bot web service should start at localhost and map this to ngrok to make it accessible from the internet.

Step 4: Create a Telegram bot and get the access token to be used for configuration with Azure Bot Service

For telegram bot, go to Telegram app on your phone or computer and search for BotFather. BotFather is the Telegram Bot that will enable you to create and manage your Telegram bots. Your BotFather will start with a message like below:

As you can see, /newbot will allow you to create a bot. You have a give your bot a unique name that ends with “bot”.

Once you create a bot, you’ll get a message giving you the token to access the API. You’ll need this token to configure telegram with Azure Bot Service.

At the end of of this step, you have your Azure Bot deployed as a web service and the Telegram bot created and you have an access token for your Telegram bot. Last step is to hook it all up with an Azure Bot service to make this work end to end (next step in this tutorial).

Step 5: Configure Azure Bot Service to connect to your Azure Bot Faramwork web service and publish to Telegram channel

Now, all you need to do is go to your Azure Bot Service in Azure and configure that bot service to the url of your deployed web service of Azure Bot Framework (Step 3 above) and then publish to Telegram.

For my case, I got an nrok url for my web service, so I used that to configure as shown below:

Just in case, you don’t know what is Azure Bot Service, just search for Azure Bot Service in Azure console and you’ll see a service. Clicking on that service will take you to a screen like below:

Once do this configuration, you should be able to test this bot and integration using “Test in Web Chat feature”.

Now, the last step is to just go to Channels menu in the left nav of your Bot Service, select Telegram, and give the access code that BotFather gave you in Step 4.

And that’s it. You are done. Now, go to your Telegram bot and it should work like a charm. Below is the screenshot of how it worked for me in Telegram:

Phew! That was a lot. This is one of the most complex tutorial I wrote. Microsoft sample code and documentation are really bad for these needs and many of these things are deprecated too. Telegram and other channels like these are very common scenario and Copilot Studio doesn’t support it out of box (as of the writing of this article). So I spent a lot of time in figuring out the right steps for this.

Hope this tutorial helps you. Let me know your questions/ thoughts as comments below.

Please reach out to us at hello@cloudatica.com if you need help with any of the following:

  • Access to full code for this tutorial by being a premium subscriber
  • Getting this implemented in your organization or for your customer
  • Fully managed Azure Bot Framework web service — including Deployment, monitoring, and ongoing management
  • Trainings on topics like these

--

--

Cloudatica

Team of experts in Microsoft AI Stack (Copilot) and Power Platform. For consulting or training, reach out to us at hello@cloudatica.com