Skip to content

Commit

Permalink
Smarti 0.6.1 (#147)
Browse files Browse the repository at this point in the history
* WIP: Proxy for Smarti stub

* WIP: Proxy for Smarti stub added to package

```
curl -X POST \
  http://localhost:3000/api/v1/assistify/smarti/conversation/ \
  -H 'content-type: application/json' \
  -H 'x-auth-token: EKZ2_t7Wfqg5GML8bs44v3uS0d_b7f3rUd9IUYKNpvJ' \  //from login
  -H 'x-user-id: S4Mew2PDtEPr3Ejne' \                               //from login
  -d '{
	"thisIsMy":"JSON-Body"
}'
```

* use injected logger for logging in API

* Fixes #151 - Misspelled label "jetzt chaten"

* Using the configured access token as HTTP header

* Using the RC proxy for Smarti

* Revert "Feature/#23 title first message to new request (#149)"

This reverts commit 484b04c.

* Revert "Fixes #151 - Misspelled label "jetzt chaten""

This reverts commit 0c9ac4f.

* Fixing lint errors.

* Fixing non authorized access.

* fixed issue with wrong knowledge provider indexes

* improved tailing slashes for URLs

* - add authorization checks before routing the API calls
- roll back injected logger, since it is not defined

* removed unused localization keys

* Reducing code for adding a tailing slash to the Smarti URL.

* consolidate constants naming

* - Use RateLimiter for Smarti requests
- Use propagation function for 'onMessage' and 'onClose'
- Make Smarti the default knowledge provider
- Reordered Smarti backend settings
- Added descriptions for Smarti backend settings
- Also reload the settings, when reloading the Smarti widget
- Refactored file names

* Using rate limiter in each proxy method and limit the propagateToSmarti function instead of HTTP.call

* - merged proxy and adapter into only one file
- only use DDP for Smarti Widget / Rocket.Chat messages (getConversation, getSmartiQueryBuilderResult)
- added proxy endpoint for Smarti conversation search
- added several comments

* Migrate settings

* Extract Smarti loader

* additional migrations

* Do not migrate old settings with other defaults

* separated responsibilities

* Simplify adapter, proxy, router, widgetBackend

* Minor fixes to get it work with Smarti.

* Fix Webhook-token not being transmitted

* revert unintentional changes to other files after branching off the wron state.
Copied all changes from `develop` which were not included in `assistify:ai`-package
  • Loading branch information
mrsimpson authored and ruKurz committed Dec 13, 2017
1 parent 6e7a4ec commit 24882e1
Show file tree
Hide file tree
Showing 16 changed files with 471 additions and 417 deletions.
25 changes: 25 additions & 0 deletions packages/assistify-ai/client/smartiLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* globals RocketChat */

/**
* Load Smarti script asynchronously to the window.
* This ensures the invalidation as settings are changed and allow the script to live beyond template lifetime.
*/
RocketChat.settings.onload('Assistify_AI_Smarti_Base_URL', function() {
Meteor.call('getSmartiUiScript', function(error, script) {
if (error) {
console.error('could not load Smarti:', error.message);
} else {
// generate a script tag for smarti JS
const doc = document;
const smartiScriptTag = doc.createElement('script');
smartiScriptTag.type = 'text/javascript';
smartiScriptTag.async = true;
smartiScriptTag.defer = true;
smartiScriptTag.innerHTML = script;
// insert the smarti script tag as first script tag
const firstScriptTag = doc.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(smartiScriptTag, firstScriptTag);
console.debug('loaded Smarti successfully');
}
});
});
58 changes: 7 additions & 51 deletions packages/assistify-ai/client/views/app/tabbar/smarti.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* globals TAPi18n */
/* globals TAPi18n, RocketChat */

Template.AssistifySmarti.onCreated(function() {
this.helpRequest = new ReactiveVar(null);
Expand Down Expand Up @@ -26,6 +26,7 @@ Template.AssistifySmarti.onDestroyed(function() {

/**
* Create Smarti (as soon as the script is loaded)
* @namespace SmartiWidget
*/
Template.AssistifySmarti.onRendered(function() {

Expand All @@ -45,46 +46,23 @@ Template.AssistifySmarti.onRendered(function() {
}
} else {
instance.smartiLoaded.set(true);
const DBS_AI_Redlink_URL =
RocketChat.settings.get('DBS_AI_Redlink_URL').endsWith('/') ?
RocketChat.settings.get('DBS_AI_Redlink_URL') :
`${ RocketChat.settings.get('DBS_AI_Redlink_URL') }/`;

const SITE_URL_W_SLASH =
RocketChat.settings.get('Site_Url').endsWith('/') ?
RocketChat.settings.get('Site_Url') :
`${ RocketChat.settings.get('Site_Url') }/`;

const ROCKET_CHAT_URL = RocketChat.settings.get('Site_Url').replace(/\/?$/, '/');
// stripping only the protocol ("http") from the site-url either creates a secure or an insecure websocket connection
const WEBSOCKET_URL = `ws${ SITE_URL_W_SLASH.substring(4) }websocket/`;

let customSuffix = RocketChat.settings.get('Assistify_AI_DBSearch_Suffix') || '';
customSuffix = customSuffix.replace(/\r\n|\r|\n/g, '');

const WEBSOCKET_URL = `ws${ ROCKET_CHAT_URL.substring(4) }websocket/`;
const WIDGET_POSTING_TYPE = RocketChat.settings.get('Assistify_AI_Widget_Posting_Type') || 'postRichText';
console.log(WIDGET_POSTING_TYPE, RocketChat.settings.get('Assistify_AI_Widget_Posting_Type'));
const SMARTI_CLIENT_NAME = RocketChat.settings.get('Assistify_AI_Smarti_Domain');

const smartiOptions = {
socketEndpoint: WEBSOCKET_URL,
smartiEndpoint: DBS_AI_Redlink_URL,
clientName: SMARTI_CLIENT_NAME,
channel: instance.data.rid,
postings: {
type: WIDGET_POSTING_TYPE,
cssInputSelector: '.rc-message-box .js-input-message'
},
widget: {
'query.dbsearch': {
numOfRows: 2,
suffix: customSuffix
},
'query.dbsearch.keyword': {
numOfRows: 2,
suffix: customSuffix,
disabled: true
}
},
lang: 'de'
};
console.debug('Initializing Smarti with options: ', JSON.stringify(smartiOptions, null, 2));
instance.smarti = new window.SmartiWidget(instance.find('.smarti-widget'), smartiOptions);
}
}
Expand Down Expand Up @@ -131,25 +109,3 @@ Template.AssistifySmarti.helpers({
}
});

/**
* Load Smarti script
*/
RocketChat.settings.onload('DBS_AI_Redlink_URL', function() {
Meteor.call('getSmartiUiScript', function(error, script) {
if (error) {
console.error('could not load Smarti:', error.message);
} else {
// generate a script tag for smarti JS
const doc = document;
const smartiScriptTag = doc.createElement('script');
smartiScriptTag.type = 'text/javascript';
smartiScriptTag.async = true;
smartiScriptTag.defer = true;
smartiScriptTag.innerHTML = script;
// insert the smarti script tag as first script tag
const firstScriptTag = doc.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(smartiScriptTag, firstScriptTag);
console.debug('loaded Smarti successfully');
}
});
});
41 changes: 24 additions & 17 deletions packages/assistify-ai/config.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
/* globals RocketChat */

Meteor.startup(() => {
const addAISettings = function() {

this.section('Knowledge_Base', function() {

this.add('DBS_AI_Enabled', false, {
this.add('Assistify_AI_Enabled', false, {
type: 'boolean',
public: true,
i18nLabel: 'Enabled'
});

this.add('DBS_AI_Source', '', {
this.add('Assistify_AI_Source', '0', {
type: 'select',
values: [
{key: '0', i18nLabel: 'DBS_AI_Source_APIAI'},
{key: '1', i18nLabel: 'DBS_AI_Source_Redlink'},
{key: '2', i18nLabel: 'DBS_AI_Source_Smarti'}
{key: '0', i18nLabel: 'Assistify_AI_Source_Smarti'},
{key: '1', i18nLabel: 'Assistify_AI_Source_APIAI'}
],
public: true,
i18nLabel: 'DBS_AI_Source'
i18nLabel: 'Assistify_AI_Source'
});

this.add('DBS_AI_Redlink_URL', '', {
type: 'string',
public: true,
i18nLabel: 'DBS_AI_Redlink_URL'
this.add('Assistify_AI_Reload', 'reloadSmarti', {
type: 'action',
actionText: 'Reload_Settings'
});

this.add('DBS_AI_Redlink_Hook_Token', '', {
this.add('Assistify_AI_Smarti_Base_URL', '', {
type: 'string',
public: true,
i18nLabel: 'DBS_AI_Redlink_Hook_Token'
i18nLabel: 'Assistify_AI_Smarti_Base_URL'
});

let domain = RocketChat.settings.get('Site_Url');
Expand All @@ -41,10 +41,16 @@ Meteor.startup(() => {
domain = domain.substr(0, domain.length - 1);
}
}
this.add('DBS_AI_Redlink_Domain', domain, {
this.add('Assistify_AI_Smarti_Domain', domain, {
type: 'string',
public: true,
i18nLabel: 'DBS_AI_Redlink_Domain'
i18nLabel: 'Assistify_AI_Smarti_Domain'
});

this.add('Assistify_AI_Smarti_Auth_Token', '', {
type: 'string',
public: true,
i18nLabel: 'Assistify_AI_Smarti_Auth_Token'
});

this.add('Assistify_AI_Widget_Posting_Type', '', {
Expand All @@ -58,9 +64,10 @@ Meteor.startup(() => {
i18nLabel: 'Assistify_AI_Widget_Posting_Type'
});

this.add('reload_Assistify', 'reloadSmarti', {
type: 'action',
actionText: 'Reload_Settings'
this.add('Assistify_AI_RocketChat_Webhook_Token', '', {
type: 'string',
public: true,
i18nLabel: 'Assistify_AI_RocketChat_Webhook_Token'
});
});
};
Expand Down
8 changes: 8 additions & 0 deletions packages/assistify-ai/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ Package.onUse(function(api) {
addDirectory(api, 'server/hooks', 'server');
addDirectory(api, 'server/methods', 'server');

// Smarti proxy and router
api.addFiles('server/SmartiProxy.js', 'server');
api.addFiles('server/SmartiRouter.js', 'server');

//migration scripts
api.addFiles('server/migrations.js', 'server');

//Configuration
api.addFiles('config.js', 'server');

Expand All @@ -36,6 +43,7 @@ Package.onUse(function(api) {

//client views
addDirectory(api, 'client/views/app/tabbar', 'client');
api.addFiles('client/smartiLoader.js', 'client');

//styling
api.addFiles('client/public/stylesheets/smarti.css', 'client');
Expand Down
54 changes: 54 additions & 0 deletions packages/assistify-ai/server/SmartiProxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* globals SystemLogger, RocketChat */

/** The HTTP methods. */
export const verbs = {
get: 'GET',
post: 'POST',
put: 'PUT',
delete: 'DELETE'
};

/**
* The proxy propagates the HTTP requests to Smarti.
* All HTTP outbound traffic (from Rocket.Chat to Smarti) should pass the this proxy.
*/
export class SmartiProxy {

static get smartiAuthToken() {
return RocketChat.settings.get('Assistify_AI_Smarti_Auth_Token');
}

static get smartiUrl() {
return RocketChat.settings.get('Assistify_AI_Smarti_Base_URL');
}

/**
* Propagates requests to Smarti.
* Make sure all requests to Smarti are using this function.
*
* @param {String} method - the HTTP method to use
* @param {String} path - the path to call
* @param {Object} [body=null] - the payload to pass (optional)
*
* @returns {Object}
*/
static propagateToSmarti(method, path, body = null) {

const url = `${ SmartiProxy.smartiUrl }${ path }`;
const header = {
'X-Auth-Token': SmartiProxy.smartiAuthToken,
'Content-Type': 'application/json; charset=utf-8'
};
try {
const response = HTTP.call(method, url, {data: body, headers: header});
if (response.statusCode === 200) {
return response.data || response.content; //.data if it's a json-response
} else {
SystemLogger.debug('Got unexpected result from Smarti', method, 'to', url, 'response', JSON.stringify(response));
}
} catch (error) {
SystemLogger.error('Could not complete', method, 'to', url);
SystemLogger.debug(error);
}
}
}
36 changes: 36 additions & 0 deletions packages/assistify-ai/server/SmartiRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* globals SystemLogger, RocketChat */

import {SmartiAdapter} from './lib/SmartiAdapter';

/**
* The SmartiRouter handles all incoming HTTP requests from Smarti.
* This is the only place, where adding routes to the Rocket.Chat API, related to Smarti
* All HTTP inbound traffic (from Rocket.Chat to Smarti) should pass the this router.
*/

/**
* Add an incoming webhook '/newConversationResult' to receive answers from Smarti.
* This allows asynchronous callback from Smarti, when analyzing the conversation has finished.
*/
RocketChat.API.v1.addRoute('smarti.result/:_token', {authRequired: false}, {

post() {

check(this.bodyParams, Match.ObjectIncluding({
conversationId: String,
channelId: String
}));

const rcWebhookToken = RocketChat.settings.get('Assistify_AI_RocketChat_Webhook_Token');

//verify token
if (this.urlParams._token && this.urlParams._token === rcWebhookToken) {

SystemLogger.debug('Smarti - got conversation result:', JSON.stringify(this.bodyParams, null, 2));
SmartiAdapter.analysisCompleted(this.bodyParams.channelId, this.bodyParams.conversationId, this.bodyParams.token);
return RocketChat.API.v1.success();
} else {
return RocketChat.API.v1.unauthorized({msg: 'token not valid'});
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
}

let knowledgeEnabled = false;
RocketChat.settings.get('DBS_AI_Enabled', function(key, value) {
RocketChat.settings.get('Assistify_AI_Enabled', function(key, value) {
knowledgeEnabled = value;
});

Expand All @@ -37,4 +37,4 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
});

return message;
}, RocketChat.callbacks.priority.LOW, 'dbsAI_OnMessage');
}, RocketChat.callbacks.priority.LOW, 'Assistify_AI_OnMessage');
30 changes: 5 additions & 25 deletions packages/assistify-ai/server/lib/KnowledgeAdapterProvider.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* globals RocketChat */

import {SmartiAdapterFactory} from './Smarti';
import {SmartiAdapter} from './SmartiAdapter';
import {ApiAiAdapter} from './AiApiAdapter';

export function getKnowledgeAdapter() {
let knowledgeSource = '';

const KNOWLEDGE_SRC_APIAI = '0';
const KNOWLEDGE_SRC_SMARTI = '2';
const KNOWLEDGE_SRC_SMARTI = '0';
const KNOWLEDGE_SRC_APIAI = '1';

RocketChat.settings.get('DBS_AI_Source', function(key, value) {
RocketChat.settings.get('Assistify_AI_Source', function(key, value) {
knowledgeSource = value;
});

Expand All @@ -29,28 +29,8 @@ export function getKnowledgeAdapter() {
RocketChat.settings.get('Assistify_AI_Apiai_Language', function(key, value) {
adapterProps.language = value;
});

return new ApiAiAdapter(adapterProps);
case KNOWLEDGE_SRC_SMARTI:
return SmartiAdapterFactory.getInstance(); // buffering done inside the factory method
return SmartiAdapter;
}
}


/**
* Refreshes the adapter instances on change of the configuration - the redlink-adapter factory does that on its own
*/
//todo: refresh adapter instances on change of the configuration. Observe a raw cursor
// Meteor.autorun(()=> {
// RocketChat.settings.get('DBS_AI_Source', function(key) {
// _dbs.apiaiAdapter = undefined;
// });
//
// RocketChat.settings.get('Assistify_AI_Apiai_Key', function(key) {
// _dbs.apiaiAdapter = undefined;
// });
//
// RocketChat.settings.get('Assistify_AI_Apiai_Language', function(key) {
// _dbs.apiaiAdapter = undefined;
// });
// });
Loading

0 comments on commit 24882e1

Please sign in to comment.