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
1,265 lines (1,190 loc) • 68 kB
HTML
<script type="text/javascript">
$.RedBot = {};
$.RedBot.buttonTypes = [
{
type: 'url',
label: 'URL',
className: 'platform-combo-telegram-facebook-viber',
platforms: ['facebook', 'telegram', 'viber']
},
{
type: 'postback',
label: 'Postback',
className: 'platform-combo-telegram-facebook-slack-viber',
platforms: ['facebook', 'telegram', 'slack', 'viber']
},
{
type: 'dialog',
label: 'Open Dialog',
className: 'platform-combo-slack',
platforms: ['slack']
},
{
type: 'quick-reply',
label: 'Postback',
className: 'platform-combo-facebook',
platforms: ['facebook']
},
{
type: 'location',
label: 'Location',
className: 'platform-combo-facebook',
platforms: ['facebook']
},
{
type: 'call',
label: 'Phone Call',
className: 'platform-combo-facebook',
platforms: ['facebook']
},
{
type: 'share',
label: 'Share',
className: 'platform-combo-facebook',
platforms: ['facebook']
},
{
type: 'login',
label: 'Log In',
className: 'platform-combo-facebook',
platforms: ['facebook']
},
{
type: 'logout',
label: 'Log Out',
className: 'platform-combo-facebook',
platforms: ['facebook']
},
{
type: 'newline',
label: 'New row of buttons',
className: 'platform-combo-telegram-viber',
platforms: ['telegram', 'viber']
},
{
type: 'keyboardButton',
label: 'Keyboard Button',
className: 'platform-combo-telegram',
platforms: ['telegram']
}
];
$.RedBot.elementTypes = [
{
type: 'text',
label: 'Text',
className: 'platform-slack',
platforms: ['slack']
},
{
type: 'textarea',
label: 'Textarea',
className: 'platform-slack',
platforms: ['slack']
},
{
type: 'select',
label: 'Select',
className: 'platform-slack',
platforms: ['slack']
}
];
$.RedBot.validate = {};
$.RedBot.validate.isVariable = function(value) {
return $.RedBot.validate.string(value) && value.match(/^\{\{[A-Za-z0-9_-]*\}\}$/) != null;
};
$.RedBot.validate.number = function(value) {
return typeof value === 'number';
};
$.RedBot.validate.string = function(value) {
return typeof value === 'string';
};
$.RedBot.validate.email = function(value) {
return typeof value === 'string' && value !== '' && value.indexOf('@') !== -1;
};
$.RedBot.validate.privateKey = function(value) {
return $.RedBot.validate.string(value)
&& value.indexOf('-----BEGIN PRIVATE KEY-----') !== -1
&& value.indexOf('-----END PRIVATE KEY-----') !== -1;
};
$.RedBot.validate.numberOrVariable = function(value) {
return $.RedBot.validate.number(value)
||
($.RedBot.validate.notEmpty(value) && !isNaN(parseFloat(value)))
||
($.RedBot.validate.notEmpty(value) && $.RedBot.validate.isVariable(value));
};
$.RedBot.validate.price = function(price) {
return price != null
&& $.RedBot.validate.notEmpty(price.label)
&& $.RedBot.validate.numberOrVariable(price.amount);
};
$.RedBot.validate.prices = function(prices) {
if (prices == null) {
return false;
}
var valid = true;
var idx;
for(idx = 0; idx < prices.length; idx++) {
if (!$.RedBot.validate.price(prices[idx])) {
valid = false;
}
}
return valid;
};
$.RedBot.validate.notEmpty = function(str) {
return str != null && str != '';
};
$.RedBot.validate.isEmpty = function(str) {
return str == null || str.length === 0;
};
$.RedBot.validate.url = function(url) {
return url != null && url.match(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi);
};
$.RedBot.validate.arrayNotEmpty = function(array) {
return array != null && array.length !== 0;
};
$.RedBot.validate.element = function(element) {
if (element == null) {
return false;
}
switch(element.type) {
case 'text':
case 'textarea':
return $.RedBot.validate.notEmpty(element.name) && $.RedBot.validate.notEmpty(element.label);
case 'select':
return $.RedBot.validate.notEmpty(element.name) && $.RedBot.validate.notEmpty(element.label)
&& $.RedBot.validate.options(element.options);
default:
return false;
}
};
$.RedBot.validate.options = function(options) {
if (options == null || options.length == null || options.length === 0) {
return false;
}
var idx;
var result = true;
for(idx = 0; idx < options.length; idx++) {
if (!($.RedBot.validate.notEmpty(options[idx].value) && $.RedBot.validate.notEmpty(options[idx].label))) {
result = false;
}
}
return result;
};
$.RedBot.validate.button = function(button) {
if (button == null) {
return false;
}
switch(button.type) {
case 'url':
return $.RedBot.validate.notEmpty(button.label) && $.RedBot.validate.url(button.url);
case 'postback':
return $.RedBot.validate.notEmpty(button.label) && $.RedBot.validate.notEmpty(button.value);
case 'dialog':
return $.RedBot.validate.notEmpty(button.label) && $.RedBot.validate.notEmpty(button.value);
case 'quick-reply':
return $.RedBot.validate.notEmpty(button.label) && $.RedBot.validate.notEmpty(button.value);
case 'call':
return $.RedBot.validate.notEmpty(button.label) && $.RedBot.validate.notEmpty(button.number);
case 'keyboardButton':
return $.RedBot.validate.notEmpty(button.label);
case 'newline':
case 'share':
case 'logout':
case 'location':
return true;
case 'login':
return $.RedBot.validate.url(button.url);
default:
return false;
}
};
$.RedBot.validate.confirmationStatus = function(status) {
return ['none', 'confirmed', 'denied'].indexOf(status) !== -1;
};
$.RedBot.validate.rule = function(rule) {
if (rule == null) {
return false;
}
switch(rule.type) {
case 'isSlotConfirmationStatus':
return $.RedBot.validate.notEmpty(rule.slot) && $.RedBot.validate.confirmationStatus(rule.confirmationStatus);
case 'isIntentConfirmationStatus':
return $.RedBot.validate.confirmationStatus(rule.confirmationStatus);
case 'dialogState':
return $.RedBot.validate.notEmpty(rule.state);
case 'environment':
return $.RedBot.validate.notEmpty(rule.environment);
case 'topicIncludes':
case 'isNotTopic':
case 'isTopic':
return $.RedBot.validate.notEmpty(rule.topic);
case 'isVariable':
return $.RedBot.validate.notEmpty(rule.value) && $.RedBot.validate.notEmpty(rule.variable);
case 'hasNotVariable':
case 'hasVariable':
return $.RedBot.validate.notEmpty(rule.variable);
case 'command':
return $.RedBot.validate.notEmpty(rule.command);
case 'messageType':
return $.RedBot.validate.notEmpty(rule.messageType);
case 'transport':
return $.RedBot.validate.notEmpty(rule.transport);
case 'messageEvent':
return $.RedBot.validate.notEmpty(rule.event);
case 'isIntentName':
return $.RedBot.validate.notEmpty(rule.intent);
case 'isIntent':
case 'outbound':
case 'inbound':
case 'isTopicEmpty':
case 'catchAll':
case 'pending':
case 'notPending':
case 'anyCommand':
return true;
default:
return false;
}
};
$.RedBot.validate.rules = function(rules) {
if (rules == null) {
return false;
}
var valid = true;
var idx;
for(idx = 0; idx < rules.length; idx++) {
if (!$.RedBot.validate.rule(rules[idx])) {
valid = false;
}
}
return valid;
};
$.RedBot.buttonTypes.findButton = function(type) {
var button = null;
this.forEach(function(current) {
if (current.type === type) {
button = current;
}
});
return button;
};
$.RedBot.elementTypes.findElement = function(type) {
var element = null;
this.forEach(function(current) {
if (current.type === type) {
element = current;
}
});
return element;
};
$.RedBot.platforms = function(platforms, availablePlatforms) {
var match = false;
platforms.forEach(function(platform) {
if (availablePlatforms.indexOf(platform) !== -1) {
match = true;
}
});
return match;
};
$.RedBot.intersect = function(array1, array2) {
var result = [];
var k = 0;
// perhaps use ES6 set
for(k = 0; k < array1.length; k++) {
if (array2.indexOf(array1[k]) !== -1 && result.indexOf(array1[k]) === -1) {
result.push(array1[k]);
}
}
for(k = 0; k < array2.length; k++) {
if (array1.indexOf(array2[k]) !== -1 && result.indexOf(array2[k]) === -1) {
result.push(array2[k]);
}
}
return result;
};
$.RedBot.platformClass = function(control, platforms) {
var base = null;
switch(control) {
case 'select': base = 'platform-combo'; break;
case 'icon': base = 'icon-platform'; break;
case 'text': base = 'platform'; break;
default:
console.error('Unrecognized platform icon for type "' + control + '"');
return '';
}
var platformClasses = [];
if (platforms.indexOf('telegram') !== -1) {
platformClasses.push('telegram');
}
if (platforms.indexOf('facebook') !== -1) {
platformClasses.push('facebook');
}
if (platforms.indexOf('smooch') !== -1) {
platformClasses.push('smooch');
}
if (platforms.indexOf('slack') !== -1) {
platformClasses.push('slack');
}
if (platforms.indexOf('viber') !== -1) {
platformClasses.push('viber');
}
if (platforms.indexOf('alexa') !== -1) {
platformClasses.push('alexa');
}
if (platforms.indexOf('twilio') !== -1) {
platformClasses.push('twilio');
}
if (platforms.indexOf('discord') !== -1) {
platformClasses.push('discord');
}
return platformClasses.length !== 0 ? base + '-' + platformClasses.join('-') : '';
};
$.RB_Legend = function() {
$('.redbot-form-legend').each(function() {
var dom = $(this);
dom.html('Some attributes apply for specific platform:<br>\n' +
' <span class="icon-platform-alexa"></span> = <span style="font-size:10px">Alexa</span>\n' +
' <span class="icon-platform-discord"></span> = <span style="font-size:10px">Discord</span>\n' +
' <span class="icon-platform-facebook"></span> = <span style="font-size:10px">Facebook</span>\n' +
' <span class="icon-platform-smooch"></span> = <span style="font-size:10px">Smooch</span>\n' +
' <span class="icon-platform-slack"></span> = <span style="font-size:10px">Slack</span>\n' +
' <span class="icon-platform-twilio"></span> = <span style="font-size:10px">Twilio</span>\n' +
' <span class="icon-platform-telegram"></span> = <span style="font-size:10px">Telegram</span>\n' +
' <span class="icon-platform-universal"></span> = <span style="font-size:10px">Universal</span>\n' +
' <span class="icon-platform-viber"></span> = <span style="font-size:10px">Viber</span>')
});
};
$.fn.RB_setComboWithPlatformLabels = function(value) {
var combo = $(this);
$(this).val(value);
var selected = $('option[value=' + value + ']', this);
combo
.attr('class')
.split(' ')
.forEach(function(className) {
if (className.indexOf('platform-') !== -1) {
combo.removeClass(className);
}
});
if (selected != null) {
combo.addClass(selected.attr('class'));
}
};
$.fn.RB_comboWithPlatformLabels = function() {
$(this)
.change(function() {
var combo = $(this);
var selected = $('option[value=' + combo.val() + ']', this);
combo
.attr('class')
.split(' ')
.forEach(function(className) {
if (className.indexOf('platform-') !== -1) {
combo.removeClass(className);
}
});
if (selected != null) {
combo.addClass(selected.attr('class'));
}
});
};
$.fn.RB_OptionsEditor = function(command, options) {
var container = $(this);
var defaultOptions = {
label: 'Options',
labelAdd: 'add'
};
if (arguments.length === 1 && typeof arguments[0] === 'object') {
options = $.extend({}, defaultOptions, arguments[0]);
command = 'init';
} else if (arguments.length === 0) {
command = 'init';
options = $.extend({}, defaultOptions);
} else if (arguments.length === 1 && arguments[0] === 'value') {
command = 'get_value';
} else if (arguments.length === 2 && arguments[0] === 'value') {
command = 'set_value';
}
var addRow = function(value, label) {
var count = $('.option', container);
$('<div class="option"/>').css('marginTop','5px')
.append('<div style="display:inline-block;width:6%;" class="order">' + (count.length + 1) + '.</div>')
.append($('<input style="width:35%" class="node-input-rule-type" type="text" name="value" placeholder="Value" value="' + value + '"/>'))
.append($('<input style="width:48%;margin-left: 5%" class="node-input-rule-type" type="text" name="label" placeholder="Label" value="' + label + '"/>'))
.append('<a href="#" class="btn-remove" style="width:5%;display:inline-block;"><i class="fa fa-times"></i></a>')
.appendTo($('.options', container));
};
switch(command) {
case 'init':
container
.addClass('rb-options-editor')
.on('click', '.btn-remove', function() {
$(this).parents('.option').remove();
$('.option', container).each(function(idx, item) {
$('.order', item).html(idx + 1);
});
});
$('<div class="control-description"/>')
.html(options.label)
.appendTo(container);
$('<div class="options"/>')
.appendTo(container);
$('<div class="button"/>')
.append('<a class="btn-add" href="#">' + options.labelAdd+ '</a>')
.appendTo(container);
$('a.btn-add', container)
.click(function(e) {
e.preventDefault();
addRow('', '');
});
break;
case 'get_value':
var values = [];
$('.option', container).each(function(idx, item) {
var value = $('input[name=value]', item).val();
var label = $('input[name=label]', item).val();
if (value === '' && label !== '') {
value = label;
} else if (value !== '' && label === '') {
label = value;
}
if (value !== '' && label !== '') {
values.push({
value: value,
label: label
});
}
});
return values;
case 'set_value':
$('.options').empty();
$(arguments[1]).each(function(idx, option) {
addRow(option.value, option.label);
});
break;
}
return container;
};
$.fn.RB_setElementData = function(obj, options) {
options = options || {};
options.badges = options.badges !== false;
var container = $(this);
var select = $('select[name=type]', container).val(obj.type);
var panel = $('.panel-' + obj.type, container).show();
var element = $.RedBot.elementTypes.findElement(obj.type);
if (element != null && options.badges) {
select.attr('class', element.className);
}
$('input[name="label"]', panel).val(obj.label);
$('input[name="name"]', panel).val(obj.name);
$('input[name="value"]', panel).val(obj.value);
$('input[name="placeholder"]', panel).val(obj.placeholder);
$('input[name="hint"]', panel).val(obj.hint);
$('select[name="subtype"]', panel).val(obj.subtype);
$('input[name="max_length"]', panel).val(obj.maxLength);
$('input[name="min_length"]', panel).val(obj.minLength);
$('input[name="optional"]', panel).attr('checked', obj.optional === true);
$('.container-options', panel).RB_OptionsEditor('value', obj.options);
return container;
};
$.fn.RB_setButtonData = function(obj, options) {
options = options || {};
options.badges = options.badges !== false;
var container = $(this);
var availablePlatforms = $.isArray(container.data('platforms')) ?
container.data('platforms') : ['facebook', 'telegram'];
var select = $('select[name=type]', container).val(obj.type);
var panel = $('.panel-' + obj.type, container).show();
var button = $.RedBot.buttonTypes.findButton(obj.type);
// update badges of select with intersection of available platforms and button compatibility
if (button != null && options.badges) {
select.attr('class', $.RedBot.platformClass('select', $.RedBot.intersect(availablePlatforms, button.platforms)));
} else {
select.attr('class', '');
}
$('input[name=label]', panel).val(obj.label);
$('input[name=url]', panel).val(obj.url);
$('input[name=value]', panel).val(obj.value);
$('input[name=number]', panel).val(obj.number);
$('input[name=answer]', panel).val(obj.answer);
$('input[name=alert]', panel).attr('checked', obj.alert === true);
$('select[name=webview_height_ratio]', panel).val(obj.webViewHeightRatio);
$('select[name=style]', panel).val(obj.style);
$('input[name=messenger_extensions]').attr('checked', obj.extensions === true);
return container;
};
$.fn.RB_getElementData = function() {
var container = $(this);
var type = $('select[name="type"]', container);
var panel = $('.panel-' + type.val(), container);
var label = $('input[name="label"]', panel);
var name = $('input[name="name"]', panel);
var value = $('input[name="value"]', panel);
var placeholder = $('input[name="placeholder"]', panel);
var hint = $('input[name="hint"]', panel);
var subtype = $('select[name="subtype"]', panel);
var maxLength = $('input[name="max_length"]', panel);
var minLength = $('input[name="min_length"]', panel);
var optional = $('input[name="optional"]', panel);
var options = $('.container-options', panel);
var result = null;
switch(type.val()) {
case 'text':
case 'textarea':
result = {
type: type.val(),
label: label.val(),
name: name.val(),
value: value.val(),
placeholder: placeholder.val(),
hint: hint.val(),
optional: optional.is(':checked')
};
if (subtype.val() !== '') {
result.subtype = subtype.val();
}
if (!isNaN(parseInt(maxLength.val(), 10))) {
result.maxLength = parseInt(maxLength.val(), 10);
}
if (!isNaN(parseInt(minLength.val(), 10))) {
result.minLength = parseInt(minLength.val(), 10);
}
break;
case 'select':
result = {
type: 'select',
label: label.val(),
name: name.val(),
options: options.RB_OptionsEditor('value')
};
break;
}
return result;
};
$.fn.RB_getButtonData = function() {
var container = $(this);
var type = $('select[name="type"]', container);
var panel = $('.panel-' + type.val(), container);
var label = $('input[name="label"]', panel);
var url = $('input[name="url"]', panel);
var value = $('input[name="value"]', panel);
var number = $('input[name="number"]', panel);
var answer = $('input[name="answer"]', panel);
var alert = $('input[name="alert"]', panel);
var style = $('select[name="style"]', panel);
var messengerExtensions = $('input[name="messenger_extensions"]', panel);
var webViewHeight = $('select[name="webview_height_ratio"]', panel);
var result = null;
switch(type.val()) {
case 'keyboardButton':
result = {
type: 'keyboardButton',
label: label.val()
};
break;
case 'url':
result = {
type: 'url',
label: label.val(),
url: url.val(),
webViewHeightRatio: webViewHeight.val(),
extensions: messengerExtensions.is(':checked'),
answer: answer.val(),
alert: alert.is(':checked')
};
break;
case 'call':
result = {
type: 'call',
label: label.val(),
number: number.val()
};
break;
case 'postback':
result = {
type: 'postback',
label: label.val(),
value: value.val(),
answer: answer.val(),
alert: alert.is(':checked'),
style: style.val()
};
break;
case 'dialog':
result = {
type: 'dialog',
label: label.val(),
value: value.val(),
answer: answer.val(),
alert: alert.is(':checked'),
style: style.val()
};
break;
case 'quick-reply':
result = {
type: 'quick-reply',
label: label.val(),
value: value.val(),
url: url.val()
};
break;
case 'newline':
result = {
type: 'newline'
};
break;
case 'location':
result = {
type: 'location'
};
break;
case 'share':
result = {
type: 'share'
};
break;
case 'logout':
result = {
type: 'logout'
};
break;
case 'login':
result = {
type: 'login',
url: url.val()
};
break;
}
return result;
};
$.fn.RB_hasPlatform = function(platform, platforms) {
platform = platform instanceof Array ? platform : [platform];
var hasMatch = false;
for(var idx = 0; idx < platform.length; idx++) {
if (platforms.indexOf(platform[idx]) !== -1) {
hasMatch = true;
}
}
return hasMatch ? $(this) : $();
};
$.fn.RB_mountButtonDialog = function(options) {
options = options || {};
options.types = options.types != null ?
options.types : ['url', 'postback', 'call', 'share', 'login', 'newline', 'logout', 'location', 'quick-reply', 'dialog', 'key'];
options.platforms = options.platforms != null ? options.platforms : ['facebook', 'telegram'];
options.badges = options.badges !== false;
var container = $(this);
// store available platforms in the container
container.data('platforms', options.platforms);
var main = $('<div/>').appendTo(container);
var cx = options.badges ? $.RedBot.platformClass : function() { return ''; };
var selectButtonType = $('<select name="type"/>', {style:"width:200px; margin-left: 5px; text-align: center;"})
.attr('placeholder', 'Select button type')
.change(function() {
var type = $(this).val();
$('.panel', container).hide();
$('.panel-' + type, container).show();
// find the right button
var button = $.RedBot.buttonTypes.findButton(type);
// store class only if badges should be shown
if (button != null && options.badges) {
$(this).attr('class', cx('select', $.RedBot.intersect(options.platforms, button.platforms)));
} else {
$(this).attr('class', '');
}
})
.appendTo(main);
selectButtonType.append($('<option value="">Select button type</option>'));
$.RedBot.buttonTypes.forEach(function(button) {
if (options.types.indexOf(button.type) !== -1 && $.RedBot.platforms(options.platforms, button.platforms)) {
selectButtonType.append($('<option value="' + button.type + '">' + button.label + '</option>'));
}
});
var panelUrl = $('<div class="panel panel-url" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Button label"/>'))
.appendTo(panelUrl);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="url" placeholder="Button URL"/>'))
.appendTo(panelUrl);
$('<div/>').css('marginTop','5px')
.RB_hasPlatform('telegram', options.platforms)
.append('<input class="node-input-rule-type ' + cx('text', ['telegram']) + '" type="text" name="answer" placeholder="Button feedback"/>')
.append('<input type="checkbox" name="alert" value="on" style="width: auto;margin:0px 10px 5px 10px;"/>')
.append('<span>With popup</span><span class="' + cx('icon', ['telegram']) + '"></span>')
.appendTo(panelUrl);
$('<div/>').css('marginTop','5px')
.RB_hasPlatform('facebook', options.platforms)
.append('<select name="webview_height_ratio" class="' + cx('select', ['facebook']) + '" style="width:70%">'
+ '<option value="tall">Tall</option>'
+ '<option value="compact">Compact</option>'
+ '<option value="full">Full</option>'
+ '</select>')
.append('<input type="checkbox" name="messenger_extensions" value="on" style="width: auto;margin:0px 10px 5px 10px;"/>')
.append('<span>Extensions</span><span class="' + cx('icon', ['facebook']) + '"></span>')
.appendTo(panelUrl);
var panelPostback = $('<div class="panel panel-postback" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Button label"/>'))
.appendTo(panelPostback);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="value" placeholder="Postback value">'))
.appendTo(panelPostback);
var feedbackRow = $('<div/>').css('marginTop','5px')
.RB_hasPlatform(['telegram', 'slack'], options.platforms)
.append('<input class="node-input-rule-type ' + cx('text', ['telegram', 'slack']) + '" type="text" name="answer" placeholder="Button feedback (optional)"/>');
$('<input type="checkbox" name="alert" value="on" style="width: auto;margin:0px 10px 5px 10px;"/>')
.RB_hasPlatform(['telegram'], options.platforms)
.appendTo(feedbackRow);
$('<span>With popup</span><span class="' + cx('icon', ['telegram']) + '"></span>')
.RB_hasPlatform(['telegram'], options.platforms)
.appendTo(feedbackRow);
feedbackRow.appendTo(panelPostback);
$('<div/>').css('marginTop','5px')
.RB_hasPlatform('slack', options.platforms)
.append('<select name="style" class="' + cx('select', ['slack']) + '" style="width:70%">'
+ '<option value="">Select button style</option>'
+ '<option value="default">Default</option>'
+ '<option value="primary">Primary</option>'
+ '<option value="danger">Danger</option>'
+ '</select>')
.appendTo(panelPostback);
var panelDialog = $('<div class="panel panel-dialog" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Button label"/>'))
.appendTo(panelDialog);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="value" placeholder="Dialog Id">'))
.appendTo(panelDialog);
$('<div/>').css('marginTop','5px')
.RB_hasPlatform(['telegram', 'slack'], options.platforms)
.append('<input class="node-input-rule-type ' + cx('text', ['slack']) + '" type="text" name="answer" placeholder="Button feedback (optional)"/>')
.appendTo(panelDialog);
$('<div/>').css('marginTop','5px')
.append('<select name="style" class="' + cx('select', ['slack']) + '" style="width:70%">'
+ '<option value="">Select button style</option>'
+ '<option value="default">Default</option>'
+ '<option value="primary">Primary</option>'
+ '<option value="danger">Danger</option>'
+ '</select>')
.appendTo(panelDialog);
var panelQuickReply = $('<div class="panel panel-quick-reply" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Button label"/>'))
.appendTo(panelQuickReply);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="value" placeholder="Postback value">'))
.appendTo(panelQuickReply);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="url" placeholder="Image URL"/>'))
.appendTo(panelQuickReply);
var panelCall = $('<div class="panel panel-call" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Button label"/>'))
.appendTo(panelCall);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="number" placeholder="Phone number"/>'))
.appendTo(panelCall);
var panelNewline = $('<div class="panel panel-newline" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.html('This creates a new row of buttons')
.appendTo(panelNewline);
var panelKeyboardButton = $('<div class="panel panel-keyboardButton" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Button label"/>'))
.appendTo(panelKeyboardButton);
$('<div class="panel panel-share" style="display:none;"/>').appendTo(container);
$('<div class="panel panel-logout" style="display:none;"/>').appendTo(container);
$('<div class="panel panel-location" style="display:none;"/>').appendTo(container);
var panelLogin = $('<div class="panel panel-login" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="url" placeholder="Authentication URL callback"/>'))
.appendTo(panelLogin);
return container;
};
$.fn.RB_mountElementDialog = function(options) {
options = options || {};
options.types = options.types != null ?
options.types : ['text', 'textarea', 'select'];
options.platforms = options.platforms != null ? options.platforms : ['slack'];
options.badges = options.badges !== false;
var container = $(this);
var main = $('<div/>').appendTo(container);
var cx = options.badges ? $.RedBot.platformClass : function() { return ''; };
var selectElementType = $('<select name="type"/>', {style:"width:200px; margin-left: 5px; text-align: center;"})
.attr('placeholder', 'Select element type')
.change(function() {
var type = $(this).val();
$('.panel', container).hide();
$('.panel-' + type, container).show();
// find the right button
var button = $.RedBot.buttonTypes.findButton(type);
// store class only if badges should be shown
if (button != null && options.badges) {
$(this).attr('class', button.className);
} else {
$(this).attr('class', '');
}
})
.appendTo(main);
selectElementType.append($('<option value="">Select element type</option>'));
$.RedBot.elementTypes.forEach(function(button) {
if (options.types.indexOf(button.type) !== -1 && $.RedBot.platforms(options.platforms, button.platforms)) {
selectElementType.append($('<option value="' + button.type + '">' + button.label + '</option>'));
}
});
var panelText = $('<div class="panel panel-text" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Label"/>'))
.appendTo(panelText);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="name" placeholder="Name"/>'))
.appendTo(panelText);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="value" placeholder="Initial value"/>'))
.appendTo(panelText);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="placeholder" placeholder="Placeholder"/>'))
.appendTo(panelText);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="hint" placeholder="Hint"/>'))
.appendTo(panelText);
$('<div/>').css('marginTop','5px')
.append('<select name="subtype" class="' + cx('select', ['']) + '" style="width:60%">'
+ '<option value="">Select sub-type</option>'
+ '<option value="email">Email</option>'
+ '<option value="number">Number</option>'
+ '<option value="tel">Telephone</option>'
+ '<option value="url">URL</option>'
+ '</select>')
.append('<input type="checkbox" name="optional" value="on" style="width: auto;margin:0px 10px 5px 10px;"/>')
.append('<span>Optional</span>')
.appendTo(panelText);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:45%" class="node-input-rule-type" type="number" name="min_length" placeholder="Min Length"/>'))
.append($('<input style="width:45%;margin-left: 10%" class="node-input-rule-type" type="number" name="max_length" placeholder="Max Length"/>'))
.appendTo(panelText);
var panelTextarea = $('<div class="panel panel-textarea" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Label"/>'))
.appendTo(panelTextarea);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="name" placeholder="Name"/>'))
.appendTo(panelTextarea);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="value" placeholder="Initial value"/>'))
.appendTo(panelTextarea);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="placeholder" placeholder="Placeholder"/>'))
.appendTo(panelTextarea);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="hint" placeholder="Hint"/>'))
.appendTo(panelTextarea);
$('<div/>').css('marginTop','5px')
.append('<select name="subtype" class="' + cx('select', ['']) + '" style="width:60%">'
+ '<option value="">Subtype</option>'
+ '<option value="email">Email</option>'
+ '<option value="number">Number</option>'
+ '<option value="tel">Telephone</option>'
+ '<option value="url">URL</option>'
+ '</select>')
.append('<input type="checkbox" name="optional" value="on" style="width: auto;margin:0px 10px 5px 10px;"/>')
.append('<span>Optional</span>')
.appendTo(panelTextarea);
var panelSelect = $('<div class="panel panel-select" style="display:none;"/>').appendTo(container);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="label" placeholder="Label"/>'))
.appendTo(panelSelect);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="name" placeholder="Name"/>'))
.appendTo(panelSelect);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="value" placeholder="Initial value"/>'))
.appendTo(panelSelect);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="placeholder" placeholder="Placeholder"/>'))
.appendTo(panelSelect);
$('<div/>').css('marginTop','5px')
.append($('<input style="width:100%" class="node-input-rule-type" type="text" name="hint" placeholder="Hint"/>'))
.appendTo(panelSelect);
$('<div/>').css('marginTop','5px')
.append('<div class="container-options"/>')
.appendTo(panelSelect);
panelSelect.find('.container-options').RB_OptionsEditor({ label: 'Combo box options'});
};
RED.nodes.registerType('chatbot-inline-buttons', {
category: 'RedBot',
color: '#FFCC66',
defaults: {
name: {
value: ''
},
buttons: {
value: [],
validate: function(buttons) {
if (buttons == null) {
return true; // allow no buttons
}
var valid = true;
var idx;
for(idx = 0; idx < buttons.length; idx++) {
if (!$.RedBot.validate.button(buttons[idx])) {
valid = false;
}
}
return valid;
}
},
message: {
value: ''
}
},
inputs: 1,
outputs: 1,
paletteLabel: 'Buttons',
icon: 'chatbot-inline-button.png',
label: function() {
return this.name || 'Buttons';
},
oneditsave: function() {
var buttons = $("#node-input-buttons-container").editableList('items');
var node = this;
node.buttons = [];
var idx;
for(idx = 0; idx < buttons.length; idx++) {
node.buttons.push($(buttons[idx]).RB_getButtonData());
}
},
oneditprepare: function() {
$.RB_Legend();
$('#node-input-buttons-container')
.css('min-height','300px')
.css('min-width','450px')
.editableList({
addButton: 'Add button',
addItem: function(container, i, item) {
$(container).RB_mountButtonDialog({
types: ['url', 'postback', 'call', 'login', 'newline', 'logout', 'dialog'],
platforms: ['facebook', 'telegram', 'slack']
});
$(container).RB_setButtonData(item);
},
removable: true,
sortable: true
});
var buttons = this.buttons;
var idx;
for (idx = 0; idx < buttons.length; idx++) {
$('#node-input-buttons-container').editableList('addItem', buttons[idx]);
}
},
oneditresize: function() {
var dialogForm = $('#dialog-form');
var rowName = $('.form-row-name', dialogForm);
var rowMessage = $('.form-row-message', dialogForm);
var rowLabel = $('.form-row-label', dialogForm);
var rowHint = $('.form-row-hint', dialogForm);
var height = dialogForm.height() - rowName.height() - rowMessage.height() - rowLabel.height() - rowHint.height() - 30;
$('#node-input-buttons-container').editableList('height', height);
}
});
</script>
<script type="text/x-red" data-template-name="chatbot-inline-buttons">
<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-message">
<label for="node-input-message"><i class="icon-envelope"></i> Message</label>
<textarea id="node-input-message" placeholder="Message" style="width:93%;height:50px;"></textarea>
<div style="max-width: 460px;font-size: 12px;color: #999999;line-height: 14px;clear:both;margin-top:5px;">
Supports handlebars-like variables for chat context like {{firstName}}, {{lastName}}, etc. and emoticons (:smile:, etc.)
</div>
</div>
<div class="form-row form-row-label" style="margin-bottom:0;">
<label><i class="fa fa-list"></i> <span>Buttons</span></label>
</div>
<div class="form-row node-input-rule-container-row">
<ol id="node-input-buttons-container"></ol>
</div>
<div class="redbot-form-legend"></div>
</script>
<script type="text/x-red" data-help-name="chatbot-inline-buttons"><p>Available buttons:</p>
<ul>
<li><strong>URL</strong>: Open a URL inside the client. Button feedbacks are available for Telegram and Messenger extensions in Facebook <em>[Telegram, Facebook, Viber]</em></li>
<li><strong>Postback</strong>: Allow the user to send a predefined message, the message will no be shown in the chat history. Button feedbacks are available for Telegram and Messenger extensions in Facebook <em>[Telegram, Facebook, Smooch, Viber]</em></li>
<li><strong>Phone call</strong>: Starts a phone call on mobile. <em>[Facebook]</em></li>
<li><strong>Log In</strong>: Starts the account linking flow. <em>[Facebook]</em></li>
<li><strong>Log Out</strong>: Starts the account unlinking flow. <em>[Facebook]</em></li>
<li><strong>New row</strong>: Telegram supports buttons layout with multiple row, this element just starts a new one. <em>[Telegram, Viber]</em></li>
</ul>
<p>Platform exceptions</p>
<ul>
<li><strong>Telegram</strong> supports a visual feedback, visible in the client, when a <code>Postback</code> or <code>URL</code> buttons is pressed</li>
<li><strong>Facebook</strong> supports some extra parameters in the <code>URL</code> button, like the size of the web view and <a href="https://developers.facebook.com/docs/messenger-platform/webview">messenger extensions</a> and a maximum od three buttons.</li>
</ul>
<p>Buttons can be created programmatically by an upstream <code>Function node</code> passing array of buttons in the message payload:</p>
<pre><code>msg.payload = [
{
type: 'url',
url: 'http://javascript-jedi.com',
label: 'Javascript Jedi'
},
{
type: 'postback',
value: 'MY_POSTBACK_MESSAGE',
label: 'Click me!'
},
{
type: 'share'
}
];
return msg;
</code></pre><p>Another way is to specify each config option as keys of an object in the message payload:</p>
<pre><code>msg.payload = {
message: 'This is a message above the buttons',
buttons: [
{
type: 'url',
url: 'http://javascript-jedi.com',
label: 'Javascript Jedi'
},
{
type: 'postback',
value: 'MY_POSTBACK_MESSAGE',
label: 'Click me!'
},
{
type: 'share'
}
]
};
return msg;
</code></pre><p>Available parameters for the <code>msg.payload</code></p>
<dl class="message-properties">
<dt>message<span class="property-type">string</span><dd>The message text above the buttons</dd>
<dt>buttons<span class="property-type">array of [button]</span><dd>The list of inline buttons</dd>
</dl>
<p>The <code>[button]</code> object</p>
<dl class="message-properties">
<dt>type<span class="property-type">string</span><dd>Type of button: <em>url</em>, <em>postback</em>, <em>quick-reply</em>, <em>location</em>, <em>call</em>, <em>share</em>, <em>login</em>, <em>logout</em>, <em>newline</em></dd>
<dt>label<span class="property-type">string</span><dd>Label of the button</dd>
<dt>value<span class="property-type">string</span><dd>Value returned as payload in <code>postback</code> buttons</dd>
<dt>url<span class="property-type">string</span><dd>Url to redirect to for <code>url</code> buttons or authentication URL for <code>login</code> buttons</dd>
<dt>number<span class="property-type">string</span><dd>Phone number to call for <code>call</code> buttons</dd>
<dt>messengerExtensions<span class="property-type">boolean</span><dd>Include Messenger Extensions for <code>url</code> buttons</dd>
<dt>answer<span class="property-type">string</span><dd>The feedback shown on Telegram client for <code>url</code> and <code>postback</code> buttons</dd>
<dt>alert<span class="property-type">boolean</span><dd>Show the feedback as alert on Telegram client for <code>url</code> and <code>postback</code> buttons</dd>
<dt>style<span class="property-type">string</span><dd>Style of buttons in Slack: <em>default</em>, <em>primary</em>, <em>danger</em></dd>
<dt>webViewHeightRatio<span class="property-type">string</span><dd>Aspect ratio of the webview in Facebook Messenger for <code>url buttons</code>. Valid values: <em>tall</em>, <em>compact</em>, <em>full</em></dd>
</dl>
<p><img src="https://img.shields.io/badge/platform-Telegram-blue.svg?colorB=7cbaea" alt="Telegram">
<img src="https://img.shields.io/badge/platform-Facebook-blue.svg" alt="Facebook">
<img src="https://img.shields.io/badge/platform-Slack-green.svg" alt="Slack">
<img src="https://img.shields.io/badge/platform-Smooch-orange.svg" alt="Smooch">
<img src="https://img.shields.io/badge/platform-Viber-8079d0.svg" alt="Viber"></p>
</script>
<style>
.form-row-checkbox {
margin-bottom: 0px;
}
.form-row-checkbox input[type=checkbox] {
width: auto;
margin-top: 0px;
margin-right: 5px;
}
.icon-platform-telegram {
display: inline-block;
width: 9px;
height: 9px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAADRJREFUGBlj1F764D8DAcACk78SJQ9jwmmdZQ/BbLgimABIMYwNU80EY+Cjh4QidJ+BPAQAUcEMK/ZfXe8AAAAASUVORK5CYII=);
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-discord {
display: inline-block;
width: 9px;
height: 9px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAADxJREFUGBljVFPT+s9AALDA5G/evApjgml1dW04H64IJIIsAdIE4zPBleNh0FkRisORfQhzNMipjMSEEwBROQx6SDYaygAAAABJRU5ErkJggg==');
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-facebook {
display: inline-block;
width: 9px;
height: 9px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAEJJREFUGBljtIqc8Z+BAGCByR9dlg5jwmnrqJlgNlwRiAcThKuCMpjQBbDxUUxCthLZVBRFyBLIJhJlHVGKGIkJJwAbVQzJXzC/gwAAAABJRU5ErkJggg==);
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-smooch {
display: inline-block;
width: 9px;
height: 9px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAENJREFUGBljXOyx/T8DAcAEk4/Z7gFjgmlkPlwRSAYmAaNhulAULfHcAVYIopEBiiKQBLoCkBiGIpAgOqCeIkZiwgkAEPoRCUXTyWoAAAAASUVORK5CYII=);
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-slack {
display: inline-block;
width: 9px;
height: 9px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAEtJREFUGBljDNvW/Z+BAGBBll/pWYLMZQjf3gPmwxWBFMAEUVQCOUzoAtj4cJNApiBbh2wqXBHIBGQJZOtJsw7ZKnRT4dYhW4XueACZqRaCOTsvfwAAAABJRU5ErkJggg==);
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-viber {
display: inline-block;
width: 9px;
height: 9px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAANklEQVQImWNsKL/MgAMwQaj6Dh24EJzNhEsT5XKNFVcg1tR36DRWXCHFTIhWuCZ0fcgSDAwMAO+nENww2Up1AAAAAElFTkSuQmCC);
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-universal {
display: inline-block;
width: 9px;
height: 9px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAKUlEQVQImWP8v4MBF2CCUP/d/8OF4GwmnLroL8cI9wPceYw7GdHlMAEAfGIMv3ninmsAAAAASUVORK5CYII=');
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-twilio {
display: inline-block;
width: 9px;
height: 9px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAADNJREFUGBljfGfk9Z+BAGCByQue3Qpjwun3xt5gNiO6SSDFMEmYaiYYAx89GBVh+A6bBwDzEA1NPYkyaAAAAABJRU5ErkJggg==');
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.icon-platform-alexa {
display: inline-block;
width: 9px;
height: 9px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAEJJREFUGBljVD799z8DAcCCLH/HhAnOVTnzD86GKwIpQJZA5iO0wvVhMuAmgaSQrUNWiqII3TqYQqKsI0oRIzHhBABD+hL/gIpxvwAAAABJRU5ErkJggg==');
margin-left: 3px;
margin-right: 3px;
vertical-align: middle;
}
.platform-facebook {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAEJJREFUGBljtIqc8Z+BAGCByR9dlg5jwmnrqJlgNlwRiAcThKuCMpjQBbDxUUxCthLZVBRFyBLIJhJlHVGKGIkJJwAbVQzJXzC/gwAAAABJRU5ErkJggg==);
background-position: right 2px top 2px;
background-repeat: no-repeat;
}
.platform-combo-facebook {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAEJJREFUGBljtIqc8Z+BAGCByR9dlg5jwmnrqJlgNlwRiAcThKuCMpjQBbDxUUxCthLZVBRFyBLIJhJlHVGKGIkJJwAbVQzJXzC/gwAAAABJRU5ErkJggg==);
background-position: right 12px top 2px;
background-repeat: no-repeat;
}
.platform-telegram {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAADRJREFUGBlj1F764D8DAcACk78SJQ9jwmmdZQ/BbLgimABIMYwNU80EY+Cjh4QidJ+BPAQAUcEMK/ZfXe8AAAAASUVORK5CYII=);
background-position: