Adaptive card on Select Item event of Search Message Extension - botframework

I am trying to return an adaptive card when search result is selected in Search Message extension. Below is my class to return the card:
import { IEntity } from '../model/IEntity';
import { Attachment, CardFactory } from "botbuilder";
export class AccountResultCard {
public static getCard(account: IEntity): Attachment {
// Get map
let logo = `https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg`;
let url = `https://pbs.twimg.com/docs/test.aspx?GoPage=AccountCard.aspx?AccountID='${account.ID}'&_Division_='${account.Division}'`;
const accountDetail = account.Code ? account.Code + ' - ' + account.Description : '';
const accountcontextinfo1 = account.ContextInfo[1] ? account.ContextInfo[1] : '';
const accountcontextinfo2 = account.ContextInfo[3] ? account.ContextInfo[3] + ' '+ account.ContextInfo[2] : '';
const card = CardFactory.adaptiveCard(
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"text": "Title"
},
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": accountDetail
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": accountcontextinfo1,
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"text": accountcontextinfo2,
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"text": "Missing state and country",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "Image",
"style": "Person",
"url": logo,
"size": "Small"
}
],
"width": "auto"
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Open in Aurora",
"url": url,
"width": "Small",
"style": "default"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2"
}
);
return card;
}
}
and below is my select item event:
public async handleTeamsMessagingExtensionSelectItem(
context: TurnContext,
obj: any
): Promise<any> {
const item = this.response.find(e => e.ID == obj.name);
return {
composeExtension: {
type: "result",
attachmentLayout: "list",
attachments: [AccountResultCard.getCard(item)],
},
};
}
But it never return anything. I deleted everything from get card and sent just a one single text block with hard coded value in body. That also didn't work.Can you guys suggest me where I am doing wrong.

I was able to send the adaptive card in SelectItem event by changing the code to :
return {
composeExtension: {
type: "result",
attachmentLayout: "list",
attachments: [{preview: CardFactory.heroCard("title", ""),
...yourAdaptiveCard}],
},
};
This has been answered here.

Related

Incoming Webhook for Microsoft Teams - Add to Planner (Task) Tab

