Flow Yammer

Using Flow to monitor activity in Yammer

This post demonstrates how you can use Microsoft Flow to work with JSON. The example used is monitoring a Yammer Group.
UPDATE 16/04 - You need to take some additional steps if you need to access messages in the All Company or All Network groups. The additional steps are described in this post.

Recently I was asked whether it was possible to monitor activity in a specific Yammer Group. The particular request was to alert the Group Owners when files where added but you can use the approach outlined below for other situations e.g. harvesting text for sentiment analysis.

Whilst Yammer includes audit reports they are aimed at the Network Administrator and the end points exposed through the Graph are more focused on Group analytics e.g. Get user detail, Get activity counts etc. This is not ideal for situations where you do not want to grant full administrative access to Yammer e.g. you have a curious Group Owner who wants to look at the behavior of a Group in more detail.

Microsoft Flow includes a limited number of triggers and actions for Yammer that do not require elevated permissions run. Usefully it includes a trigger for ‘When there is a new message in a group‘ which returns a JSON response that includes every element of a message. This includes items like:

  • Details of the sender (as an ID value which is easy to convert to a name etc.)
  • Whether it was a new message or a reply
  • Creation date and time
  • Links to the message and any attachment
  • Message text
  • Number of likes (and who liked it)
  • Topics

The JSON response includes an array detailing any message attachments and GIF’s. For Files, the array includes items like:

  • File type
  • File name
  • Description
  • Creation date and time
  • Links view or download the file
  • File size

Additionally for Video content it includes details for encoding and streaming. For GIF’s, the array includes items like:

  • Name
  • Link to the GIF source

Gotchas

When implementing this method, there are a couple of gotchas to be aware of:

#1 Message attachments are stored in an array so any monitoring would need to loop through the array and process each item separately. For example if you had a message that contained a File and a GIF then you would have an array containing two items to process. Looping through an array is not a gotcha but the JSON schema used for GIFs is different to Files. The difference is the gotcha as processing the JSON can result in the Flow failing – I have a method to address this later in the post. IMHO Yammer could easily solve this inconsistency.

The image shows the different schemas used for Files and GIFs
Attachments array – note the difference between Files and GIFs

#2 Flow only contains two triggers for Yammer namely ‘When there is a new message in a group’ and ‘When there is a new message in my followed feed’. In the case of monitoring Files a user could work around the Flow as they could add the File to the files section of a Group but not include it in a message. This changes if the Yammer Group is underpinned by an Office 365 Group as you could then monitor the underlying SharePoint Document Library with a ‘When a file is created or modified’ trigger and then merge the results with message monitoring.

#3 You have to define the Group to be monitored. If you have many Groups to monitor then you need to either consider using the audit reports or create multiple Flows. Thankfully Flow includes a method to duplicate Flows.

Basic process

Setting the difference between Files and GIFs to one side for a moment the process is straight forward:

Image shows three steps in the basic Flow
Basic three step process
  1. Trigger Flow using a ‘When there is a new message in a group’ trigger
  2. Process the JSON used to describe the message using a ‘Parse JSON’ action (from the Data Operations group)
  3. Determine whether an attachment is present and act if there is one using a ‘Condition’ action (from the Control group)

‘When there is a new message in a group’ trigger

Flow trigger for new message in Yammer
New message trigger

Yammer treats new messages and replies in the same way and so you can use the
‘When there is a new message in a group’ trigger to start your Flow. Use the drop down lists to first select the Target Group and then your Network name.

‘Parse JSON’ action

Parse JSON action using the Message List field as Content from the trigger action
Parse JSON action

The output of the When there is a new message in a group’ trigger is a blob of JSON. The ‘Parse JSON’ action allows you to turn the JSON items into fields that you can reference and act upon in subsequent steps. The input to the action is the Message List field from the trigger. You could create a Schema for use in the action by first adding the trigger and a dummy step e.g. a ‘Compose’ action and then inspecting the output of a successful Flow or you could use the Schema below. Note that for the Schema I have removed the items held within Attachments array owing to the difference between Files and GIFs as that is not important for the basic solution. In the next step I will test whether the array is empty i.e. contains no attachments of either type. Later in the post I will show you how to handle the details of the attachments.

{
    "type": "object",
    "properties": {
        "id": {
            "type": "integer"
        },
        "sender_id": {
            "type": "integer"
        },
        "replied_to_id": {
            "type": "integer"
        },
        "created_at": {
            "type": "string"
        },
        "network_id": {
            "type": "integer"
        },
        "message_type": {
            "type": "string"
        },
        "sender_type": {
            "type": "string"
        },
        "url": {
            "type": "string"
        },
        "web_url": {
            "type": "string"
        },
        "group_id": {
            "type": "integer"
        },
        "body": {
            "type": "object",
            "properties": {
                "parsed": {
                    "type": "string"
                },
                "plain": {
                    "type": "string"
                },
                "rich": {
                    "type": "string"
                }
            }
        },
        "thread_id": {
            "type": "integer"
        },
        "client_type": {
            "type": "string"
        },
        "client_url": {
            "type": "string"
        },
        "system_message": {
            "type": "boolean"
        },
        "direct_message": {
            "type": "boolean"
        },
        "chat_client_sequence": {},
        "language": {
            "type": "string"
        },
        "notified_user_ids": {
            "type": "array"
        },
        "privacy": {
            "type": "string"
        },
        "attachments": {
            "type": "array"
        },
        "liked_by": {
            "type": "object",
            "properties": {
                "count": {
                    "type": "integer"
                },
                "names": {
                    "type": "array"
                }
            }
        },
        "content_excerpt": {
            "type": "string"
        },
        "group_created_id": {
            "type": "integer"
        },
        "topics": {
            "type": "array"
        }
    }
}

