Skip to content

Commit

Permalink
added dynamic cards
Browse files Browse the repository at this point in the history
  • Loading branch information
khanh1902 committed Jun 24, 2024
1 parent a6c02b3 commit 90e2c90
Show file tree
Hide file tree
Showing 6 changed files with 516 additions and 308 deletions.
2 changes: 1 addition & 1 deletion actions/CheckVariable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { ComponentDialog, WaterfallDialog } = require('botbuilder-dialogs');
const { REPLACE_ACTION, CHECK_VARIABLE } = require('../Constant');
const { CHECK_VARIABLE } = require('../Constant');

const CHECKVARIABLE_WATERFALL = 'CHECKVARIABLE_WATERFALL';

Expand Down
10 changes: 3 additions & 7 deletions actions/Prompting.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ const {
const { PROMPTING } = require('../Constant');
const {
getPrompt,
replaceData,
formatMessage,
getExtendTypeMessage,
} = require('../utils/utils');
} = require('../utils/prompts');
const { replaceData } = require('../utils/utils');
const { translate } = require('../services/translate');
const { predict } = require('../services/intent');
const { ERROR_MESSAGE } = process.env;
Expand Down Expand Up @@ -88,11 +88,7 @@ class Prompting extends ComponentDialog {
conversationData,
});

const extendType = await getExtendTypeMessage(
contents,
language,
conversationData.channelId
);
const extendType = await getExtendTypeMessage(contents, conversationData);