Is there any way to create a new task within a Planner (Task) tab within a channel by using webhooks?
Either directly or having a button on the incoming message that then creates the task once the end user presses it?
I'm new to webhooks but having a look on Google, Power Automate is a way of doing it but not sure we have access to that.
I think I went down the wrong rabbit hole of task modules as I want to use what's already configured within the Planner tab and not create something bespoke. The button also doesn't work, comes up with an error message saying "This card action is disabled because it is not supported for Connectors". But this is what I have at the moment:
{
"type":"message",
"attachments":[
{
"contentType":"application/vnd.microsoft.card.adaptive",
"contentUrl":null,
"content":{
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"type":"AdaptiveCard",
"version":"1.2",
"body":[
{
"type": "Container",
"id": "066de76b-17da-e6f7-8ab3-81d36d8a6162",
"padding": "None",
"items": [
{
"type": "FactSet",
"id": "10eebecd-851c-2080-24f4-9b6044821d90",
"facts": [
{
"title": "**ID**",
"value": "EXAMPLE ID"
},
{
"title": "**Name**",
"value": "EXAMPLE NAME"
}
]
}
]
},
{
"type": "Container",
"id": "879303cd-81de-618a-8e99-e66be9f22ac1",
"padding": "None",
"items": [
{
"type": "TextBlock",
"id": "0833d313-657b-b587-d4d3-f4a961e6434a",
"text": "EXAMPLE ERROR MESSAGE.",
"wrap": true
},
{
"type": "Container",
"id": "de1d9a46-b8d4-443a-5afc-8843c445cf7b",
"padding": "None",
"items": [
{
"type": "ActionSet",
"id": "3b0c8763-dba8-3d88-98f5-7189ced1fe60",
"actions": [
{
"type": "Action.Submit",
"id": "btnCreateTask",
"title": "Create Task",
"card": {
"type": "AdaptiveCard",
"padding": "None",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
]
}
]
}
]
}
]
}
}
]
}
Thanks in advance for any help/suggestions

How to extract content/text of an attachment from an MS Teams messaged generated by a webhook?

Automation #1 is ServiceNow > webhook > MS Teams > message.
Automation #2 is Power Automate > When keywords are mention (trigger) > read message from automation #1 > Post message in chat or channel
Problem: I am unable to workout the expression needed to read the content from the message body marked as the 2nd of the 3 blocks below i.e. "content":
"body": {
"contentType": "html",
"content": "<attachment id=\"x\"></attachment>",
"plainTextContent": ""
},
"channelIdentity": {
"teamId": "x",
"channelId": "x"
},
"attachments": [
{
"id": "x",
"contentType": "application/vnd.microsoft.teams.card.o365connector",
"contentUrl": null,
"content": "{\r\n \"summary\": \"P1/2 incident next action overdue [xx_NAO_P1-2]\",\r\n \"title\": \"P1/2 incident next action overdue [xx_NAO_P1-2]\",\r\n \"themeColor\": \"ff0000\",\r\n \"sections\": [\r\n {\r\n \"text\": \"Attn: <strong>name_here</strong> (email_here) \\n</br></br>\\nThe next action for case <strong>case_number</strong> is <span style=\\\"color:red\\\"><strong>over due</strong></span>. The stated action was:\\n</br></br>\\n<span style=\\\"color:blue\\\">This is the next action for case_number.</span>\",\r\n \"title\": \"\",\r\n \"activityTitle\": \"\",\r\n \"activitySubtitle\": \"\",\r\n \"activityText\": \"\",\r\n \"markdown\": false,\r\n \"startGroup\": false\r\n },\r\n {\r\n \"markdown\": true,\r\n \"startGroup\": false\r\n }\r\n ],\r\n \"potentialAction\": []\r\n}",
"name": null,
"thumbnailUrl": null
}
],
You can use the Body Content property for this.
Below is an example of this.
Btw, with an expression that would be:
outputs('Get_message_details')?['body/body/content']
Okay, this is my first ever Power Automate flow, so its not pretty. I think "Compose 2" and "Compose 3" are not required. If there are any suggestions on how to eliminate the need for 2x "Parse JSON" steps I am all ears. If there is a way to bypass the JSON steps altogether, even better.
I hope my experience, can help someone else.
Power Automate – Trigger
When keywords are mentioned
Message type: Channel
Keywords to search for: “[my keyword]”
Team: [my channel]
Channel – 1: General
Power Automate – Actions
Initialize variable – msg email
Name: msg_email
Type: String
Value: [empty]
Initialize variable – msg caseNumber
Name: msg_caseNumber
Type: String
Value: [empty]
Initialize variable – msg nextAction
Name: msg_nextAction
Type: String
Value: [empty]
Apply to each
Select an output from previous steps: Value = triggerOutputs()?[‘body/value’]
Get message details
Message: Message ID = items(‘Apply_to_each’)?[messageId’]
Message type: Channel
Team: Team ID = items(‘Apply_to_each’)?[‘teamId’]
Channel: Channel ID = items(‘Apply_to_each’)?[‘channelId’]
Parent message ID = [empty]
Parse JSON
Content: Body = body(‘Get_message_details’)
Schema: [I took the output of the message and generated a schema]
{
"type": "object",
"properties": {
"##odata.context": {
"type": "string"
},
"id": {
"type": "string"
},
"replyToId": {},
"etag": {
"type": "string"
},
"messageType": {
"type": "string"
},
"createdDateTime": {
"type": "string"
},
"lastModifiedDateTime": {
"type": "string"
},
"lastEditedDateTime": {},
"deletedDateTime": {},
"subject": {},
"summary": {
"type": "string"
},
"chatId": {},
"importance": {
"type": "string"
},
"locale": {
"type": "string"
},
"webUrl": {
"type": "string"
},
"policyViolation": {},
"eventDetail": {},
"from": {
"type": "object",
"properties": {
"device": {},
"user": {},
"application": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"displayName": {
"type": "string"
},
"applicationIdentityType": {
"type": "string"
}
}
}
}
},
"body": {
"type": "object",
"properties": {
"contentType": {
"type": "string"
},
"content": {
"type": "string"
},
"plainTextContent": {
"type": "string"
}
}
},
"channelIdentity": {
"type": "object",
"properties": {
"teamId": {
"type": "string"
},
"channelId": {
"type": "string"
}
}
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"contentType": {
"type": "string"
},
"contentUrl": {},
"content": {
"type": "string"
},
"name": {},
"thumbnailUrl": {}
},
"required": [
"id",
"contentType",
"contentUrl",
"content",
"name",
"thumbnailUrl"
]
}
},
"mentions": {
"type": "array"
},
"reactions": {
"type": "array"
},
"messageLink": {
"type": "string"
},
"threadType": {
"type": "string"
},
"teamId": {
"type": "string"
},
"channelId": {
"type": "string"
}
}
}
Apply to each 3
Select an output from previous steps: attachments = body(‘Parse_JSON’)?[‘attachments’]
Compose 2
Inputs: content x = items(‘Apply_to_each_3’)?[‘content’]
Parse JSON 2
Content: Outputs x = outputs(‘Compose_2’)
Schema: [I took the output of the attachment item and generated a schema]
{
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"title": {
"type": "string"
},
"themeColor": {
"type": "string"
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"title": {
"type": "string"
},
"activityTitle": {
"type": "string"
},
"activitySubtitle": {
"type": "string"
},
"activityText": {
"type": "string"
},
"markdown": {
"type": "boolean"
},
"startGroup": {
"type": "boolean"
}
},
"required": [
"markdown",
"startGroup"
]
}
},
"potentialAction": {
"type": "array"
}
}
}
Compose 3
Inputs: bodu(…) x = body(‘Parse_JSON_2’)[‘sections’][0][‘text’]
Note: after Compose 3 there are 3 parallel branches to create 3 variables which then converge back at Get an #mention token for a user.
1. Branch for variable msg_email
Find text position “(“
Text: Outputs x = outputs(‘Compose_3’)
Search Text: (
Substring email
Text: Outputs x = outputs(‘Compose_3’)
Starting Position: add(…) x = add(int(outputs('Find_text_position_"("')?['body']),1)
Length: sub(…) x = sub(sub(int(outputs('Find_text_position_")"')?['body']),int(outputs('Find_text_position_"("')?['body'])),1)
Set variable msg email
Name: msg_email
Value: Body x = body(‘Substring_email’)
2. Branch for variable msg_caseNumber
Find text position “[case prefix]“
Text: Outputs x = outputs(‘Compose_3’)
Search Text: [case prefix]
Substring caseNumber
Text: Outputs x = outputs(‘Compose_3’)
Starting Position: Body x = body(‘Find_text_position_”[case prefix]”’)
Length: 11
Set variable msg caseNumber
Name: msg_caseNumber
Value: Body x = body(‘Substring_caseNumber’)
3. Branch for variable msg_nextAction
Find text position “colour:blue“
Text: Outputs x = outputs(‘Compose_3’)
Search Text: color:blue
Substring nextAction+end tag
Text: Outputs x = outputs(‘Compose_3’)
Starting Position: add(…) x = add(12,int(outputs('Find_text_position_"color:blue"')?['body']))
Length: [empty]
Substring nextAction
Text: Body x = body(‘Substring_nextAction+end_tag’)
Starting Position: 0
Length: sub(…) x = sub(length(outputs('Substring_nextAction+end_tag')?['body']),7)
Set variable msg nextAction
Name: msg_nextAction
Value: Body = body(‘Substring_nextAction’)
Get an #mention token for a user
User: variables(‘msg_email’)
Reply with a message in a channel
Post as: Flow bot
Post in: Channel
Message ID: outputs(‘Get_message_deatils’)?[‘body/id’]
Channel: Channel ID = outputs(‘Get_message_details’)?[‘body/channelId’]
Message:
<p>#{outputs('Get_an_#mention_token_for_a_user')?['body/atMention']} Case #{variables('msg_caseNumber')} has an <span style="color: red"><strong>overdue Next Action</strong></span>. The stated action was:.<br>
<br>
<span style="color: blue"></span><span style="color: blue">#{variables('msg_nextAction')}</span><span style="color: blue"></span></p>

How to make interactive message in slack return answer without replacing

I'm using Dialogflow CX <-> Slack integration in Golang from this repo and I try to change the function "UpdateInteractiveSlackMessgae" so that my chatbot won't replace existing message when I click the button. As you see there is ReplaceOriginal attribute set as false but it still doesn't work.
func (slackReq *SlackRequest) UpdateInteractiveSlackMessage(interactiveCallbackMessage *slack.InteractionCallback, responseMessages []*cx.ResponseMessage) error {
responseStr := utils.ParseStringFromResponse(responseMessages)
blocks, _ := utils.ParsePayloadFromResponse(responseMessages)
webhookMsg := slack.WebhookMessage{
Channel: interactiveCallbackMessage.Channel.ID,
Blocks: &slack.Blocks{BlockSet: blocks},
Text: responseStr,
ReplaceOriginal: false,
DeleteOriginal: false,
}
// if it is an interaction event, then post as separate message if DM
// if channel, post as a reply to the thread
if interactiveCallbackMessage != nil {
if !interactiveCallbackMessage.Channel.IsIM {
webhookMsg.ThreadTimestamp = interactiveCallbackMessage.Container.ThreadTs
}
slack.PostWebhook(interactiveCallbackMessage.ResponseURL, &webhookMsg)
}
return nil
}
And this is my custom payload from Dialogflow CX:
{
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Choose one:",
"emoji": true
}
},
{
"type": "actions",
"block_id": "actionblock789",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Yes"
},
"style": "primary",
"value": "Yes"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "No"
},
"style": "primary",
"value": "No"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": " "
}
}
]
}
Do you know what should I do to make chatbot respond as a new message instead of replace already existing?

