Using Yammer Open Graph in .NET - Part 2

In the first part of this series - https://blogs.technet.com/b/speschka/archive/2014/05/29/using-yammer-open-graph-in-net.aspx - I described how to create a Yammer Open Graph object (OGO). In this posting I'll explain how to create a new post in the newsfeed for the OGO, as well as read all messages that have been posted to that OGO. Just like the previous post, you should always begin by downloading the code from my original post on using the Yammer APIs from a .NET client application: https://blogs.technet.com/b/speschka/archive/2013/10/05/using-the-yammer-api-in-a-net-client-application.aspx. Make sure you also visit part 1 of this mini series and download the class attached to the post that is used to serialize and deserialize OGO items in .NET.

Now, with all that in hand, let's talk about creating and reading messages for an OGO. Fortunately, if you've used the other code in my original post on the Yammer APIs in .NET, it will look pretty similar. Before we get into actually creating or reading messages though, there is one fundamental task that you have to do in both cases - you need the OGO ID. The reason why is that we're delving into the somewhat, um, undocumented world of Yammer REST APIs and OpenGraph. Now like anything, there can be a variety of ways that you get the OGO ID you need. Arguably the best place to do it is when you actually create the OGO; you will get a JSON string in response. The JSON that you get back can be deserialized into the YammerGraphObjectItem class that I posted in part 1 of this series. When you look at it you'll see that it has an object_id property; what I've done in some cases is just taken that value after I create the item and write it to a database so I can use it later (for cases just like this). If you didn't do that, or that isn't feasible for some reason, then you need to grab it some other way. Your choices for doing so are a little less than awesome however. As a practical matter, I've only found two ways of digging up the object ID after the fact that work reliably - using Yammer search or posting an update to the original OGO. Let's look at each of those options in a little more detail.

To use search you can use the code I posted in yet another post about using the Yammer API for integrating search results here: https://blogs.technet.com/b/speschka/archive/2014/03/11/integrating-yammer-and-sharepoint-2013-search-results.aspx and here: https://blogs.technet.com/b/speschka/archive/2014/03/12/enhancing-the-yammer-search-results-and-messages-samples-and-deserialization.aspx (yes, I AM starting to realize that this whole unstructured data thing can be a real PITA to use when you're just trying to accomplish a basic task).

Once you have that code, you can go ahead and query Yammer. The question you may have is "what do I search for"? Ha, :-) the answer is you search for the Url associated with your OGO. If you're using the sample code I posted previously, your code to search Yammer would look something like this:

response = MakeGetRequest(searchUrl + "?search=" + Url + "&match=any-exact", accessToken);
YammerSearchResults ysr = YammerSearchResults.GetInstanceFromJson(response);

where "Url" in the first line of code is the Url associated with your OGO (you should know all about what the accessToken is and how to get it from my other posts on this topic). Now you may or may not get back search results, and this is another pain point. If ALL you've done is create an OGO, in my experience the OGO will not be returned in the search results. However, if you have at least ONE newsfeed item created for your OGO then you WILL get it back in search results. If you do get search results back, then you need to do a little digging with LINQ to get the object ID you need. Here's the code I use for that:

if ((ysr != null) && (ysr.Messages.Messages.Count > 0))
{
var msgs = from YammerMessage ym in ysr.Messages.Messages
where ym.Attachments.Count > 0 &&
ym.Attachments[0].WebUrl == Url
select ym;

List<YammerMessage> yMsgs = msgs.ToList<YammerMessage>();

if (yMsgs.Count > 0)
{
gi = new YammerGraphObjectItem();
gi.object_id = yMsgs[0].Attachments[0].RecordID;
}
}

So if I found the item using search, then I look for an item that has an attachment where the WebUrl property matches the Url of my OGO. Each attachment has a RecordID property, and for an OGO it's the object ID. So I can grab that piece of data from there and then use it to work with my OGOs. If I didn't get any search results back, then the only way I found to get the object ID again is just to create a "fake" update to the OGO. When you do that, just like when you create an OGO, it returns the object ID. Just like when you create it, you need the Url associated with the OGO in order to update it. Here's some code showing a minimalistic "fake" update (I just keep adding myself as a follower to the OGO):

YammerGraphObject go = new YammerGraphObject();

go.Activity.Action = "follow";
go.Activity.Actor = new YammerActor("Steve Peschka", "speschka@yammo.onmicrosoft.com");
go.Activity.Private = true;

YammerGraphObjectInstance jo = new YammerGraphObjectInstance();
jo.Url = Url;
go.Activity.Object = jo;

string postData = go.ToString();
response = MakePostRequest(postData, graphPostUrl, accessToken, "application/json");

gi = JsonConvert.DeserializeObject<YammerGraphObjectItem>(response);

I set the Private property of the Activity to true so that it won't bother trying to email anyone, which I don't want to happen since it's a "fake" update. Otherwise, I plug the OGO Url into the Url property, convert all that object stuff to a string that the REST API understands, and then send in my POST request to do the update. I take the string I get back and use the NewtonSoft JSON library to convert it to an instance of a YammerGraphObjectItem so I can pull the object_id property out of there.

Whew! That's a lot of background information, but now you know how to go get that OGO ID if you weren't able to capture it when it was created. Now that you have the ID, everything is downhill from here. So, going back to our original problem statement, let's work on creating a new post in the discussion of the OGO. The good news is, it's pretty simple:

string myMessage = "This is a message from my OGO!";

string msg = "body=" + myMessage + "&attached_objects[]=open_graph_object:" +
gi.object_id + "&skip_body_notifications=true";

string response = MakePostRequest(msg, messageUrl + ".json", accessToken);

if (!string.IsNullOrEmpty(response))
{
//it worked! You can look at the return message using code like this:
//YammerGraphMessages newMsg = YammerGraphMessages.GetInstanceFromJson(response);
}

A couple of things to point out here: first, the gi.object_id above is what I got from getting my OGO ID using one of the two methods I described earlier in this post. I just created a simple method that looks for it in search, and if it doesn't find it there then it does a "fake" update to get the ID and returns the YammerObjectGraphItem as shown in the code above. Second, the "messageUrl" you see in the MakePostRequest method is the same exact Url that we use for creating new messages in groups. You will find this in the Yammer Developer docs (developer.yammer.com) or of course in the sample code I've posted previously. For completeness the Url is https://www.yammer.com/api/v1/messages. See - not bad at all!

Okay, now let's get the messages that have been posted to my OGO. Doing that is also very similar to getting them for a Yammer group; here's some sample code:

string response = MakeGetRequest(messageUrl + "/open_graph_objects/" + gi.object_id + ".json", accessToken);

YammerGraphMessages yms = YammerGraphMessages.GetInstanceFromJson(response);

foreach (YammerMessage ym in yms.Messages)
{
//do whatever you want with your messages...for example get the text of the message:
//ym.MessageContent.PlainText
}

Here are the some points about this code: first, like above, you'll notice I'm using the gi.object_id to get the object ID of the OGO. The second thing is that I'm using the super secret and currently undocumented Url path to get to the OGO item, which you see I append to the messageUrl: "/open_graph_objects/". I then append the ID of the OGO I want and away we go - again, extremely simple at that point.

With that I will conclude this two part series. You learned how to use .NET to create an Open Graph object in Yammer, how to find the ID of your Open Graph object if you didn't save it when you created the object, and how to create new items in the newsfeed for the OGO as well as read items in the newsfeed for the OGO. Hope you find this helpful!