if (
extendType &&
Expand Down
45 changes: 35 additions & 10 deletions actions/SendText.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
const { ComponentDialog, WaterfallDialog } = require('botbuilder-dialogs');
const { SEND_TEXT } = require('../Constant');
const { getPrompt, replaceData, formatMessage, getExtendTypeMessage } = require('../utils/utils');
const {
getPrompt,
formatMessage,
getExtendTypeMessage,
} = require('../utils/prompts');
const { replaceData } = require('../utils/utils');
const { translate } = require('../services/translate');
const { ERROR_MESSAGE } = process.env;

Expand All @@ -10,7 +15,9 @@ class SendText extends ComponentDialog {
constructor(dialog) {
super(SEND_TEXT);
this.dialog = dialog;
this.addDialog(new WaterfallDialog(SENDTEXT_WATERFALL, [this.SendTextAction.bind(this)]));
this.addDialog(
new WaterfallDialog(SENDTEXT_WATERFALL, [this.SendTextAction.bind(this)])
);
this.initialDialogId = SENDTEXT_WATERFALL;
}

Expand All @@ -19,28 +26,46 @@ class SendText extends ComponentDialog {

console.log(`[SendText] ${name}`);

const conversationData = await this.dialog.conversationDataAccessor.get(step.context);
const conversationData = await this.dialog.conversationDataAccessor.get(
step.context
);

const { language } = conversationData;

let msg = getPrompt(contents, language);

msg.message = replaceData({ text: msg.message, data: conversationData.variables });
msg.message = replaceData({
text: msg.message,
data: conversationData.variables,
});

// translate
msg.message = await translate(msg.message, msg.language, language);

msg = formatMessage({ data: (msg && msg.message) || '', conversationData, type: msg.type, extend });

const extendType = await getExtendTypeMessage(contents, language, conversationData.channelId);

if (extendType && Array.isArray(extendType.data) && extendType.data.length) {
msg = formatMessage({
data: (msg && msg.message) || '',
conversationData,
type: msg.type,
extend,
});

const extendType = await getExtendTypeMessage(
contents,
language,
conversationData.channelId
);

if (
extendType &&
Array.isArray(extendType.data) &&
extendType.data.length
) {
msg.channelData.extendData = extendType.data;

msg.channelData.type = extendType.type;
}

if(!msg.message && !extendType) msg.text = ERROR_MESSAGE;
if (!msg.message && !extendType) msg.text = ERROR_MESSAGE;

await step.context.sendActivity(msg);

Expand Down
252 changes: 252 additions & 0 deletions utils/cards.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
const { translate } = require('../services/translate');
const { replaceData } = require('./utils');

module.exports = class Cards {
constructor(cards, currentLanguage, conversationData) {
const { channelId, variables, language } = conversationData;
this.channelId = channelId;
this.cardsData = this.parseCards(cards);
this.currentLanguage = currentLanguage;
this.variables = variables;
this.defaultLanguage = language;
this.replaceCardsData();
}

parseCards(cards) {
try {
if (!cards) throw new Error('Cards is empty!');

let result = typeof cards === 'string' ? JSON.parse(cards) : cards;

if (typeof result === 'object' && !Array.isArray(result)) {
result = [result];
}

if (!Array.isArray(result) || !result.length)
throw new Error(`Cards must be an array or cards is empty!`);

return result;
} catch (e) {
console.log(`[parseCards] parse cards failed: ${e.message}`);
return;
}
}

replaceCardsData() {
if (!this.cardsData) return;
try {
this.cardsData = this.cardsData.map((card) => {
let { title, imageUrl, subtitle, buttons } = card;

const newData = {
title: replaceData({ text: title, data: this.variables }),
imageUrl: replaceData({ text: imageUrl, data: this.variables }),
subtitle: replaceData({ text: subtitle, data: this.variables }),
};

if (!Array.isArray(buttons) || !buttons.length) return { ...newData };

return {
...newData,
buttons:
Array.isArray(buttons) &&
buttons.length &&
buttons.map((button) => {
return {
...button,
label: replaceData({
text: button.label,
data: this.variables,
}),
};
}),
};
});
} catch (e) {
console.log(`[replaceCardsData] replace data failed: ${e.message}`);
return;
}
}

async formatCards() {
let results = [];
try {
switch (this.channelId) {
case 'WEB':
case 'MSG':
results = await this.formatWebMsg();
break;
case 'LIN':
results = await this.formatLINE();
break;
default:
break;
}
} catch (err) {
console.log('[formatCards] format failed: ' + err.message || err);
}
return results;
}

async formatWebMsg() {
let result = [];
if (!Array.isArray(this.cardsData) || !this.cardsData.length) return result;

for (const data of this.cardsData) {
try {
let { title, subtitle, imageUrl, buttons } = data;

if (!title || !imageUrl || !subtitle) throw new Error('Missing parameter');

subtitle =
this.currentLanguage !== this.defaultLanguage
? await translate(
subtitle,
this.currentLanguage,
this.defaultLanguage
)
: subtitle;

const card = {
title: title.slice(0, 80),
image_url: imageUrl,
subtitle: subtitle.slice(0, 80),
}

buttons =
buttons &&
(await this.formatWebMsgButtons(
buttons,
this.currentLanguage,
this.defaultLanguage
));
if (buttons.length) card.buttons = buttons;
result.push(card);
} catch (error) {
console.log(
'[formatWebMsg] format card messenger or web failed: ' + error.message
);
}
}
return result.slice(0, 10);
}

async formatWebMsgButtons(buttons, currentLanguage, defaultLanguage) {
let result = [];
if (!Array.isArray(buttons) || !buttons.length) return result;

for (const button of buttons) {
try {
const { value, type, label } = button;

if (!value || !type || !label) throw new Error('Missing parameter');

const translateLabel =
currentLanguage !== defaultLanguage
? await translate(label, currentLanguage, defaultLanguage).slice(0, 20)
: label.slice(0, 20);

switch (type) {
case 'url':
result.push({
type: 'web_url',
url: value,
title: translateLabel,
});
break;
case 'postback':
result.push({
type: 'postback',
payload: value.slice(0, 1000),
title: translateLabel,
});
break;
default:
break;
}
} catch (error) {
console.log('[formatWebMsgButtons] format failed: ' + error.message);
}
}

return result.slice(0, 3);
}

async formatLINE() {
let result = [];
if (!Array.isArray(this.cardsData) || !this.cardsData.length) return result;

for (let data of cards) {
try {
let { title, subtitle, imageUrl, buttons } = data;

if (!title || !imageUrl || !subtitle) throw new Error('Missing parameter');

subtitle =
this.currentLanguage !== this.defaultLanguage
? await translate(
subtitle,
this.currentLanguage,
this.defaultLanguage
)
: subtitle;

const card = {
title: title.slice(0, 40),
thumbnailImageUrl: imageUrl,
text: subtitle.slice(0, 60),
};

buttons = buttons && (await this.formatLINEButtons(buttons));
if (buttons.length) card.actions = buttons;

result.push(card);
} catch (error) {
console.log('[formatLINE] format cards failed: ' + error.message);
}
}
return result.slice(0, 10);
}

async formatLINEButtons(buttons, currentLanguage, defaultLanguage) {
let result = [];
if (!Array.isArray(buttons) || !buttons.length) return result;

for (let button of buttons) {
try {
const { value, type, label } = button;

if (!value || !type || !label) throw new Error('Missing parameter');

const translateLabel =
currentLanguage !== defaultLanguage
? await translate(label, currentLanguage, defaultLanguage).slice(0, 20)
: label.slice(0, 20);

switch (type) {
case 'url':
result.push({
type: 'uri',
uri: value,
label: translateLabel,
});
break;
case 'postback':
result.push({
type: 'postback',
data: value,
label: translateLabel,
displayText: translateLabel,
});
break;
default:
break;
}
} catch (error) {
console.log('[formatLINEButtons] format failed: ' + error.message);
}
}

return result.slice(0, 3);
}
};
Loading

0 comments on commit 90e2c90

Please sign in to comment.