How to display images in imageset dynamically from an array object in adaptiev cards?

I have an array object like this:
{
"Items":[
{
"ShortName":"Product short name",
"Image":"https://www.andrew.cmu.edu/user/cfperron/cats/images/cat8.jpg",
"ManufacturerName":"MMM",
"CatalogName":"cats"
},
{
"ShortName":"Product2 short name",
"Image":"https://www.andrew.cmu.edu/user/cfperron/cats/images/cat7.jpg",
"ManufacturerName":"SSS",
"CatalogName":"Dogs"
}
]
}
how to get Images in ImageSet dynamically in adaptive cards? I was able to get rest of the values in FactSet. But stuck with images.
Ok I figured it out.
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"text": "Search Results"
},
{
"type": "Container",
"items": [
{
"type": "ImageSet",
"imageSize": "medium",
"images": [
{
"type": "Image",
"url": "${Image}",
"size": "Medium"
}
]
},
{
"type": "FactSet",
"facts": [
{
"title": "Short Name",
"value": "${ShortName}"
},
{
"title": "Supplier Name",
"value": "${SupplierName} "
},
{
"title": "Price ",
"value": "${PriceAmount} "
}
]
}
],
"$data": "${$root['Items']}"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2"
}
But next issue I am facing is if image url is empty, it throws an error "Adaptive Card Rendered error:
{
"message": "Cannot read property 'style' of null"
}"
How can we give a null check inside adaptive card for Image property?