‘Condition’ action

Condition action
Condition action used to test whether there are any attachments

A ‘Condition’ action is used to test if the Attachments array is empty i.e. no attachments. If empty the ‘If yes’ branch is followed and if there are attachments the ‘If no’ branch is followed. An ‘is equal to’ test is used to test if the array is empty:

(Left side of the Condition) Using an Empty expression to check if the attachment array is empty
(Left side of the Condition) Using an Empty expression to check if the attachment array is empty
(Right side of the Condition) A True expression is used as the comparison value

(Right side of the Condition) A True expression is used as the comparison value

A result of true means the array is empty. The outcome of the condition could be used to send an email when attachments are detected using a ‘Send an email (V2)’ action.

Advanced process

What if you want to log the attachments in list or only act if a File opposed to a GIF is added? As mentioned earlier, the JSON schema for Files and GIF’s are different and so, in theory, each type needs to be processed differently. The potential for multiple attachments means that you will need to loop through the attachment array. I’ve covered variables and loops in detail in a previous post so I’ll not cover them here. To be honest this is where the Flow can get ugly as you’ll be tempted to act on successes and failures using multiple ‘Parse JSON’ actions nested inside the ‘If no’ branch to process Files and GIF’s. My advice is to not take that approach. My advice is to create an additional ‘Parse JSON’ action that uses a composite JSON Schema (combining fields from both the File and GIF type) which is focused upon the attachments array. The additional action is then nested inside an ‘Apply to each’ action inside the ‘If no’ branch to work through the attachments array.

Additional Parse JSON action nested inside an Apply to Each action to work through each item in the attachment array
Additional Parse JSON focused upon the attachments nested inside an Apply to each action

If you use the Sample generator included in a ‘Parse JSON’ action then watch out for the Description item as it may be processed as a string which causes errors when it is null i.e. empty (which can happen with GIFs). An example composite Schema is shown below:

{
    "type": "object",
    "properties": {
        "id": {
            "type": "integer"
        },
        "created_at": {
            "type": "string"
        },
        "network_id": {
            "type": "integer"
        },
        "url": {
            "type": "string"
        },
        "web_url": {
            "type": "string"
        },
        "group_id": {
            "type": "integer"
        },
        "privacy": {
            "type": "string"
        },
        "fake_ogo_ymodule": {
            "type": "boolean"
        },
        "type": {
            "type": "string"
        },
        "record_id": {
            "type": "integer"
        },
        "name": {
            "type": "string"
        },
        "original_name": {
            "type": "string"
        },
        "full_name": {
            "type": "string"
        },
        "description": {},
        "content_type": {
            "type": "string"
        },
        "content_class": {
            "type": "string"
        },
        "owner_id": {
            "type": "integer"
        },
        "official": {
            "type": "boolean"
        },
        "storage_type": {
            "type": "string"
        },
        "target_type": {
            "type": "string"
        },
        "small_icon_url": {
            "type": "string"
        },
        "large_icon_url": {
            "type": "string"
        },
        "download_url": {
            "type": "string"
        },
        "thumbnail_url": {},
        "object_type": {
            "type": "string"
        },
        "object_name": {
            "type": "string"
        },
        "host_url": {},
        "inline_url": {
            "type": "string"
        },
        "inline_html": {
            "type": "string"
        },
        "ymodule": {
            "type": "object",
            "properties": {
                "web_app_id": {
                    "type": "string"
                },
                "app_id": {
                    "type": "string"
                },
                "icon_url": {
                    "type": "string"
                }
            }
        },
        "preview_url": {},
        "large_preview_url": {},
        "size": {
            "type": "integer"
        },
        "owner_type": {
            "type": "string"
        },
        "last_uploaded_at": {
            "type": "string"
        },
        "last_uploaded_by_id": {
            "type": "integer"
        },
        "last_uploaded_by_type": {
            "type": "string"
        },
        "uuid": {},
        "transcoded": {},
        "streaming_url": {},
        "path": {
            "type": "string"
        },
        "y_id": {
            "type": "integer"
        },
        "overlay_url": {},
        "file": {
            "type": "object",
            "properties": {
                "url": {
                    "type": "string"
                },
                "size": {
                    "type": "integer"
                }
            }
        },
        "latest_version_id": {
            "type": "integer"
        },
        "status": {
            "type": "string"
        },
        "real_type": {
            "type": "string"
        }
    }
}

The output of the ‘Apply to each’ action can be included in a ‘Create HTML Table’ action (say for use in an email). You will need an ‘Initialize variable’ action before the ‘Compose’ action to hold the output of the ‘Apply to each’ action as you cannot define new variables inside a branch.

Using a variable to store values from the attachment array and outputting to a Table
Using a variable to store values from the attachment array and outputting to a Table

In the example above I create a simple table that has two columns – File type and Link using an array definition {“Heading1”: “Flow field1”,
“Heading2”: “Flow field2” etc.}. It is important to add the Flow field inside quotation marks (Note: When you copy Flow items and paste them into a document you see the expression formula that defines the field. The details below are the same as in ‘Append to array variable’ action in the screenshot above ):

{
  "File type": "@{body('Parse_JSON_for_Table')['type']}",
  "Link": "@{body('Parse_JSON_for_Table')['web_url']}"
}

The sample output is shown below:

Table of results showing 3 items attached to a message.
Table showing details of a message that has 3 attachments.

The ymodule file type corresponds to a GIF. Videos are classified as files.

There you have it. A methodology for using Flow to monitor Yammer. The full Flow is:

Image showing all of the Flow actions
Complete Flow