This commit is contained in:
morsh 2017-05-14 18:55:42 +03:00
Родитель 630fc37c93
Коммит 33ca6d5bc1
10 изменённых файлов: 430 добавлений и 18 удалений

0
events.js → dist/events.js поставляемый
Просмотреть файл

8
dist/example.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
var monitor = require('./');
var bot = null; // botbuilder object
monitor.monitor(bot, { transactions: [
{
intent: 'alarm.set',
test: /^(Creating alarm named)/i
}
] });

392
dist/index.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,392 @@
var util = require('util');
var _ = require('lodash');
var request = require('request');
var builder = require('botbuilder');
var appInsights = require("applicationinsights");
var client = null;
var _console = {};
var _methods = {
"debug": 0,
"info": 1,
"log": 2,
"warn": 3,
"error": 4
};
var _sentimentMinWords = 3;
var _sentimentUrl = 'https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment';
var _sentimentId = 'bot-analytics';
var _sentimentKey = null;
var Events = {
ReceiveMessage: {
name: 'message.received',
format: {
text: 'message.text',
type: 'message.type',
timestamp: 'message.timestamp',
conversationId: 'message.address.conversation.id'
}
},
SendMessage: {
name: 'message.send',
format: {
text: 'message.text',
type: 'message.type',
timestamp: '(new Date()).toISOString()',
conversationId: 'message.address.conversation.id'
}
},
ConversionStarted: {
name: 'message.convert.start',
format: {
name: 'comversion name',
timestamp: 'message.timestamp',
channel: 'address.channelId - facebook/slack/webchat/etc...',
conversationId: 'conversation.id',
userId: 'user.id',
userName: 'user.name'
}
},
ConversionEnded: {
name: 'message.convert.end',
format: {
name: 'comversion name - similar to start',
successful: 'true/false',
count: 'default is 1, but can log more than 1',
timestamp: 'message.timestamp',
channel: 'address.channelId',
conversationId: 'conversation.id',
callstack_length: 'callstack.length - how many dialogs/steps are in the stack',
userId: 'user.id',
userName: 'user.name'
}
},
Intents: {
name: 'message.intent.dialog',
format: {
intent: 'intent name / id / string',
state: 'current session state',
channel: 'address.channelId',
conversationId: 'conversation.id',
callstack_length: 'callstack.length',
userId: 'user.id',
userName: 'user.name'
}
},
Sentiment: {
name: 'message.sentiment',
format: {
text: 'message.text',
score: 'sentiment score',
timestamp: 'message.timestamp',
channel: 'address.channelId',
conversationId: 'conversation.id',
userId: 'user.id',
userName: 'user.name'
}
}
};
var formatArgs = (args) => {
return util.format.apply(util.format, Array.prototype.slice.call(args));
};
var setup = () => {
Object.keys(_methods).forEach(method => {
console[method] = (function () {
var orig = console.log;
return function () {
try {
var msg = formatArgs(arguments);
client.trackTrace(msg, _methods[method]);
var tmp = process.stdout;
process.stdout = process.stderr;
orig.apply(console, arguments);
}
finally {
process.stdout = tmp;
}
};
})();
});
};
/**
* Monitor requests made to the bot framework
* @param {UniversalBot} bot
* @param {ConversionConfig} conversionConfig
*/
var monitor = (bot, options) => {
options = options || {};
if ((!options.instrumentationKey) &&
(!process.env.APPINSIGHTS_INSTRUMENTATIONKEY)) {
throw new Error('App Insights instrumentation key was not provided in options or the environment variable APPINSIGHTS_INSTRUMENTATIONKEY');
}
appInsights.setup(options.instrumentationKey || process.env.APPINSIGHTS_INSTRUMENTATIONKEY).start();
client = appInsights.getClient(options.instrumentationKey || process.env.APPINSIGHTS_INSTRUMENTATIONKEY);
if (!options.sentimentKey && !process.env.CG_SENTIMENT_KEY) {
console.warn('No sentiment key was provided - text sentiments will not be collected');
}
else {
_sentimentKey = options.sentimentKey || process.env.CG_SENTIMENT_KEY;
}
var transactions = options.transactions || [];
setup();
if (bot) {
// Adding middleware to intercept all received messages
bot.use({
botbuilder: function (session, next) {
try {
var message = session.message;
var address = message.address || {};
var conversation = address.conversation || {};
var user = address.user || {};
var item = {
text: message.text,
type: message.type,
timestamp: message.timestamp,
conversationId: conversation.id,
channel: address.channelId,
userId: user.id,
userName: user.name
};
client.trackEvent(Events.ReceiveMessage.name, item);
}
catch (e) {
}
finally {
next();
}
},
send: function (message, next) {
try {
var b = bot;
client.trackEvent(Events.SendMessage.name, {
text: message.text,
type: message.type,
timestamp: (new Date()).toISOString(),
conversationId: message.address && message.address.conversation && message.address.conversation.id
});
}
catch (e) {
}
finally {
next();
}
}
});
}
// Monitoring new dialog calls like session.beginDialog
// When beginning a new dialog, the framework uses pushDialog to change context
// to a new dialog
// Todo: Check alternative as <builder.SimpleDialog.prototype.begin>
builder.Session.prototype.pushDialog = (function () {
var orig = builder.Session.prototype.pushDialog;
return function (args) {
var _session = this;
var _message = _session.message || {};
var _address = _message.address || {};
var _conversation = _address.conversation || {};
var _user = _address.user || {};
var _callstack = _session.sessionState.callstack;
var item = {
intent: args && args.id,
state: args && args.state && JSON.stringify(args.state),
channel: _address.channelId,
conversationId: _conversation.id,
callstack_length: _callstack.length.toString(),
userId: _user.id,
userName: _user.name
};
_.take(_callstack, 3).forEach((stackItem, idx) => {
item[`callstack_${idx}_id`] = stackItem.id;
item[`callstack_${idx}_state`] = JSON.stringify(stackItem.state);
});
client.trackEvent(Events.Intents.name, item);
orig.apply(_session, [args]);
};
})();
// Capture message session before send
builder.Session.prototype.prepareMessage = (function () {
var orig = builder.Session.prototype.prepareMessage;
return function (msg) {
var _session = this;
var res = orig.apply(_session, [msg]);
if (_session.dialogData['transaction.started']) {
var transactionEnded = false;
var success = false;
var conversation = _.find(transactions, { intent: _session.dialogData['transaction.id'] });
if (conversation.intent != _session.dialogData['BotBuilder.Data.Intent']) {
transactionEnded = true;
}
else {
var test = conversation.test;
var success = typeof test == 'string' ? test == msg.text : test.test(msg.text);
if (success) {
transactionEnded = true;
}
}
if (transactionEnded) {
endConverting(_session, null, success);
delete _session.dialogData['transaction.started'];
delete _session.dialogData['transaction.id'];
}
}
return res;
};
})();
// Collect intents collected from LUIS after entities were resolved
builder.IntentDialog.prototype.recognize = (function () {
var _recognize = builder.IntentDialog.prototype.recognize;
return function (context, cb) {
var _dialog = this;
_recognize.apply(_dialog, [context, (err, result) => {
var entities = [];
if (result && result.entities) {
result.entities.forEach(value => {
entities.push({
type: value.type,
entity: value.entity
});
});
}
var message = context.message;
var address = message.address || {};
var conversation = address.conversation || {};
var user = address.user || {};
var item = {
text: message.text,
timestamp: message.timestamp,
intent: result && result.intent,
channel: address.channelId,
score: result && result.score,
entities: entities,
withError: !err,
error: err,
conversationId: conversation.id,
userId: user.id,
userName: user.name
};
client.trackEvent("message.intent.received", item);
transactions.forEach(cc => {
if (cc.intent == item.intent) {
startConverting(context, null);
context.dialogData['transaction.started'] = true;
context.dialogData['transaction.id'] = cc.intent;
}
});
collectSentiment(context, message.text);
// Todo: on "set alarm" utterence, failiure
return cb(err, result);
}]);
};
})();
};
var collectSentiment = (session, text) => {
text = text || '';
if (!_sentimentKey)
return;
if (text.match(/\S+/g).length < _sentimentMinWords)
return;
var _message = session.message || {};
var _address = _message.address || {};
var _conversation = _address.conversation || {};
var _user = _address.user || {};
request({
url: _sentimentUrl,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': _sentimentKey
},
json: true,
body: {
"documents": [
{
"language": "en",
"id": _sentimentId,
"text": text
}
]
}
}, (error, response, body) => {
if (error) {
return client.trackException(error);
}
try {
var score = _.find(body.documents, { id: _sentimentId }).score;
if (isNaN(score)) {
throw new Error('Could not collect sentiment');
}
var item = {
text: text,
score: score,
timestamp: _message.timestamp,
channel: _address.channelId,
conversationId: _conversation.id,
userId: _user.id,
userName: _user.name
};
client.trackEvent(Events.Sentiment.name, item);
}
catch (error) {
return client.trackException(error);
}
});
};
var startConverting = (session, name) => {
name = name || 'default';
var _message = session.message || {};
var _address = _message.address || {};
var _conversation = _address.conversation || {};
var _user = _address.user || {};
var item = {
name,
timestamp: _message.timestamp,
channel: _address.channelId,
conversationId: _conversation.id,
userId: _user.id,
userName: _user.name
};
client.trackEvent(Events.ConversionStarted.name, item);
};
var endConverting = (session, name, successful, count) => {
name = name || 'default';
count = isNaN(count) && 1 || count;
successful = successful !== false;
var _message = session.message || {};
var _address = _message.address || {};
var _conversation = _address.conversation || {};
var _user = _address.user || {};
var _callstack = session.sessionState.callstack;
var item = {
name,
successful: successful.toString(),
count: count.toString(),
timestamp: _message.timestamp,
channel: _address.channelId,
conversationId: _conversation.id,
callstack_length: _callstack.length.toString(),
userId: _user.id,
userName: _user.name
};
client.trackEvent(Events.ConversionEnded.name, item);
};
var measure = (session, name, count) => {
name = name || 'default';
count = count || 1;
var _message = session.message || {};
var _address = _message.address || {};
var _conversation = _address.conversation || {};
var _user = _address.user || {};
var _callstack = session.sessionState.callstack;
var item = {
timestamp: _message.timestamp,
channel: _address.channelId,
conversationId: _conversation.id,
callstack_length: _callstack.length.toString(),
userId: _user.id,
userName: _user.name
};
client.trackEvent('custom-' + name, item, { count });
};
module.exports = {
monitor,
measure
};

