asksuite-core
Version:
251 lines (197 loc) • 6.67 kB
JavaScript
const Util = require('./util');
const RequestPreprocessor = require('./util/RequestPreprocessor');
const _ = require('lodash');
const md5 = require('md5');
const IntentResolver = require('./util/IntentResolver');
const AsksuiteTranslator = require('./util/Translator');
const AsksuiteUtil = require('./asksuite.util')({});
const DEFAULT_LANGUAGE = 'pt-br';
class AsksuiteProcessingNaturalUtils {
static get EXPIRATION_TIME() {
return 60 * 60 * 24;
} // 24 hrs
constructor(config) {
this.config = config;
if (!_.isEmpty(_.get(this.config, 'redis'))) {
this.redis = this.config.redis;
}
}
withRedis() {
return !_.isEmpty(_.get(this.config, 'redis'));
}
resolveLanguage(paramLanguage, languages) {
let language;
if (languages) {
language = _.find(languages, _.matchesProperty('formatIso', paramLanguage));
if (language) {
return language.formatIso;
}
language = _.find(
languages,
_.matchesProperty('parentLanguage', paramLanguage.substring(0, 2)),
);
if (language) {
return language.formatIso;
}
}
language = paramLanguage.toLowerCase();
if (language.startsWith('pt')) {
return DEFAULT_LANGUAGE;
} else if (language.startsWith('en')) {
return 'en-us';
} else if (language.startsWith('es')) {
return 'es-es';
} else {
return language;
}
}
async getInCacheDialogflow(request) {
if (!this.withRedis()) {
return Promise.resolve(null);
}
const hash = this.generateHash(request);
const language = this.resolveLanguage(request.language, request.languages);
const data = await this.getInCache(`dialogflow:${language}:${hash}`);
if (!data) {
return Promise.resolve(null);
}
const { intent, extractedData } = data;
console.log(`OBTAINED BY CACHE ${intent}`);
return { intent, extractedData };
}
findDialogByIntent(request, intent) {
const { intents, companyId, text } = request;
const intentResolver = new IntentResolver(intents, companyId, text);
return intentResolver.findDialogByIntent(intent);
}
generateHash(request) {
const { text } = request;
const plainText = _.chain(text)
.deburr() // special characters
.toLower() // lowercase
.value() // get value from chain
.replace(/\s{2,}/g, ' ') // remove duplicate spaces
.replace(/[^\w\/@ ]/g, ''); // remove all characters dont in (word, /, @, space)
return md5(plainText || text);
}
async saveInCacheDialogflow(intent, request) {
if (!this.withRedis()) {
return Promise.resolve(null);
}
if (Util.isNaoentendi(intent.intent)) {
return Promise.resolve(null);
}
const { text } = request;
const hash = this.generateHash(request);
const language = this.resolveLanguage(request.language, request.languages);
return this.setInCache(`dialogflow:${language}:${hash}`, {
intent: intent.intent,
extractedData: intent.extractedData,
text,
hash,
});
}
async removeInCacheDialogflow({ text, language }) {
if (!this.withRedis()) {
return Promise.resolve(null);
}
const normalizedLanguage = this.resolveLanguage(language);
const hash = this.generateHash({ text });
const key = `dialogflow:${normalizedLanguage}:${hash}`;
return this.removeInCache(key);
}
async removeInCache(key) {
return this.redis.remove(`${key}`);
}
async getInCache(key) {
return this.redis.getValue(`${key}`);
}
async setInCache(key, intent) {
return this.redis.setValueWithExpirationTime(
`${key}`,
intent,
true,
AsksuiteProcessingNaturalUtils.EXPIRATION_TIME,
);
}
async translateRequest(request) {
if (
this.resolveLanguage(request.language, request.languages) !== DEFAULT_LANGUAGE &&
!request.translation
) {
request.translated = true;
const asksuiteTranslator = new AsksuiteTranslator(
request.config.INFOCHAT_DATA_SERVER,
request.config.GOOGLE_TRANSLATE_KEY,
);
const { translation } = await asksuiteTranslator.googleTranslateIfNeeded(
request.text,
request.language,
request.languages,
);
request.translation = translation;
}
}
defineRegexLanguages(languages) {
if (!languages || _.isEmpty(languages)) {
return new RegExp('(pt|en|es|fr)', 'i');
}
const joinedLanguages = _.chain(languages)
.split(formatIso => formatIso.split('-')[0])
.uniq()
.join('|')
.value();
return new RegExp(`(${joinedLanguages})`, 'i');
}
async detectLanguage(request) {
const { GOOGLE_TRANSLATE_KEY } = request.config;
const [detection] = await AsksuiteTranslator.detectLanguage(GOOGLE_TRANSLATE_KEY, request.text);
const re = this.defineRegexLanguages(request.companyLanguages);
const fallbackLanguage = 'en';
const languageCode = detection.language;
const isSupported = re.test(languageCode);
const outputLanguageCode = isSupported
? this.resolveLanguage(languageCode, request.languages)
: fallbackLanguage;
const language = await AsksuiteTranslator.getLanguageByCode(
GOOGLE_TRANSLATE_KEY,
AsksuiteUtil.resolveLanguageGoogle(languageCode, request.languages, 'formatGoogle'),
AsksuiteUtil.resolveLanguageGoogle(outputLanguageCode, request.languages, 'formatGoogle'),
);
return Object.assign(detection, {
isSupported,
language,
fallbackLanguage,
});
}
async copyRequestToDefaultLanguage(request) {
await this.translateRequest(request);
const toSend = _.clone(request);
toSend.text = toSend.translation || toSend.text;
toSend.language = DEFAULT_LANGUAGE;
return RequestPreprocessor.process(toSend);
}
async findIntentDialogFlow(dialogFlowAccessor, request, toDefaultLanguage) {
if (toDefaultLanguage) {
await this.translateRequest(request);
}
let fromCache = false;
let toSend = request;
if (toDefaultLanguage) {
toSend = await this.copyRequestToDefaultLanguage(request);
}
let intent = await this.getInCacheDialogflow(toSend);
if (!intent) {
intent = await dialogFlowAccessor.resolveText(toSend);
await this.saveInCacheDialogflow(intent, toSend);
} else {
fromCache = true;
}
const extractedData = intent.extractedData;
intent = this.findDialogByIntent(toSend, intent.intent);
intent.fromCache = fromCache;
intent.extractedData = extractedData;
return intent;
}
}
module.exports = AsksuiteProcessingNaturalUtils;