Properly format a message for slack in bot framework - c#

I'd like to send a message with buttons to the slack channel. I'm using the bot framework (c#). I want to use the "blocks" (attachments are deprecated according to the slack api docs). So I composed a sample message in the slack "Bot Kit Builder":
The json for it looks like this:
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red",
"emoji": true
},
"value": "red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue",
"emoji": true
},
"value": "blue"
}
]
}
]
As I understand, I have to provide this content in the ChannelData property of the message I sent to the channel:
if (turnContext.Activity.ChannelId == Channels.Slack)
{
message = turnContext.Activity.CreateReply();
message.ChannelData = ChannelDataBuilder.Create("Which pill do you want to take?", "Red", "Blue");
}
The code of the ChannelDataBuilder looks like this:
public static dynamic Create(string text, params string[] choices)
{
var blocks = new List<Block> { new Section { Text = new Text { TextValue = text } } };
var elements = choices.Select(
c => new Button { Text = new Text { TextValue = c, Type = "plain_text" }, Value = c });
blocks.Add(new Actions { Elements = elements.ToArray() });
return JArray.FromObject(blocks, new JsonSerializer { NullValueHandling = NullValueHandling.Ignore });
}
The resulting json of this method looks like this:
{[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red"
},
"action_id": "9e8ea9fb9267484a9f02b1837f716f69",
"value": "Red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue"
},
"action_id": "34c3d9509fc04e2ea37ed54a70b78486",
"value": "Blue"
}
]
}
]}
So, basically I wonder how I should generate this array of json object using c#. Currently the array is still surrounded by the curly brackets (the list object), but I guess I have to provide an array of json objects.
I've already tried using the JsonConvert class and setting the ChannelData as string. But then nothing appears in the slack channel.

The channelData property allows you to pass a complete Slack message, but your are missing the required top-level properties.
If you want to include blocks, than those have to be defined under the blocks property.
So your JSON need to look more like this (not include the channelData property):
{
"blocks":
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red"
},
"action_id": "9e8ea9fb9267484a9f02b1837f716f69",
"value": "Red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue"
},
"action_id": "34c3d9509fc04e2ea37ed54a70b78486",
"value": "Blue"
}
]
}
]
}
See here for the relevant documentation for the botframework.
And here you can see how a message payload for Slack is defined.
Update
As mentioned by #mdrichardson the botframework currently does not support blocks. (See this issue on their github)
So while syntactically correct this solutions does currently not work.
Until botframework supports blocks I would suggest to use secondary attachments.

Related

Message card rendering issue in bot framework v3

