node-red-contrib-chatbot
Version:
REDBot a Chat bot for a full featured chat bot for Telegram, Facebook Messenger and Slack. Almost no coding skills required
390 lines (372 loc) • 15 kB
HTML
<script type="text/javascript">
var mountControl = function(container, param, defaultParam, callback) {
container.empty();
switch(param.type) {
case 'string':
var suggestions = '';
var size = '100%'
if (param.suggestions != null && param.suggestions.length !== 0) {
suggestions = '<select value="' + (defaultParam != null ? defaultParam : '') + '" name="suggestions" autocomplete="disable" style="width: 18px;border: none;display: inline-block;">'
+ '<option value="">Select:</option>'
+ param.suggestions.map(function(suggestion) {
return '<option value="' + suggestion + '">' + suggestion + '</option>';
})
+ '</select>';
size = 'calc(100% - 18px)';
}
container.append(
'<div class="form-row" style="display:inline-block;margin-left:10px;">'
+ '<input type="text" value="' + (defaultParam != null ? defaultParam : '')
+ '" autocomplete="disable" style="width:' + size + ';" placeholder="' + (param.placeholder != null && param.placeholder != '' ? param.placeholder : '') + '">'
+ suggestions
+ '</div>'
);
$('input', container)
.keyup(function() {
callback($(this).val());
});
$('select[name=suggestions]')
.change(function(e) {
$('input', container).val($(this).val());
callback($(this).val());
});
break;
case 'boolean':
container.append(
'<div class="form-row" style="display:inline-block;margin-left:10px;">'
+ '<input type="radio" name="radio_' + param.name + '" value="true" style="width:20px;margin-top:-2px;"> Yes'
+ '<input type="radio" name="radio_' + param.name + '" value="false" style="width:20px;margin-top:-2px;margin-left:5px;"> No'
+ '</div>'
);
$('input', container)
.click(function() {
callback($(this).attr('value') === 'true');
});
if (defaultParam === true || defaultParam === 'true') {
$('input[type=radio][value=true]', container).attr('checked', true);
} else if (defaultParam === false || defaultParam === 'false') {
$('input[type=radio][value=false]', container).attr('checked', true);
}
break;
case 'select':
var options = '';
var idx = 0;
for (idx = 0; idx < param.options.length; idx++) {
options += '<option value="' + param.options[idx].value + '">' + param.options[idx].label + '</option>';
}
container
.append(
'<div class="form-row" style="display:inline-block;margin-left:10px;">'
+ '<select autocomplete="disable" style="width:100%;" placeholder="'
+ (param.placeholder != null && param.placeholder != '' ? param.placeholder : '') + '">'
+ options
+ '</select>'
+ '</div>'
)
$('select', container).val(defaultParam != null ? defaultParam : '')
$('select', container)
.change(function() {
callback($(this).val());
});
// if no initial value specified, then use one from param type
if (defaultParam == null && param.default != null) {
callback(param.default);
}
break;
}
}
var buildParam = function(row, platform, current) {
var node = this;
var params = node.availableParams[platform];
var idx = 0;
var tooltip = null;
var platformDefinition = null;
for(idx = 0; idx < node.platforms.length; idx++) {
if (node.platforms[idx].id === platform) {
platformDefinition = node.platforms[idx];
}
}
var paramsOptions = '';
for(idx = 0; idx < params.length; idx++) {
paramsOptions += '<option value="' + params[idx].name + '">' + params[idx].label + '</option>';
}
row.empty()
.append('<span class="platform" '
+ 'style="font-size:10px;background-color:'
+ (platformDefinition != null && platformDefinition.color != null ? platformDefinition.color : '#bbbbbb')
+ ';padding: 2px 3px 2px 3px;border-radius:2px;text-transform:uppercase;color:#ffffff;margin-left:5px;"'
+ '>'
+ platform + '</span>')
.append('<select class="param-name" name="transport" style="width:40%;margin-left:10px;">'
+ '<option value="">Select parameter</option>'
+ paramsOptions
+ '</select>')
.append('<input class="value" type="hidden" name="" value="">')
.append('<input class="type" type="hidden" name="type" value="">')
.append('<span class="hint" style="margin-left:8px;display:inline-block;"><i class="fa fa-question-circle" style="color:#999999;"/></div>')
.append('<div class="control" style="display:inline-block;height:34px;width:35%;"> </div>');
$('.hint', row).hide();
$('select.param-name', row)
.change(function() {
var param = $(this).val();
var paramObj = null;
for(idx = 0; idx < params.length; idx++) {
if (params[idx].name === param) {
paramObj = params[idx];
}
}
if (paramObj != null) {
$('input[type=hidden].value', row).attr('name', param);
$('input[type=hidden].value', row).attr('value', paramObj.default != null ? paramObj.default : null);
$('input[type=hidden].type', row).attr('value', paramObj.type);
mountControl(
$('.control', row),
paramObj,
paramObj.default != null ? paramObj.default : null,
function(value) {
$('input[type=hidden].value', row).attr('value', value);
}
);
if (paramObj.description != null) {
if (tooltip != null) {
tooltip.dispose();
tooltip = null;
}
tooltip = new Tooltip($('.hint', row).get(0), {
placement: 'right-start',
title: paramObj.description,
trigger: 'hover'
});
$('.hint', row).show();
} else {
$('.hint', row).hide();
if (tooltip != null) {
tooltip.dispose();
tooltip = null;
}
}
}
});
if (current != null) {
var paramObj = null;
for(idx = 0; idx < params.length; idx++) {
if (params[idx].name === current.name) {
paramObj = params[idx];
}
}
$('input[type=hidden].type', row).attr('value', paramObj.type);
$('input[type=hidden].value', row).attr('name', current.name);
$('input[type=hidden].value', row).attr('value', current.value);
$('select.param-name', row).val(current.name);
mountControl(
$('.control', row),
paramObj,
current.value,
function(value) {
$('input[type=hidden].value', row).attr('value', value);
}
);
if (paramObj.description != null) {
if (tooltip != null) {
tooltip.dispose();
tooltip = null;
}
tooltip = new Tooltip($('.hint', row).get(0), {
placement: 'right-start',
title: paramObj.description,
trigger: 'hover'
});
$('.hint', row).show();
}
}
}
var buildParams = function() {
var node = this;
$("#node-input-params-container").css('min-height','250px').css('min-width','450px').editableList({
addButton: 'Add parameter',
addItem: function (container, i, opt) {
// build rule row
var param = opt;
var isEmpty = param.platform == null;
// empty row
var row = $('<div/>').appendTo(container);
if (!isEmpty) {
buildParam.call(node, row, param.platform, param);
return;
} else {
// build transport options
var transportOptions = '<option value="all">All</option>';
for(idx = 0; idx < node.platforms.length; idx++) {
// only if some parameters
if (node.availableParams[node.platforms[idx].id] != null) {
transportOptions += '<option value="' + node.platforms[idx].id + '">'
+ (node.platforms[idx].name != null ? node.platforms[idx].name : node.platforms[idx].id)
+ '</option>';
}
}
row
.append('<select class="transport" name="transport" style="width:40%;margin-left:10px;">'
+ '<option value="">Select transport</option>'
+ transportOptions
+ '</select>')
$('select.transport', row)
.change(function() {
buildParam.call(node, row, $(this).val());
});
}
},
removeItem: function(opt) {
var rules = $('#node-input-params-container').editableList('items');
rules.each(function(i) {
$(this).find('.node-input-rule-index').html(i + 1);
});
},
sortItems: function() {
var rules = $('#node-input-params-container').editableList('items');
rules.each(function(i) {
$(this).find('.node-input-rule-index').html(i + 1);
});
},
sortable: false,
removable: true
});
for (var i=0; i < node.params.length; i++) {
var param = this.params[i];
$('#node-input-params-container').editableList('addItem', param);
}
};
$.RedBot.registerType('chatbot-params', {
category: $.RedBot.config.name + ' Flow',
color: '#FFCC66',
defaults: {
name: {
value: ''
},
params: {
value: [],
validate: function(params) {
return $.RedBot.validate.params(params);
}
},
outputs: {
value: 1
}
},
inputs: 1,
outputs: 1,
paletteLabel: 'Params',
icon: 'chatbot-params.png',
label: function() {
return this.name || 'Params' ;
},
oneditsave: function() {
var params = $('#node-input-params-container').editableList('items');
var node = this;
node.params = [];
params.each(function(i) {
var paramRow = $(this);
var platform = paramRow.find('.platform').text();
var name = paramRow.find('input[type=hidden].value').attr('name');
var value = paramRow.find('input[type=hidden].value').attr('value');
var type = paramRow.find('input[type=hidden].type').attr('value');
// do some conversion
if (type === 'boolean') {
value = value === 'true' || value === true;
}
node.params.push({
platform: platform,
name: name,
value: value
});
});
},
oneditprepare: function() {
var node = this;
var nodeRedUrl = $.RedBot.getNodeRedUrl();
$.get(nodeRedUrl + 'redbot/globals')
.done(function(response) {
node.messageTypes = response.messageTypes;
node.eventTypes = response.eventTypes;
node.availableParams = response.params;
$.RedBot.fetchPlatforms()
.done(function(platforms) {
node.platforms = platforms;
buildParams.call(node);
});
});
},
oneditresize: function() {
var dialogForm = $('#dialog-form');
var rowName = $('.form-row-name', dialogForm);
var rowLabel = $('.form-row-label', dialogForm);
var height = dialogForm.height() - rowName.height() - rowLabel.height() - 30;
$('#node-input-params-container').editableList('height', height);
}
});
</script>
<script type="text/x-red" data-template-name="chatbot-params">
<div class="form-row form-row-name">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row form-row-label">
<label for="node-input-name" style="width:100%;">Add parameter to the chat flow:</label>
</div>
<div class="form-row node-input-params-container-row">
<ol id="node-input-params-container"></ol>
</div>
</script>
<script type="text/x-red" data-help-name="chatbot-params"><p>The <code>Params node</code> is used to specify some platform related parameters or flag. For example in <strong>Telegram</strong> it’s possible to deliver a message silently while in <strong>Twilio</strong> it’s possible to specify the originator number (overriding the bot configuration).</p>
<p>The text-like values of parameters also accepts chat contexts variables. For example in <strong>Telegram</strong> to modify the previous sent message instead of sending a new one, just add a parameter, select platform <strong>Telegram</strong> then select <em>“Modify message”</em> and then type <code>{{outboundMessageId}}</code></p>
<p><img src="https://s3.us-west-2.amazonaws.com/secure.notion-static.com/159083b5-2efd-4d12-b603-51634fade887/modify-message.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20230218%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20230218T124947Z&X-Amz-Expires=3600&X-Amz-Signature=df019690f0c7996e7abf979726b71e2a1ccf75d7ac8cfca354d2456cafcd9fba&X-Amz-SignedHeaders=host&x-id=GetObject" alt="Modify message"></p>
<p>The same chat context value <code>{{outboundMessageId}}</code> can be used, for example, to delete a previous message.</p>
<p>The same can obtained with in a upstream <code>Function node</code>:</p>
<pre><code class="language-javascript">msg.params = [
{ platform: 'telegram', name: 'modifyMessageId', value: '{{outboundMessageId}}'
];
return msg;
</code></pre>
<p>Available parameters for the <code>msg.payload</code></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>params</td>
<td>array of [param]</td>
<td>Array of parameters to send to the Sender node</td>
</tr>
</tbody></table>
<p>The <em>param</em> object</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>platform</td>
<td>string</td>
<td>The specific platform (transport) this parameter is used for (<em>telegram</em>, <em>slack</em>, <em>facebook</em>, etc)</td>
</tr>
<tr>
<td>name</td>
<td>string</td>
<td>The name of the parameter</td>
</tr>
<tr>
<td>value</td>
<td>any</td>
<td>The value of the parameter</td>
</tr>
</tbody></table>
<blockquote>
<p>❗ The chat context <code>messageId</code> is deprecated, it was used ambiguously in previous versions to refer both the last inbound and outbound message. It’s replace by <code>outboundMessageId</code> (the id of the last message sent by the chatbot to the user) and <code>inboundMessageId</code> (the id of the last message received by the chatbot from the user).<br>For retro-compatibility <code>messageId</code> is still available. </p>
</blockquote>
</script>