Notifications from Verint to Slack and/or Microsoft Teams when content is posted, processed in an Azure Function
- Prerequisites
- About this Azure Function
content
Azure Functionideas
Azure Function- Reference
- Troubleshooting
- Notes
This Azure Function sends notifications of content activity in Verint to Slack and/or Microsoft Teams when the following are posted: blog posts, videos, threads, replies, ideas, comments, etc.
Deploy the Azure Function from a repo to the cloud. The Azure Function package inside the Azure Function is redeployed.
- Enable the Azure Functions extension.
- Clone this repo to your machine.
- Using the Azure Function extension icons, set up a project inside the local repo.
- Add a new Azure Function locally.
- Deploy the Azure Function to the Azure Portal from your local machine.
- Go to the Azure Function in the Azure Portal.
- Go to the Deployment Center and connect your repo.
- Navigate to the Azure Function home page > Platform features > Advanced tools (Kudu) > CMD prompt navigate to
wwwroot
(site root wherepackage.json
is). - Run
npm install
.
- Create an Azure Logic App in the same resource group as the Azure Function.
- Add the
When a HTTP request is received
task. - In the Logic App, add an Azure Function item and select
content
. - Save. Copy the
HTTP POST URL
. - See also Azure Logic App schema.
Go to Verint > Click the pencil at top left > Administration
> Integrations
> Webhooks
> Add the URL copied from the Azure Logic App.
Variable(s) to replace | What to replace them with |
---|---|
ORGANIZATION |
Applicable portion of the Verint community's URL |
TOKEN |
Verint API token |
VERINTGROUPID |
Verint group ID(s) |
AZURE LOGIC APP URL |
URL produced by following these instructions |
Variable(s) to replace | What to replace them with |
---|---|
SLACK INCOMING WEBHOOK |
URL of Slack incoming webhook, created in an app on Slack's website |
theUsername |
Name of the bot |
theIconEmoji |
Emoji of the bot |
color |
HEX value of message's color in Slack |
(ideas/index.js only) footer_icon |
Emoji displayed next to the idea category |
Variable(s) to replace | What to replace them with |
---|---|
MICROSOFT TEAM ID |
Microsoft Team ID |
MICROSOFT CHANNEL ID |
Microsoft Team channel ID |
- Go to your Verint site avatar (top right) > Settings > API Keys (very bottom) > Manage application API keys > Generate new API key.
- Base-64 encode
apikey:user.name
.
- Plug the List Group REST Endpoint into a script of this nature).
- In the Microsoft Teams desktop client, click
...
next to the channel name >Get link to channel
. - Copy and paste the link somewhere to inspect.
https://teams.microsoft.com/l/channel/00%0x00x00000xx0x00xx00xx0x000x00000%00thread.skype/General?groupId=xx000x00-0xx0-0x0x-0000-x0xx0000000x&tenantId=00000x0x-000x-0x00-000x-000000x0000x
Variable | Corresponding ID in the link | Example |
---|---|---|
MICROSOFT CHANNEL ID |
Characters following channel |
00:0x00x00000xx0x00xx00xx0x000x00000%00@thread.skype (note that the HTML in the URL is replaced by the actual characters) |
MICROSOFT TEAM ID |
xx000x00-0xx0-0x0x-0000-x0xx0000000x |
Ignore tenantId
.
The parent Azure Function (your-function
) contains two child Azure Functions:
content
ideas
Verint provides a Generic Content REST API webhook that sends generic content events to an endpoint. These events do not include ideas.1
Both child Azure Functions use the slackify-html
npm package, modified for this Azure Function in Shared/slackify-html.js
, to transform processed content into Slack posts in Slack syntax (Slack does not accept the HTML format returned in the content body field of the webhook payload) and, depending on the group or container they occurred in, send the posts to designated Slack and/or Microsoft Teams channels. (Microsoft Teams also accepts the content transformed for Slack.)23
The Azure Function provides an endpoint that can be used with the webhook.4 When a Verint generic-content HTTP post is received, the Azure Logic App triggers the content
child Azure Function. See Azure Logic App for setup details.
- Type:
http trigger
- For selected events as determined in the webhooks section of the Verint
Admininstration
>Integrations
>Webhooks
panel - Selected events include both new content and updated media
- Loops through events because multiples can be sent at once56
- Treats general content events and comments differently because
ContentId
andContentTypeId
are returned with general content events whereas comments include only aCommentId
:
Note: Comment
events are included in the webhook, but the JSON sent is in a different format than other content events (see examples below) and is handled uniquely in the code.
{
"events": [
{
"TypeId": "00x0000-xxxx-0x00-0xx0-x000000x0000",
"DateOccurred": "2021-13-05T01:02:39.0760324Z",
"EventData": {
"ActorUserId": 1234,
"ContentId": "00x0000-xxxx-0x00-0xx0-x000000x0000",
"ContentTypeId": "00x0000-xxxx-0x00-0xx0-x000000x0000",
"BlogPostId": 123,
"BlogId": 12
}
}
]
}
{
"events": [{
"TypeId": "00x0000-xxxx-0x00-0xx0-x000000x0000",
"DateOccurred": "00x0000-xxxx-0x00-0xx0-x000000x0000",
"EventData": {
"ActorUserId": 1234,
"CommentId": "00x0000-xxxx-0x00-0xx0-x000000x0000"
}
}]
}
ContainersIds are roughly equivalent to GroupId
s. This is explained in the reference below. However, comments on wikis are not included in a group's ContainerId
. For this reason, in the block of if (containerId ==
clauses (which split which webhook events are posted in which Slack channel(s)), a clause must be added for the ContainerId
specific to the wiki.
- Type:
Timer trigger
- Executes every 10 minutes
- Queries the REST API for ideas posted in the past 10 minutes
- Aligns webhook and local timestamps using the
moment
andmoment-timezone
npm packages89
- The cron schedule (set in
ideas/function.json
) is59 9,19,29,39,49,59 * * * *
.10 - JavaScript code sources for Slack posts:1112
- An idea call through REST API returns
GroupId
. - A Verint webhook event does not return
GroupId
, onlyContainerId
. - Containers vs. groups1314
https://mysite/api.ashx/v2/search.json?Category=blog&PageSize=1&includefields=url,content.createdbyuser.displayname
Possible to do:
IncludeFields=Url,Content.ContentId
You can't currently specify:
IncludeFields=Url,Content.CreatedByUser.Id
To output JSON (this code is not in use - see Azure Logic app trigger):
var readyString = "<" + profileUrlNoQuotes + "|" + usernameNoQuotes + "> posted " + "<" + urlNoQuotes + "|" + subjectNoQuotes + ">\n" + trimmedString;
// Response of the Function to be used later
context.res = {
body: { readyString }
};
In a folder at root called Shared is a file of shared code (in this case, slackify-html.js
).
(If working in the Azure Functions UI rather than in the VS Code Azure Functions extension, use Kudu > site > wwwroot.)
watchDirectories
is added to host.json. If working in the Azure Functions UI, do this in Azure Function App settings:
{
"version": "2.0",
"watchDirectories": [
"Shared"
]
}
The shared code is referenced in the Azure Function index.js
file:
var slackify = require('../Shared/slackify-html.js');
This schema code represents When a HTTP request is received
> Request Body JSON Schema
"
{
"properties": {
"events": {
"items": {
"properties": {
"DateOccurred": {
"type": "string"
},
"EventData": {
"properties": {
"ActorUserId": {
"type": "integer"
},
"BlogId": {
"type": "integer"
},
"BlogPostId": {
"type": "integer"
},
"CommentId": {
"type": "string"
},
"ContentId": {
"type": "string"
},
"ContentTypeId": {
"type": "string"
},
"ForumId": {
"type": "integer"
},
"ForumReplyId": {
"type": "integer"
},
"ForumThreadId": {
"type": "integer"
},
"GalleryId": {
"type": "integer"
},
"MediaId": {
"type": "integer"
},
"WikiId": {
"type": "integer"
},
"WikiPageId": {
"type": "integer"
}
},
"type": "object"
},
"TypeId": {
"type": "string"
}
},
"required": [
"TypeId",
"DateOccurred",
"EventData"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
- Make sure the Azure Logic App is not turned off.
- If the Azure Function starts erroring out unexpectedly, restart it in the Azure portal.1516
- Make sure you have installed all the packages at root, including
moment
andmoment-timezone
.
Footnotes
-
A feature request is logged with Verint: https://community.telligent.com/community/10/f/ask-the-community/1146446/rest-api-return-results-for-specified-time-period ↩
-
https://stackoverflow.com/questions/53925981/is-there-a-better-way-to-turn-html-to-plain-text-in-javascript-than-a-series-of ↩
-
https://stackoverflow.com/questions/53698843/replacing-quot-with-using-common-methods-does-not-work-in-a-javascript-azure ↩
-
After a few days the Function stops posting to Slack, despite experiments with consumption vs. non-consumption plans and other setup options. (See Azure Function output for unused code.) For this reason an Azure Logic App in the same resource group is used as the endpoint. ↩
-
https://community.telligent.com/community/10/f/ask-the-community/1146487/multiple-events-sent-at-once-with-webhook ↩
-
https://stackoverflow.com/questions/54082871/javascript-loop-to-accommodate-filtered-array-of-objects-received-from-a-webhook ↩
-
https://stackoverflow.com/questions/53990376/process-two-nearly-identical-json-blocks-with-one-set-of-code ↩
-
https://stackoverflow.com/questions/53891951/conflicting-timestamps-in-javascript-azure-function ↩
-
https://stackoverflow.com/questions/53753338/if-statement-inside-javascript-azure-function-not-working ↩
-
https://gist.github.com/csepulv/7174b19c1fbe219f057f1c89b1abc806 ↩
-
https://community.telligent.com/community/10/f/ask-the-community/1146502/container-vs-group-in-rest-api ↩
-
https://community.telligent.com/community/10/f/ask-the-community/1146503/lists-of-containertypeid-applicationtypeid-contenttypeid-etc ↩
-
https://stackoverflow.com/questions/54015261/reference-external-script-in-javascript-azure-function-code ↩ ↩2
-
https://stackoverflow.com/questions/54085156/azure-function-triggered-by-webhook-performs-action-on-compounding-list-of-histo ↩
-
https://stackoverflow.com/questions/53972297/how-to-determine-why-an-azure-function-app-is-not-triggered-by-a-webhook ↩