I have a message card payload pushed from service now which is not rendering properly on MS Teams through bot framework. It only displays the content but not the button.Below is the payload, pls suggest what could be the issue.
BotFramework:V3
.Net SDK
Bot Builder package:3.12.2.4
Bot Connector:3.12.2.4
Bot.Connector.Teams:0.9.0
{
"contentType": "application/vnd.microsoft.teams.card.o365connector",
"content": {
"#type": "MessageCard",
"#context": "http://schema.org/extensions",
"title": "Incident Updated - INC0010010",
"summary": "Incident Updated - INC0010010",
"themeColor": "D1222B",
"sections": [
{
"title": "",
"text": "",
"activityTitle": "",
"activitySubtitle": "",
"activityText": "",
"facts": [
{
"name": "Category",
"value": "Software"
},
{
"name": "State",
"value": "New"
},
{
"name": "Priority",
"value": "5 - Planning"
},
{
"name": "Assignment group",
"value": "Software"
}
]
},
{
"text": "Please check the details"
}
],
"potentialAction": [
{
"#type": "OpenUri",
"name": "View Incident in ServiceNow",
"targets": [
{
"os": "default",
"uri": "https://dev62584.service-now.com/incident.do?sys_id=XXXXXXX&sysparm_stack=incident_list.do?sysparm_query=active=true"
},
{
"os": "iOS",
"uri": "https://dev62584.service-now.com/incident.do?sys_id=XXXXXXX&sysparm_stack=incident_list.do?sysparm_query=active=true"
},
{
"os": "android",
"uri": "https://dev62584.service-now.com/incident.do?sys_id=XXXXXXX&sysparm_stack=incident_list.do?sysparm_query=active=true"
},
{
"os": "windows",
"uri": "https://dev62584.service-now.com/incident.do?sys_id=7ec9865adb711010fcff8809489619b4&sysparm_stack=incident_list.do?sysparm_query=active=true"
}
]
}
]
}
}
Microsoft Troubleshooting and recommendation:
We were finally able to repro this issue. It is a bug in the team's scope and doesn't repro in the personal scope. An easy way to unblock you would be to add in the potentialAction section an #id field like so:
"potentialAction": [
{
"#id":"1",
"#type": "OpenUri",
Nothing stands out as a bug in your code or problem with your configuration. Linking to the GitHub issue where it is being investigated/troubleshot:
https://github.com/microsoft/BotBuilder-V3/issues/208

Waterfall model with adaptive cards in v4.0

I'm trying to create a water fall model with the help of Adaptive cards in C# version 4.0.
My scenario is like following:
On loading bot following cards will be shown:
1. SharePoint
2. Azure
3. O365
Once I click on any of them then new set of cards will be shown like:
On selecting "SharePoint" following cards will be shown:
1. Create a Site
2. Create a sub site.
and on selecting any of the above choices a form is called with set of questions like:
1. what is site title
2. site owner
and so on..
UI as shown below:
I tried creating the structure on https://adaptivecards.io/, but couldnt find any relevant code related to this type of scenario.
If any one has done it before please share the documentation or code snippet.
Thanks
Here's a basic card with Input.ChoiceSet:
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "Input.ChoiceSet",
"id": "first",
"placeholder": "Placeholder text",
"choices": [
{
"title": "SharePoint",
"value": "SharePoint"
},
{
"title": "Azure",
"value": "Azure"
},
{
"title": "O365",
"value": "O365"
}
],
"style": "expanded"
}
]
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
Then, in your bot, follow these answers for dealing with User Input:
Using Adaptive Cards in Waterfall Dialogs
(optional) Dynamically Changing The Card Before Sending
Basically, you'll want to:
Send the card
Capture the card's input by sending a blank text prompt right after sending the card (as explained in the first link)
Use if or switch statements in the next step to determine which card to display next based off of the user's input. You could optionally create the card more dynamically using the second link
The AdaptiveCards Designer is pretty good, but it lacks the ability to set the actions property. You can do so manually, by adding (like I did above):
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
],
Using Images
If you'd like to use images instead of a ChoiceSet, you can do something like this:
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"items": [
{
"type": "Image",
"id": "choice1",
"selectAction": {
"type": "Action.Submit",
"title": "choice1",
"data": {
"choice": 1
}
},
"url": "https://acuvate.com/wp-content/uploads/2017/02/Microsoft-Botframework.fw_-thegem-person.png",
"altText": ""
},
{
"type": "Image",
"id": "choice2",
"selectAction": {
"type": "Action.Submit",
"title": "choice2",
"data": {
"choice": 2
}
},
"url": "https://acuvate.com/wp-content/uploads/2017/02/Microsoft-Botframework.fw_-thegem-person.png",
"altText": ""
},
{
"type": "Image",
"id": "choice3",
"selectAction": {
"type": "Action.Submit",
"title": "choice3",
"data": {
"choice": 3
}
},
"url": "https://acuvate.com/wp-content/uploads/2017/02/Microsoft-Botframework.fw_-thegem-person.png",
"altText": ""
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
The important part is making sure that the Action.Submit:
Is on the image
Has data that you would use to capture which choice the user selected

Autodesk Forge: Download checklist attachment

When I retrieve a checklist instance, I got the following section related to attachment.
{
"type": "instance_item_attachments",
"id": "5a0a2acf-b02a-4b88-86cc-962c3831bdee",
"attributes": {
"name": "6856ad10-6ab0-11e9-9150-9fda3da0626e.png",
"attachmentType": "OSS",
"mimeType": "image/png",
"uploadStatus": "COMPLETED",
"urns": [
{
"urn": "urn:adsk.wipprod:fs.file:vf.gy4mB910SneymU86Gc4O0A?version=1",
"type": "WIP"
},
{
"urn": "urn:adsk.objects:os.object:wip.dm.prod/ede3de59-1b68-485c-82fe-f1f2af3442fe.png",
"type": "OSS"
},
{
"urn": "urn:adsk.checklists.cs.attachment:58b8afcf-d7cd-49ad-aa10-78c50610761b/5a0a2acf-b02a-4b88-86cc-962c3831bdee",
"type": "CHECKLIST"
}
],
"createdAt": "2019-04-29T18:55:51.334Z",
"updatedAt": "2019-04-29T18:55:54.137Z",
"createdBy": "TAKCJQU6HGXW",
"modifiedBy": "TAKCJQU6HGXW",
"permittedActions": [
"canArchive",
"canEdit"
],
"permittedAttributes": [
"mimeType",
"uploadStatus"
]
},
"links": {
"self": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee"
},
"relationships": {
"container": {
"meta": {
"relation": "primary",
"readOnly": false
},
"links": {
"self": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/relationships/container",
"related": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/container"
},
"data": {
"type": "containers",
"id": "58b8afcf-d7cd-49ad-aa10-78c50610761b"
}
},
"item": {
"meta": {
"relation": "primary",
"readOnly": false
},
"links": {
"self": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/relationships/item",
"related": "/containers/58b8afcf-d7cd-49ad-aa10-78c50610761b/instance_item_attachments/5a0a2acf-b02a-4b88-86cc-962c3831bdee/item"
},
"data": null
}
}
}
Now, I want to download this attachment, the provided URN is: wip.dm.prod/ede3de59-1b68-485c-82fe-f1f2af3442fe.png
If I try to access it using the following link, it says not found
developer.api.autodesk.com/oss/v2/buckets/wip.dm.prod/b30e3ffe-333b-446c-b834-e2f2141096b4.png
However, if I changed the URL a bit (by adding objects), it works fine.
developer.api.autodesk.com/oss/v2/buckets/wip.dm.prod/objects/b30e3ffe-333b-446c-b834-e2f2141096b4.png
Am I doing something wrong here? or this is a bug in the provided urn?
Adding to Adam Nagy reply, you would need to break the URN. From your original question:
urn:adsk.objects:os.object:wip.dm.prod/ede3de59-1b68-485c-82fe-f1f2af3442fe.png
In .NET you can try (using System.Linq):
string bucketKey = urn.Split("/").First().Split(":").Last();
string objectName = urn.Split("/").Last();
Then rebuild as:
string attachemtnUrl = string.Format("{0}/oss/v2/buckets/{1}/objects/{2}", BASE_URL, bucketKey, objectName);
And you'll also need the Authorization header with a valid access token.
The id / urn of an object in OSS (Object Storage Service) contains the bucket name and object name after the "urn:adsk.objects:os.object:" section.
There is a tutorial on downloading a file https://forge.autodesk.com/en/docs/data/v2/tutorials/download-file/
It shows that the reply concerning an item contains both the id and the actual URL of the download link under the storage section:
"storage": {
"data": {
"type": "objects",
"id": "urn:adsk.objects:os.object:wip.dm.prod/977d69b1-43e7-40fa-8ece-6ec4602892f3.rvt"
},
"meta": {
"link": {
"href": "https://developer.api.autodesk.com/oss/v2/buckets/wip.dm.prod/objects/977d69b1-43e7-40fa-8ece-6ec4602892f3.rvt"
}
}
}
There you can see the connection between the id and the URL you can use to download the file

Create DropDown list for jira custom field in REST API

I'm trying to retrieve the available options for fields through the Jira REST API but I'm getting stuck on the custom fields.
A normal field returns something like this:
{[priority, {
"required": false,
"schema": {
"type": "priority",
"system": "priority"
},
"name": "Priority",
"key": "priority",
"hasDefaultValue": true,
"operations": [
"set"
],
"allowedValues": [
{
"self": "https://inn-inw.atlassian.net/rest/api/2/priority/1",
"iconUrl": "https://inn-inw.atlassian.net/images/icons/priorities/highest.svg",
"name": "Highest",
"id": "1"
},
{
"self": "https://inn-inw.atlassian.net/rest/api/2/priority/2",
"iconUrl": "https://inn-inw.atlassian.net/images/icons/priorities/high.svg",
"name": "High",
"id": "2"
},
{
"self": "https://inn-inw.atlassian.net/rest/api/2/priority/3",
"iconUrl": "https://inn-inw.atlassian.net/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
{
"self": "https://inn-inw.atlassian.net/rest/api/2/priority/4",
"iconUrl": "https://inn-inw.atlassian.net/images/icons/priorities/low.svg",
"name": "Low",
"id": "4"
},
{
"self": "https://inn-inw.atlassian.net/rest/api/2/priority/5",
"iconUrl": "https://inn-inw.atlassian.net/images/icons/priorities/lowest.svg",
"name": "Lowest",
"id": "5"
}
],
"defaultValue": {
"self": "https://inn-inw.atlassian.net/rest/api/2/priority/3",
"iconUrl": "https://inn-inw.atlassian.net/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
}
}]}
The custom fields on the other hand return a much "smaller" set!
"Sprint" Custom Field:
{[customfield_10113, {
"required": false,
"schema": {
"type": "array",
"items": "string",
"custom": "com.pyxis.greenhopper.jira:gh-sprint",
"customId": 10113
},
"name": "Sprint",
"key": "customfield_10113",
"hasDefaultValue": false,
"operations": [
"set"
]
}]}
"Epic Link"
{[customfield_10006, {
"required": false,
"schema": {
"type": "any",
"custom": "com.pyxis.greenhopper.jira:gh-epic-link",
"customId": 10006
},
"name": "Epic Link",
"key": "customfield_10006",
"hasDefaultValue": false,
"operations": [
"set"
]
}]}
As you can see there are no options available, but i know that there are.
How can I get to these values through rest?
I have tried checking the request that the cloud server makes but it's pretty different for each of the custom fields and i cannot find a relation between them. For example:
To get the sprint possible values: https://inn-inw.atlassian.net/rest/greenhopper/1.0/sprint/picker?query=
and to get the Epic values: https://inn-inw.atlassian.net/rest/greenhopper/1.0/epics
I'm trying to make things as clean as possible and I would prefer not to hard code the request, specially because i would probably need to update the code every time a new custom field is created.
PS: I have tried some of the solutions presented in other questions but couldn't find one that fits what i need. eg: https://inn-inw.atlassian.net//rest/api/2/issue/createmeta?projectKeys=PROJKEY&issuetypeNames=Bug&expand=projects.issuetypes.fields returns only the short description that is displayed above.

Deserializing Chrome Bookmark JSON Data in C#

In response to a question I asked a few days ago, I'm attempting to stretch myself a little, and do something that I've not really focussed on much before. I've done some searching (both here, and in general), but can't find the answers (or even reasonable hints) to what I want to achieve (though, a few things come close-ish).
Basically, I'm trying to deserialize the data for the Google Chrome bookmarks file using the Json.NET library (though, if there's a better alternative, I'm all for that - the documentation for this library is a little confusing in places). I'm a little confused as to the next step to take, due primarily to being used to PHP's fantastic handling of JSON data (using json_decode()), allowing for a single function call, and then simple associative-array access.
The library (Json.NET) wants me to specify an Object type that it can deserialize the JSON data into, but I'm not really sure how to go about structuring such an Object, given the format of the Bookmarks file itself. The format is something along the lines of:
{
"roots": {
"bookmark_bar": {
"children": [ {
"children": [ {
"date_added": "12880758517186875",
"name": "Example URL",
"type": "url",
"url": "http://example.com"
}, {
"date_added": "12880290253039500",
"name": "Another URL",
"type": "url",
"url": "http://example.org"
} ],
"date_added": "12880772259603750",
"date_modified": "12880772452901500",
"name": "Sample Folder",
"type": "folder"
}, {
"date_added": "12880823826333250",
"name": "Json.NET",
"type": "url",
"url": "http://james.newtonking.com/pages/json-net.aspx";
} ],
"date_added": "0",
"date_modified": "12880823831234250",
"name": "Bookmarks bar",
"type": "folder"
},
"other": {
"children": [ ],
"date_added": "0",
"date_modified": "0",
"name": "Other bookmarks",
"type": "folder"
}
},
"version": 1
}
Now, in PHP, I'd be far more used to doing something along the lines of the following, to get the data I wanted, and ending up with 'Json.NET':
$data['roots']['bookmark_bar']['children'][0]['name'];
I can work out, simply enough, what objects to create to represent the data (something like a root object, then a bookmark list object, and finally an individual bookmark object) - but I'm really not sure as to how to implement them, and then get the library to deserialize into the relevant objects correctly.
Any advice that can be offered would be greatly appreciated.
It is not necessary to declare a type that reflects the json structure:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System;
class Program
{
static void Main(string[] args)
{
string json =
#"
{
""roots"": {
""bookmark_bar"": {
""children"": [ {
""children"": [ {
""date_added"": ""12880758517186875"",
""name"": ""Example URL"",
""type"": ""url"",
""url"": ""http://example.com""
}, {
""date_added"": ""12880290253039500"",
""name"": ""Another URL"",
""type"": ""url"",
""url"": ""http://example.org""
} ],
""date_added"": ""12880772259603750"",
""date_modified"": ""12880772452901500"",
""name"": ""Sample Folder"",
""type"": ""folder""
}, {
""date_added"": ""12880823826333250"",
""name"": ""Json.NET"",
""type"": ""url"",
""url"": ""http://james.newtonking.com/pages/json-net.aspx""
} ],
""date_added"": ""0"",
""date_modified"": ""12880823831234250"",
""name"": ""Bookmarks bar"",
""type"": ""folder""
},
""other"": {
""children"": [ ],
""date_added"": ""0"",
""date_modified"": ""0"",
""name"": ""Other bookmarks"",
""type"": ""folder""
}
},
""version"": 1
}
";
using (StringReader reader = new StringReader(json))
using (JsonReader jsonReader = new JsonTextReader(reader))
{
JsonSerializer serializer = new JsonSerializer();
var o = (JToken)serializer.Deserialize(jsonReader);
var date_added = o["roots"]["bookmark_bar"]["children"][0]["date_added"];
Console.WriteLine(date_added);
}
}

Categories

Resources