With this code:
var button = Value.ForStruct(new Struct{
Fields={
["type"] = Value.ForString("postback"),
["title"] = Value.ForString("Call Representative"),
["payload"] = Value.ForString("+15105551234"),
}
});
var inPayload = Value.ForStruct(new Struct{
Fields ={
["buttons"] = Value.ForList(button),
["text"] = Value.ForString("try the postback"),
["template_type"] = Value.ForString("button"),
}
});
var attachment = Value.ForStruct(new Struct{
Fields ={
["payload"] = inPayload,
["type"] = Value.ForString("template"),
}
});
var msg = Value.ForStruct(new Struct{
Fields ={
["attachment"] = attachment,
});
Payload = new Struct{
Fields ={
["facebook"] = msg
}
I was able to create the following json:
"payload": {
"facebook": {"attachment": {
"payload": {
"buttons": [ {
"type": "postback",
"title": "Call Representative",
"payload": "+15105551234"
}],
"text": "try the postback",
"template_type": "button"
},
"type": "template"
}}
Now I need to create the following other format but I dont find how to do it:
"payload": {
"message": "Yes I did it"
"platform": "kommunicate",
"attachment": {
"payload": {
"buttons": [ {
"type": "postback",
"title": "Call Representative",
"payload": "+15105551234"
}],
"text": "try the postback",
"template_type": "button"
},
"type": "template"
}
I really dont find how to eliminate the first "facebook": { element and leave only:
{
"message": "Yes I did it",
"platform": "kommunicate",
"attachment":
And include message and platform at the same level. Here is the complete json I will like to generate:
"payload": {
"platform": "kommunicate",
"message": "Yes I did it",
"attachment": {
"payload": {
"buttons": [ {
"type": "postback",
"title": "Call Representative",
"payload": "+15105551234"
}],
"text": "try the postbackggggggg",
"template_type": "button"
},
"type": "template"
}
If you want to take an object and convert it to json I would recommend taking a look at Newtonsoft Json.Net library. They have plenty of examples that might help you. There is also protobuf.net library for serializing to protobuf instead of json.
Both libraries are used in similar ways, you create a class with appropriate properties and set the values you want. You will need multiple classes for nested types as in your example. Protobuf requires you to annotate the properties with attributes, while this is optional for json.net. You then send the object to the serialization library and get a string or binary data representing your object. This kind of object is often called a Data Transfer Object (DTO), since the only purpose it has is to aid in serialization or/and transfering the data to another system.
Related
I am trying to integrate with salesforces new grpc change data capture event bus. Events are sent to clients via grpc with an avro encoded message of what the changes to the records were, so the client has to decode the message using an avro schema that is provided and unable to be changed.
I am able to easily decode the avro encoded message for objects with fields of two union types but fields with three types throw exceptions.
This is the Name field for the Account avro schema:
{
"name": "Name",
"type": [
"null",
"string",
{
"type": "record",
"name": "Switchable_PersonName",
"fields": [
{
"name": "Salutation",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "FirstName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "LastName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "MiddleName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "InformalName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "Suffix",
"type": [
"null",
"string"
],
"default": null
}
]
}
],
"doc": "Data:Switchable_PersonName",
"default": null
},
As you can see the name can either be null, a string, or an object called Switchable_PersonName.
Using the avrogen cli tool, I was able to convert the avro schema into concrete c# classes of AccountChangeEvent, ChangeEventHeader, ChangeType, Address, Switchable_PersonName.
The Name field was created in the AccountChangeEvent class as:
private object _Name;
This is the method I made for decoding the avro message:
public static void DeserializeAccountConcrete(byte[] payload)
{
var accSchema = Avro.Schema.Parse(File.ReadAllText("./avro/AccountGRPCSchema.avsc"));
var unionSchema = accSchema as Avro.UnionSchema;
var cache = new ClassCache();
cache.LoadClassCache(typeof(AccountChangeEvent), unionSchema);
cache.LoadClassCache(typeof(Switchable_PersonName), unionSchema);
cache.LoadClassCache(typeof(Address), unionSchema);
var reader = new ReflectReader<AccountChangeEvent>(accSchema, accSchema, cache);
using var accStream = new MemoryStream(payload);
accStream.Seek(0, SeekOrigin.Begin);
var accDecoder = new BinaryDecoder(accStream);
var accEvent = reader.Read(accDecoder);
Console.WriteLine(accEvent.Name);
Console.WriteLine("Event " + accEvent.ChangeEventHeader.changeType);
}
This sort of deserialization works for other schemas but it fails for the Account schema with this exception being thrown.
Avro.AvroException: Class for union record type com.sforce.eventbus.Switchable_PersonName is not registered.Create a ClassCache object and call LoadClassCache
Looking at the documentation for avro my implementation looks correct but it seems it is not.
I have changed the field type to
private com.sforce.eventbus.Switchable_PersonName _Name;
and any other code that may rely on this field but the same error is still thrown.
I am new to avro so there may be many things that I do not know or am doing wrong.
I would like to understand how to read/write data with Adaptive cards. I can read the data from a submit action, and reply as text, but not sure how present the input data in the card. First place, I would like to add the shootValue to an array that I can carry trough the lifecycle of the card. Can somebody please let me know how to do this?
The goal of this question is to understand how to keep existing responses from the card.
Like in Battleship, I shoot "A1", type it in an input box, submit, I would like to see "A1" in the card. I add "A2", submit, then I would like to see "A1" and "A2" in the card that is sent to Teams. I understand that I need to rebuild the card from scratch at every shot, that means, I need to either carry on the shots somehow with each action.
Data card:
{
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "Hello {name}"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"id": "",
"items": [
{
"type": "Container",
"items": [
{
"type": "Input.Text",
"placeholder": "Voorbeeld: A1",
"id": "id_shoot",
"$data": "shoot"
}
]
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": " {shoot}",
"horizontalAlignment": "Right",
"id": ""
}
],
"$data": "{shoots}",
"id": "shotcoords"
}
],
"$data": "{shots}"
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "{status}",
"id": ""
}
],
"$data": "{shoots}",
"id": "shotstatuses"
}
],
"id": ""
}
]
},
{
"type": "ActionSet",
"id": "",
"actions": [
{
"type": "Action.Submit",
"title": "Shoot",
"id": "",
"style": "positive",
"data": {}
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
Data
{
"name": "Test shot",
"shoots": [
{
"shoot": "a1",
"status": "hit"
},
{
"shoot": "a2",
"status": "hit"
}
]
}
There is no "simple" way to do this, but there is a way. The answer will be similar to this one.
First, you'll need a way of saving state for your card so you can update the card's activity. In C# you can declare a state property accessor like this:
public IStatePropertyAccessor<Dictionary<string, (string ActivityId, List<string> Shots)>> BattleshipStateAccessor { get; internal set; }
Then you can instantiate it like this
BattleshipStateAccessor = _conversationState.CreateProperty<Dictionary<string, (string, List<string>)>>("battleshipState");
You have a few decisions to make here. First, I'm choosing to make the state property a dictionary so I can keep track of multiple cards and only update the specific card that was clicked. If you don't care about that then you don't need a dictionary and you don't need to worry about "card ID's," but saving at least one activity ID is necessary so that you'll be able to update the card. As for saving the "shots," you have a few choices here. You could save that state on the client side by updating the submit action's data with each shot that's been made, but I figure I might as well save the shots in bot state because I already need to save the activity ID in bot state anyway. Then there's the question of what information about each shot you should save. In this example I'm only saving the location of the shot that the user entered and not the status of the shot, since I figure I can always recalculate the status whenever I need to.
I've modified your submit action to look like this:
{
"type": "Action.Submit",
"title": "Shoot",
"style": "positive",
"data": {
"behavior": "Shoot",
"cardId": ""
}
}
What I've done here is added two properties to your data object, and this data will be sent to your bot along with the text input's value. The "behavior" property will help your bot route to the correct function in case your bot uses multiple types of actions that can be handled different ways. The "cardId" property is just a placeholder that your bot code will fill in when creating the card. I've stored the names of these properties in the constants KEYBEHAVIOR and KEYCARDID.
You'll want a consistent way to generate your card that you can use when you send the card initially and when you update the card.
internal static IMessageActivity CreateBattleshipCardActivity(
string cardId,
object data = null)
{
data = data ?? new
{
name = "Test shot",
shoots = new string[0],
};
JObject card = CreateAdaptiveCard("battleship", data);
foreach (var token in card.Descendants()
.Select(token => token as JProperty)
.Where(token => token?.Name == KEYCARDID))
{
token.Value = cardId;
}
return MessageFactory.Attachment(new Attachment(
AdaptiveCard.ContentType,
content: card));
}
The CreateAdaptiveCard function loads the JSON template from a file with the given name, transforms it with the given data, and deserializes it into a JObject.
Using this function, you can send the card initially like this in C#:
public async Task TestBattleshipAsync(
ITurnContext turnContext,
CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
var cardId = Guid.NewGuid().ToString();
var reply = CreateBattleshipCardActivity(cardId);
var response = await turnContext.SendActivityAsync(reply, cancellationToken);
var dict = await BattleshipStateAccessor.GetAsync(
turnContext,
() => new Dictionary<string, (string, List<string>)>(),
cancellationToken);
dict[cardId] = (response.Id, new List<string>());
}
And you can update the card in response to the card's "Shoot" submit action like this:
private async Task ShootAsync(
ITurnContext turnContext,
CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
if (activity.ChannelId == Channels.Msteams)
{
var value = JObject.FromObject(activity.Value);
var cardId = Convert.ToString(value[BotUtil.KEYCARDID]);
var dict = await BattleshipStateAccessor.GetAsync(
turnContext,
() => new Dictionary<string, (string, List<string>)>(),
cancellationToken);
if (dict.TryGetValue(cardId, out var savedInfo))
{
savedInfo.Shots.Add(value["id_shoot"].ToString());
var data = new
{
name = "Test shot",
shoots = savedInfo.Shots.Select(shot => new
{
shoot = shot,
status = DetermineHit(shot),
}),
};
var update = CreateBattleshipCardActivity(cardId, data);
update.Id = savedInfo.ActivityId;
update.Conversation = activity.Conversation;
await turnContext.UpdateActivityAsync(update, cancellationToken);
}
}
}
My query goes like this: If I have a feedItem (an image posted on facebook), how can I verify that I have liked it or not? Can I verify all the interactions which I have done to the feedItem or the interactions other people have done to it (like, dislike, pin, share)? Is there any way in getstream.io to retrieve these interactions?
Thanks in advance...
Graph API provides all functions you need. Here are some examples:
Read shares[it's a field of an object]: https://developers.facebook.com/docs/graph-api/reference/v3.1/post#read
Read Shared posts: https://developers.facebook.com/docs/graph-api/reference/v3.1/object/sharedposts
read likes:
https://developers.facebook.com/docs/graph-api/reference/v3.1/object/likes#read
/likes returns only the profile for the current user if read with a
user access token:
Album, Photo, Post, Video
all returns are JSON, which you can directly Deserialize Anonymous Type without using stream, for example, likes:
{
"likes": {
"data": [
{
"name": "Bill the Cat",
"id": "155111347875779",
"created_time": "2017-06-18T18:21:04+0000"
},
{
"name": "Calvin and Hobbes",
"id": "257573197608192",
"created_time": "2017-06-18T18:21:02+0000"
},
{
"name": "Berkeley Breathed's Bloom County",
"id": "108793262484769",
"created_time": "2017-06-18T18:20:58+0000"
}
],
"paging": {
"cursors": {
"before": "Nzc0Njg0MTQ3OAZDZD",
"after": "NTcxODc1ODk2NgZDZD"
},
"next": "https://graph.facebook.com/vX.X/me/likes?access_token=user-access-token&pretty=0&summary=true&limit=25&after=NTcxODc1ODk2NgZDZD"
},
"summary": {
"total_count": 136
}
},
"id": "user-id"
}
I'm looking for some help formatting schema extension data in Microsoft's Graph API. I've been able to successfully send Office 365 messages in code and through the Graph Explorer using this body:
{
"message": {
"subject": "Test Subject",
"body": {
"contentType": "Text",
"content": "Test Body "
},
"toRecipients": [
{
"emailAddress": {
"address": "foo#email.com"
}
}
]
}
}
I created a schema extension and promoted it to "Available" status. I can query the extension to verify it's available and get this response body:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#schemaExtensions",
"value": [
{
"id": "extc5bnq6uk_TestExtension",
"description": "Test Extension",
"targetTypes": [
"Message"
],
"status": "Available",
"owner": "mysecretclienttenantgoeshere",
"properties": [
{
"name": "ValueOne",
"type": "String"
},
{
"name": "ValueTwo",
"type": "String"
}
]
}
]
}
So far I haven't been able to append extension data to a new message. I've tried formatting my request body like this:
{
"message": {
"subject": "Test Subject",
"body": {
"contentType": "Text",
"content": "Test Body "
},
"toRecipients": [
{
"emailAddress": {
"address": "foo#email.com"
}
}
],
"extc5bnq6uk_TestExtension": {
"ValueOne": "TestValue",
"ValueTwo": "TestValue"
}
}
}
and like this:
{
"message": {
"subject": "Test Subject",
"body": {
"contentType": "Text",
"content": "Test Body "
},
"toRecipients": [
{
"emailAddress": {
"address": "foo#email.com"
}
}
],
"extensions":[
{
"extc5bnq6uk_TestExtension" : {
"ValueOne" : "TestValue"
"ValueTwo" : "TestValue"
}
}
]
}
}
Both formats return a 400 code with response body:
{
"error": {
"code": "RequestBodyRead",
"message": "The property 'extc5bnq6uk_TestExtension' does not exist on type 'Microsoft.OutlookServices.Message'. Make sure to only use property names that are defined by the type or mark the type as open type.",
"innerError": {
"request-id": "21792fd0-44d1-42aa-8d51-f8abc92cbd04",
"date": "2018-08-14T16:39:31"
}
}
}
I'm posting to this URL in the graph explorer:
https://graph.microsoft.com/v1.0/me/sendMail
and to the "messages" and "sendMail" endpoints in code.
I found the answer in the Known Limitations of the documentation. Certain resource types, including messages, have to be done in two stages, an initial post and then a follow up patch.
Creating the message and then patching with this JSON returned a valid response.
{
"extc5bnq6uk_TestExtension": {
"ValueOne": "Test Value One",
"ValueTwo": "Test Value Two"
}
}
Unfortunately, another limitation for schema extensions on messages is that they can't be used to filter messages, which is what I was ultimately after.
Filtering on schema extension properties (using the $filter
expresssion) is not supported for Outlook entity types - contact,
event, message, or post.
Jeff
Based on your question you posted, you have created a schemaExtension successfully. I think you want to send an email with this schemaExtension, but when you send an email with this schemaExtension, we get the 400 code in the response.
Based on my test, I think we can use the request body as blow.
1.Create a schemaExtension like this:
{
"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#schemaExtensions/$entity",
"id":"{extensionId}",
"description":"sample description",
"targetTypes":[
"Message"
],
"status":"Available",
"owner":"{owner id}",
"properties":[
{
"name":"p1",
"type":"String"
},
{
"name":"p2",
"type":"String"
}
]
}
Create a message
POST https://graph.microsoft.com/v1.0/me/messages
{
"message":{
"subject":"Meet for lunch?",
"body":{
"contentType":"Text",
"content":"The new cafeteria is open."
},
"toRecipients":[
{
"emailAddress":{
"address":"{toRecipients email address}"
}
}
],
"extensions":[
{
"#odata.type":"Microsoft.Graph.OpenTypeExtension",
"extensionName":"{extensionName}",
"p1":"Wingtip Toys",
"p2":"10000"
}
]
},
"saveToSentItems":"false"
}
when we send this message with the request, we will get the 202 code. The {toRecipients email address} will receive the email.
On rare occasion when receiving the message "Additional text found in JSON string after finishing deserializing object." the first thing i do is fix some JSON structure typo of mine using JSONLint.
However this time the lint says the json is valid and i can't see where the problem lies. (As i don't control the data's source i need to ldeserialize to a generic object that i then traverse. - Is this possibly the error on my part?)
I'm deserializing with Unity specific Newtonsoft.Json (JsonNet-Lite)
object resultData = JsonConvert.DeserializeObject<object>(jsonString);
What am i missing?
JSON String
"{\"statements\":[{\"id\":\"14b6382c-ddb9-44c5-a22c-a133cec50711\",\"actor\":{\"objectType\":\"Agent\",\"mbox_sha1sum\":\"f7f99253cf6ede467c3a5425d05bfcd96e524595\",\"name\":\"My Name\"},\"verb\":{\"id\":\"https://w3id.org/xapi/dod-isd/verbs/completed\",\"display\":{\"en-US\":\"completed\"}},\"result\":{\"success\":true,\"completion\":true,\"duration\":\"PT0.05S\"},\"context\":{\"contextActivities\":{\"grouping\":[{\"id\":\"http://example.com/activities/MyActivity_Grandparent\",\"objectType\":\"Activity\"}],\"parent\":[{\"id\":\"http://example.com/activities/MyActivity_Parent\",\"objectType\":\"Activity\"}]}},\"timestamp\":\"2018-02-23T19:18:34.145Z\",\"stored\":\"2018-02-23T19:18:34.145Z\",\"authority\":{\"objectType\":\"Agent\",\"account\":{\"homePage\":\"http://cloud.scorm.com\",\"name\":\"abcdef-ghijk\"},\"name\":\"Test Provider\"},\"version\":\"1.0.0\",\"object\":{\"id\":\"http://example.com/activities/MyActivity\",\"definition\":{\"extensions\":{\"https://w3id.org/xapi/dod-isd/extensions/interactivity-level\":\"3\",\"https://w3id.org/xapi/dod-isd/extensions/ksa\":\"Attitude\",\"https://w3id.org/xapi/dod-isd/extensions/category\":\"Responding\",\"http://example.com/TerminalObjective\":\"My Activity Objective\"},\"name\":{\"en-US\":\"My Activity Name\"},\"description\":{\"en-US\":\"My Activity Description\"},\"type\":\"http://adlnet.gov/expapi/activities/simulation\"},\"objectType\":\"Activity\"}}],\"more\":\"/tc/Z5R2XATQZW/sandbox/statements?continueToken=e50555fe-0c3d-4663-91c4-7f0ff7df4ccf\"}"
JSON Formatted for readability
{
"statements": [{
"id": "14b6382c-ddb9-44c5-a22c-a133cec50711",
"actor": {
"objectType": "Agent",
"mbox_sha1sum": "f7f99253cf6ede467c3a5425d05bfcd96e524595",
"name": "My Name"
},
"verb": {
"id": "https://w3id.org/xapi/dod-isd/verbs/completed",
"display": {
"en-US": "completed"
}
},
"result": {
"success": true,
"completion": true,
"duration": "PT0.05S"
},
"context": {
"contextActivities": {
"grouping": [{
"id": "http://example.com/activities/MyActivity_Grandparent",
"objectType": "Activity"
}],
"parent": [{
"id": "http://example.com/activities/MyActivity_Parent",
"objectType": "Activity"
}]
}
},
"timestamp": "2018-02-23T19:18:34.145Z",
"stored": "2018-02-23T19:18:34.145Z",
"authority": {
"objectType": "Agent",
"account": {
"homePage": "http://cloud.scorm.com",
"name": "abcdef-ghijk"
},
"name": "Test Provider"
},
"version": "1.0.0",
"object": {
"id": "http://example.com/activities/MyActivity",
"definition": {
"extensions": {
"https://w3id.org/xapi/dod-isd/extensions/interactivity-level": "3",
"https://w3id.org/xapi/dod-isd/extensions/ksa": "Attitude",
"https://w3id.org/xapi/dod-isd/extensions/category": "Responding",
"http://example.com/TerminalObjective": "My Activity Objective"
},
"name": {
"en-US": "My Activity Name"
},
"description": {
"en-US": "My Activity Description"
},
"type": "http://adlnet.gov/expapi/activities/simulation"
},
"objectType": "Activity"
}
}],
"more": "/tc/Z5R2XATQZW/sandbox/statements?continueToken=e50555fe-0c3d-4663-91c4-7f0ff7df4ccf"
}
The deserialization method, you chose, is rather for a case, when you are deserializing a well-known Json to an object, which matches the structure.
In this case the Json structure does not match the System.Object type structure, you are trying to deserialize to. The library complaints, that there's much more in the Json, than in the System.Object structure.
For parsing any Json object, you could try an example from the Newtonsoft.Json documentation:
string json = #"{
CPU: 'Intel',
Drives: [
'DVD read/writer',
'500 gigabyte hard drive'
]
}";
JObject o = JObject.Parse(json);
You will still need to traverse the deserialized object. Depending on what you want to achieve, it may actually be much better to look for a .Net class that matches the Json structure (or write one).