MS Bot framework: Let user post multiple data

I´m trying to find a way to let a user retrieve, edit and post multiple data in a table. Like this:
A user asks for some entity, for e.g. Person
The MS bot returns a table with all the attributes for that Person (for ex: age, name, address etc)
The user can edit the data directly in the table, press send and the data
stored in the DB.
Is there any support for this? Any guidance is appreciated.
You can use Adaptive Cards to provide the user with a 'Form' to fill out. Not all channels support Adaptive Cards, but the list of supported channels is growing. For more information, please see: https://adaptivecards.io/ and howto: send-an-adaptive-card
Here is an example from MS Teams:
The card can be created using the Adaptive Cards library, or loaded from .json file like this:
string cardPath = Path.Combine(_hostingEnvironment.WebRootPath, "testcard.json");
string cardText = File.ReadAllText(cardPath);
var card = AdaptiveCards.AdaptiveCard.FromJson(cardText);
var reply = turnContext.Activity.CreateReply("card");
reply.Attachments.Add(new Attachment()
{
Content = card.Card,
ContentType = AdaptiveCards.AdaptiveCard.ContentType
});
await turnContext.SendActivityAsync(reply);
Note: when the user submits the card, the activity.Value will contain a .json string of values:
Example Card .json:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "Tell us about yourself...",
"weight": "bolder",
"size": "large"
},
{
"type": "TextBlock",
"text": "We just need a few more details to get you booked for the trip of a lifetime!",
"isSubtle": true,
"wrap": true
},
{
"type": "TextBlock",
"text": "Don't worry, we'll never share or sell your information.",
"isSubtle": true,
"wrap": true,
"size": "small"
},
{
"type": "TextBlock",
"text": "Your name",
"wrap": true
},
{
"type": "Input.Text",
"id": "firstlast",
"placeholder": "Last, First",
"style": "text",
"separataion": "none"
},
{
"type": "TextBlock",
"text": "Your email",
"wrap": true
},
{
"type": "Input.Text",
"id": "email",
"placeholder": "youremail#example.com",
"style": "email",
"separataion": "none"
},
{
"type": "TextBlock",
"text": "Phone Number"
},
{
"type": "Input.Text",
"id": "phone",
"placeholder": "xxx.xxx.xxxx",
"style": "tel"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Send",
"data": { "personalInfo": "" }
}
]
}

Resources