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
170 lines (151 loc) • 4.9 kB
JavaScript
const _ = require('underscore');
const utils = require('./helpers/utils');
const when = utils.when;
const moment = require('moment');
module.exports = (msg, node, opts) => {
const options = Object.assign({
preserveNumbers: false // do not translate numbers into string
}, opts)
// extract subtokens from a object value
const extractObjectKeys = (value, subtokens) => {
let result = value;
let currentValue = value;
if (_.isArray(subtokens) && !_.isEmpty(subtokens)) {
_(subtokens).each(function(subtoken) {
if (_.isObject(currentValue) && currentValue[subtoken] != null) {
currentValue = currentValue[subtoken];
}
});
result = currentValue;
}
return result;
};
const getTokenValue = token => {
let value = null;
const context = node.context();
const subtokens = token.split('.');
const variable = subtokens[0];
const chatContext = msg.chat();
if (token === 'payload') {
value = String(msg.payload);
} else if (chatContext != null && chatContext.get(variable) != null) {
value = chatContext.get(variable);
} else if (!_.isEmpty(context.flow.get(variable))) {
value = context.flow.get(variable);
} else if (!_.isEmpty(context.get(variable))) {
value = context.get(variable);
} else if (!_.isEmpty(context.global.get(variable))) {
value = context.global.get(variable);
} else if (!_.isEmpty(msg[variable])) {
value = msg[variable];
}
return when(value)
.then(value => {
// if nothing found, return empty space
return value != null ? value : when(null);
})
.then(value => {
if (subtokens.length > 0) {
value = extractObjectKeys(value, subtokens.slice(1));
}
return value;
});
};
const replaceTokens = (message, tokens) => {
let stack = new Promise(resolve => resolve(message));
if (tokens != null && tokens.length !== 0) {
// replace all tokens
_(tokens).each(token => {
stack = stack.then(message => {
return new Promise(resolve => {
getTokenValue(token)
.then(value => {
// if value was not fount, leve the token in place
if (value != null) {
// todo take the format date from config
const formattedValue = value instanceof moment ? value.format('DD MMMM,YYYY') : value;
resolve(message.replace(new RegExp(`{{${token}}}`, 'g'), formattedValue));
} else {
resolve(message);
}
});
});
});
});
}
return stack;
};
const renderString = message => {
if (_.isEmpty(message)) {
return message;
}
// extract tokens
let tokens = message.match(/\{\{([A-Za-z0-9\-\._]*?)\}\}/g);
if (tokens != null && tokens.length != 0) {
tokens = tokens.map(token => token.replace('{{', '').replace('}}', ''));
}
// replace them
return replaceTokens(message, tokens);
}
const renderObject = item => {
let task = when(true);
const result = {};
return new Promise(resolve => {
_(item).each((value, key) => {
task = task
.then(() => renderItem(value))
.then(translated => result[key] = translated);
});
task.then(() => resolve(result));
});
};
const renderArray = item => {
let task = when(true);
const result = [];
return new Promise(resolve => {
_(item).each(value => {
task = task
.then(() => renderItem(value))
.then(translated => result.push(translated));
});
task.then(() => resolve(result));
});
}
const renderItem = item => {
if (_.isString(item)) {
return renderString(item);
} else if (!options.preserveNumbers && _.isNumber(item)) {
return renderString(String(item));
} else if (options.preserveNumbers && _.isNumber(item)) {
return item;
} else if (_.isArray(item)) {
return renderArray(item);
} else if (item instanceof Buffer) {
return when(item); // leave buffers untouched
} else if (_.isObject(item)) {
return renderObject(item);
}
return when(item);
}
return function() {
const toTranslate = Array.prototype.slice.call(arguments, 0);
return new Promise(resolve => {
const translated = [];
let task = when(true);
// translate each element of the array
_(toTranslate).each(sentence => {
task = task
.then(() => renderItem(sentence))
.then(message => translated.push(message));
});
// finally
task.then(() => {
if (translated.length === 1) {
resolve(translated[0]);
} else {
resolve(translated);
}
});
});
}
};