forked from RocketChat/Rocket.Chat
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from mrsimpson/assistify_0
Preparation for PoC Assistify.
- Loading branch information
Showing
111 changed files
with
5,962 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Write your package code here! | ||
|
||
// Variables exported by this module can be imported by other packages and | ||
// applications. See bot-tests.js for an example of importing. | ||
export const name = 'bot'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
Meteor.startup(()=> { | ||
RocketChat.settings.add('Assistify_Bot_Username', "", { | ||
group: 'Assistify', | ||
i18nLabel: 'Assistify_Bot_Username', | ||
type: 'string', | ||
section: 'Bot', | ||
public: true | ||
}); | ||
|
||
RocketChat.settings.add('Assistify_Bot_Automated_Response_Threshold', 50, { | ||
group: 'Assistify', | ||
i18nLabel: 'Assistify_Bot_Automated_Response_Threshold', | ||
type: 'int', | ||
section: 'Bot', | ||
public: true | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
Package.describe({ | ||
name: 'assistify:bot', | ||
version: '0.0.1', | ||
// Brief, one-line summary of the package. | ||
summary: 'Adds a bot which propagates AI-results', | ||
// URL to the Git repository containing the source code for this package. | ||
git: '', | ||
// By default, Meteor will default to using README.md for documentation. | ||
// To avoid submitting documentation, set this field to null. | ||
documentation: 'README.md' | ||
}); | ||
|
||
Package.onUse(function (api) { | ||
api.versionsFrom('1.4.2.6'); | ||
api.use('ecmascript'); | ||
api.use('assistify:help-request'); | ||
api.mainModule('bot.js'); | ||
|
||
//Server | ||
api.addFiles('config.js', 'server'); | ||
|
||
//hooks | ||
api.addFiles('server/hooks/onKnowledgeProviderResult.js', 'server'); | ||
|
||
//i18n in Rocket.Chat-package (packages/rocketchat-i18n/i18n | ||
}); |
67 changes: 67 additions & 0 deletions
67
packages/assistify-bot/server/hooks/onKnowledgeProviderResult.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
Meteor.startup(() => { | ||
/* | ||
Trigger a bot to reply with the most relevant result one AI has retrieved results | ||
Do this only once in order to avoid user-frustration | ||
*/ | ||
RocketChat.callbacks.add('afterExternalMessage', function (externalMessage) { | ||
|
||
const helpRequest = RocketChat.models.HelpRequests.findOneByRoomId(externalMessage.rid); | ||
if(!helpRequest || helpRequest.latestBotReply){ | ||
return; | ||
} | ||
|
||
let totalResults = []; | ||
|
||
if (externalMessage.result && externalMessage.result.queryTemplates) { | ||
externalMessage.result.queryTemplates.forEach((queryTemplate, templateIndex) => { | ||
queryTemplate.queries.forEach((query) => { | ||
if (query.inlineResultSupport) { | ||
const results = _dbs.getKnowledgeAdapter().getQueryResults(externalMessage.rid, templateIndex, query.creator); | ||
if (results) { | ||
totalResults = totalResults.concat( | ||
results.map((result)=> { | ||
return { | ||
overallScore: query.confidence * (result.score || 1), | ||
replySuggestion: result.replySuggestion | ||
} | ||
}) | ||
); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
if (totalResults.length > 0) { | ||
// AI believes it can contribute to the conversation => create a bot-response | ||
const mostRelevantResult = totalResults.reduce((best, current) => current.overallScore > best.overallScore ? current : best, | ||
{ | ||
overallScore: 0, | ||
replySuggestion: "" | ||
}); | ||
const scoreThreshold = RocketChat.settings.get('Assistify_Bot_Automated_Response_Threshold'); | ||
if(mostRelevantResult.replySuggestion && (mostRelevantResult.overallScore * 1000) >= scoreThreshold ) { //multiply by 1000 to simplify configuration | ||
const botUsername = RocketChat.settings.get('Assistify_Bot_Username'); | ||
const botUser = RocketChat.models.Users.findOneByUsername(botUsername); | ||
|
||
if (!botUser) { | ||
throw new Meteor.Error('Erroneous Bot-Configuration: Check username') | ||
} | ||
try { | ||
|
||
const botMessage = RocketChat.sendMessage({ | ||
username: botUser.username, | ||
_id: botUser._id | ||
}, {msg: mostRelevantResult.replySuggestion}, {_id: externalMessage.rid}); | ||
|
||
helpRequest.latestBotReply = botMessage; | ||
RocketChat.models.HelpRequests.registerBotResponse(helpRequest._id, botMessage); | ||
|
||
} catch (err) { | ||
console.error('Could not add bot help message', err); | ||
throw new Meteor.Error(err); | ||
} | ||
} | ||
} | ||
} | ||
}, RocketChat.callbacks.priority.LOW) | ||
}); |
Empty file.
40 changes: 40 additions & 0 deletions
40
packages/assistify-help-request/assets/stylesheets/helpRequestContext.less
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
.assistify-create-section { | ||
margin-bottom: 30px; | ||
|
||
.input-submit { | ||
margin-top: 5px; | ||
} | ||
} | ||
.help-request-context { | ||
padding: 15px; | ||
|
||
.title { | ||
background-color: #ccc; | ||
font-weight: 700; | ||
padding: 10px; | ||
font-size: 15px; | ||
color: #757575 | ||
} | ||
|
||
.context-parameters-wrapper { | ||
border: solid; | ||
border-width: 1px; | ||
border-color: #CCCCCC; | ||
|
||
.context-parameter { | ||
margin: 0; | ||
border: 0; | ||
border-color: #ccc; | ||
border-top: 1px solid #ccc; | ||
font-size: 100%; | ||
vertical-align: baseline; | ||
background-color: white; | ||
padding: 10px 15px; | ||
font-style: italic; | ||
|
||
.value { | ||
font-weight: bold; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* Makes the knowledge base panel open on opening a room in which it is active | ||
* (a request, an expertise or a livechat) | ||
*/ | ||
RocketChat.callbacks.add('enter-room', function(subscription){ | ||
const roomOpened = RocketChat.models.Rooms.findOne({_id: subscription.rid}); | ||
if(roomOpened.t === 'r' || roomOpened.t === 'e' || roomOpened.t === 'l'){ | ||
$('.flex-tab-container:not(.opened) .flex-tab-bar .hidden .icon-lightbulb').click(); //there is no ID of the tabbar's Button which we could use so far | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
var expertiseSearchCache = new Meteor.Collection(null); |
8 changes: 8 additions & 0 deletions
8
packages/assistify-help-request/client/views/sideNav/AssistifyCreateChannel.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<template name="AssistifyCreateChannel"> | ||
<div class="wrapper assistify-create-section"> | ||
<h4>{{_ "Assistify" }}</h4> | ||
{{> AssistifyCreateRequest}} | ||
<div style="min-height: 30px"></div> | ||
{{> AssistifyCreateExpertise}} | ||
</div> | ||
</template> |
27 changes: 27 additions & 0 deletions
27
packages/assistify-help-request/client/views/sideNav/AssistifyCreateExpertise.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<template name="AssistifyCreateExpertise"> | ||
{{#if hasPermission 'create-e'}} | ||
<div class="input-line no-icon"> | ||
<span>{{_ "Expertise"}}</span> | ||
<input type="text" id="expertise" dir="auto" placeholder="{{_ 'Enter_name_here'}}"> | ||
</div> | ||
|
||
<div class="input-line no-icon"> | ||
<label class="color-tertiary-font-color" for="experts">{{_ "Select_users"}}</label> | ||
{{> inputAutocomplete settings=autocompleteSettings id="experts" class="search" placeholder=(_ "Search_by_username") autocomplete="off"}} | ||
{{#if selectedUsers}} | ||
<ul class="selected-users"> | ||
{{#each selectedUsers}} | ||
<li class="background-transparent-darker">{{.}} <i | ||
class="icon-cancel remove-expert"></i> | ||
</li> | ||
{{/each}} | ||
</ul> | ||
{{/if}} | ||
</div> | ||
|
||
<div class="input-submit"> | ||
<button class="button primary save-expertise">{{_ "Create" }}</button> | ||
<button class="button cancel-expertise">{{_ "Cancel" }}</button> | ||
</div> | ||
{{/if}} | ||
</template> |
127 changes: 127 additions & 0 deletions
127
packages/assistify-help-request/client/views/sideNav/AssistifyCreateExpertise.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import toastr from 'toastr'; | ||
|
||
Template.AssistifyCreateExpertise.helpers({ | ||
selectedUsers: function () { | ||
const instance = Template.instance(); | ||
return instance.selectedUsers.get(); | ||
}, | ||
autocompleteSettings: function () { | ||
return { | ||
limit: 10, | ||
// inputDelay: 300 | ||
rules: [ | ||
{ | ||
// @TODO maybe change this 'collection' and/or template | ||
collection: 'UserAndRoom', | ||
subscription: 'userAutocomplete', | ||
field: 'username', | ||
template: Template.userSearch, | ||
noMatchTemplate: Template.userSearchEmpty, | ||
matchAll: true, | ||
filter: { | ||
exceptions: Template.instance().selectedUsers.get() | ||
}, | ||
selector(match) { | ||
return {term: match}; | ||
}, | ||
sort: 'username' | ||
} | ||
] | ||
}; | ||
} | ||
}); | ||
|
||
Template.AssistifyCreateExpertise.events({ | ||
'autocompleteselect #experts'(event, instance, doc) { | ||
instance.selectedUsers.set(instance.selectedUsers.get().concat(doc.username)); | ||
|
||
instance.selectedUserNames[doc.username] = doc.name; | ||
|
||
event.currentTarget.value = ''; | ||
return event.currentTarget.focus(); | ||
}, | ||
|
||
'click .remove-expert'(e, instance) { | ||
let self = this; | ||
|
||
let users = instance().selectedUsers.get(); | ||
users = _.reject(instance().selectedUsers.get(), _id => _id === self.valueOf()); | ||
|
||
instance().selectedUsers.set(users); | ||
|
||
return $('#experts').focus(); | ||
}, | ||
|
||
|
||
'keyup #expertise': function (e, instance) { | ||
if (e.keyCode == 13) { | ||
instance.$('#experts').focus(); | ||
} | ||
}, | ||
|
||
'keydown #channel-members'(e, instance) { | ||
if (($(e.currentTarget).val() === '') && (e.keyCode === 13)) { | ||
return instance.$('.save-channel').click(); | ||
} | ||
}, | ||
|
||
'click .cancel-expertise': function (event, instance) { | ||
SideNav.closeFlex(() => { | ||
instance.clearForm() | ||
}); | ||
}, | ||
|
||
'click .save-expertise': function (event, instance) { | ||
event.preventDefault(); | ||
const name = instance.find('#expertise').value.toLowerCase().trim(); | ||
instance.expertiseRoomName.set(name); | ||
|
||
if (name) { | ||
Meteor.call('createExpertise', name, instance.selectedUsers.get(), (err, result) => { | ||
if (err) { | ||
console.log(err); | ||
switch (err.error) { | ||
case 'error-invalid-name': | ||
toastr.error(TAPi18n.__('Invalid_room_name', name)); | ||
return; | ||
case 'error-duplicate-channel-name': | ||
toastr.error(TAPi18n.__('Duplicate_channel_name', name)); | ||
return; | ||
case 'error-archived-duplicate-name': | ||
toastr.error(TAPi18n.__('Duplicate_archived_channel_name', name)); | ||
return; | ||
case 'error-no-members': | ||
toastr.error(TAPi18n.__('Expertise_needs_experts', name)); | ||
return; | ||
default: | ||
return handleError(err) | ||
} | ||
} | ||
|
||
// we're done, so close side navigation and navigate to created request-channel | ||
SideNav.closeFlex(() => { | ||
instance.clearForm() | ||
}); | ||
RocketChat.callbacks.run('aftercreateCombined', {_id: result.rid, name: name}); | ||
FlowRouter.go('expertise', {name: name}, FlowRouter.current().queryParams); | ||
}); | ||
} else { | ||
console.log(err); | ||
toastr.error(TAPi18n.__('The_field_is_required', TAPi18n.__('expertise'))); | ||
} | ||
} | ||
}); | ||
|
||
Template.AssistifyCreateExpertise.onCreated(function () { | ||
const instance = this; | ||
instance.expertiseRoomName = new ReactiveVar(''); | ||
instance.selectedUsers = new ReactiveVar([]); | ||
instance.selectedUserNames = {}; | ||
|
||
instance.clearForm = function () { | ||
instance.expertiseRoomName.set(''); | ||
instance.selectedUsers.set([]); | ||
instance.find('#expertise').value = ''; | ||
instance.find('#experts').value = ''; | ||
}; | ||
}); |
13 changes: 13 additions & 0 deletions
13
packages/assistify-help-request/client/views/sideNav/AssistifyCreateRequest.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<template name="AssistifyCreateRequest"> | ||
<label class="color-tertiary-font-color" for="expertise-search">{{_ "New_request_for_expertise"}}</label> | ||
{{> inputAutocomplete settings=autocompleteExpertiseSettings id="expertise-search" class="search" autocomplete=off value=expertise}} | ||
<!--Placeholder: Seperate field to enter any name--> | ||
<!--<div class="input-line no-icon">--> | ||
<!--<span>{{_ "Request"}}</span>--> | ||
<!--<input type="text" id="request-name" class="required" dir="auto" placeholder="{{_ 'Enter_name_here'}}">--> | ||
<!--</div>--> | ||
<div class="input-submit"> | ||
<button class="button primary save-request">{{_ "Create" }}</button> | ||
<button class="button cancel-request">{{_ "Cancel" }}</button> | ||
</div> | ||
</template> |
Oops, something went wrong.