19
main.js → dist/main.js поставляемый
Просмотреть файл

@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
const util = require("util");
const _ = require("lodash");
const builder = require("botbuilder");
const request_1 = require("request");
const request = require("request");
const ApplicationInsights = require("applicationinsights");
const events_1 = require("./events");
class BotFrameworkInstrumentation {
@ -71,7 +71,7 @@ class BotFrameworkInstrumentation {
var _address = _message.address || {};
var _conversation = _address.conversation || {};
var _user = _address.user || {};
request_1.default({
request({
url: this.settings.sentiments.url,
method: 'POST',
headers: {
@ -115,13 +115,17 @@ class BotFrameworkInstrumentation {
});
}
monitor(bot) {
ApplicationInsights.setup(this.settings.instrumentationKey).start();
ApplicationInsights.setup(this.settings.instrumentationKey)
.setAutoCollectConsole(true)
.setAutoCollectExceptions(true)
.setAutoCollectRequests(true)
.start();
this.appInsightsClient = ApplicationInsights.getClient(this.settings.instrumentationKey);
this.setupConsoleCollection();
//this.setupConsoleCollection();
// Adding middleware to intercept all user messages
if (bot) {
bot.use({
botbuilder: function (session, next) {
botbuilder: (session, next) => {
try {
let message = session.message;
let address = message.address || {};
@ -224,6 +228,7 @@ class BotFrameworkInstrumentation {
// }
// })();
// Collect intents collected from LUIS after entities were resolved
let self = this;
builder.IntentDialog.prototype.recognize = (() => {
let _recognize = builder.IntentDialog.prototype.recognize;
return function (context, cb) {
@ -255,7 +260,7 @@ class BotFrameworkInstrumentation {
userId: user.id,
userName: user.name
};
this.appInsightsClient.trackEvent(events_1.default.Intent.name, item);
self.appInsightsClient.trackEvent(events_1.default.Intent.name, item);
// transactions.forEach(cc => {
// if (cc.intent == item.intent) {
// startConverting(context, null);
@ -263,7 +268,7 @@ class BotFrameworkInstrumentation {
// context.dialogData['transaction.id'] = cc.intent;
// }
// });
this.collectSentiment(context, message.text);
self.collectSentiment(context, message.text);
// Todo: on "set alarm" utterence, failiure
return cb(err, result);
}]);

Просмотреть файл

@ -1,8 +1,8 @@
{
"name": "botbuilder-instrumentation",
"version": "1.0.6",
"version": "1.0.7",
"description": "",
"main": "main.js",
"main": "dist/main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,7 +1,7 @@
import * as util from 'util';
import * as _ from 'lodash';
import * as builder from 'botbuilder';
import request from 'request';
import * as request from 'request';
import ApplicationInsights = require("applicationinsights");
import Events from './events';
@ -151,15 +151,19 @@ export class BotFrameworkInstrumentation {
monitor (bot: builder.UniversalBot) {
ApplicationInsights.setup(this.settings.instrumentationKey).start();
ApplicationInsights.setup(this.settings.instrumentationKey)
.setAutoCollectConsole(true)
.setAutoCollectExceptions(true)
.setAutoCollectRequests(true)
.start();
this.appInsightsClient = ApplicationInsights.getClient(this.settings.instrumentationKey);
this.setupConsoleCollection();
//this.setupConsoleCollection();
// Adding middleware to intercept all user messages
if (bot) {
bot.use({
botbuilder: function (session, next) {
botbuilder: (session, next) => {
try {
let message: any = session.message;
@ -278,6 +282,7 @@ export class BotFrameworkInstrumentation {
// Collect intents collected from LUIS after entities were resolved
let self = this;
builder.IntentDialog.prototype.recognize = (() => {
let _recognize = builder.IntentDialog.prototype.recognize;
return function(context, cb) {
@ -314,7 +319,7 @@ export class BotFrameworkInstrumentation {
userName: user.name
};
this.appInsightsClient.trackEvent(Events.Intent.name, item);
self.appInsightsClient.trackEvent(Events.Intent.name, item);
// transactions.forEach(cc => {
// if (cc.intent == item.intent) {
@ -324,7 +329,7 @@ export class BotFrameworkInstrumentation {
// }
// });
this.collectSentiment(context, message.text);
self.collectSentiment(context, message.text);
// Todo: on "set alarm" utterence, failiure
return cb(err, result);

Просмотреть файл

@ -3,15 +3,17 @@
"module": "commonjs",
"target": "es6",
"allowJs": true,
"outDir": "dist",
"rootDir": "src",
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": false,
"noImplicitAny": false,
"checkJs": true
"noImplicitAny": false
},
"exclude": [
"node_modules"
"node_modules",
"dist"
],
"types": [
"node_modules/@types"