From 87dbe3447a003f0ab60b11e1fc49c89799a527b2 Mon Sep 17 00:00:00 2001 From: Manu Artero Anguita Date: Wed, 27 Jan 2021 17:00:16 +0100 Subject: [PATCH] Implement basic logic for 'subscribe' buttons only from memory for now --- config/bot.yaml | 38 ++++++++++++++++++++++++------------- src/bot.js | 50 +++++++++++++++++++++++++++---------------------- src/cards.js | 44 +++++++++++++++++++++++++++++++++++++------ src/storage.js | 44 +++++++++++++++++++++++++++++++++++++++---- types.d.ts | 15 +++++++++++++++ 5 files changed, 146 insertions(+), 45 deletions(-) create mode 100644 types.d.ts diff --git a/config/bot.yaml b/config/bot.yaml index ed27d71..55a8dff 100644 --- a/config/bot.yaml +++ b/config/bot.yaml @@ -1,14 +1,26 @@ cards: - menu: - - title: Label 1 - text: key1 - - title: Label a - text: key2 - - title: Label b - text: key3 - - title: Label c - text: key4 - - title: Label d - text: key5 - - title: Label e - text: key6 + welcomeCard: + title: Private Notifications Center + text: Lorem Ipsum + + unknownCard: + title: "" + text: Unknown command... + + menuCard: + title: Available Options + checkButton: + title: CHECK my subscribed topics ๐Ÿงพ + value: CHECK + resetButton: + title: RESET all my subscribed topics โŒ + value: RESET + subscriptionButtons: + - title: Subscribe to LATAM notifications ๐Ÿ‡ฆ๐Ÿ‡ท + value: LATAM + - title: Subscribe to UK notifications ๐Ÿ‡ฌ๐Ÿ‡ง + value: UK + - title: Subscribe to SPAIN notifications ๐Ÿ‡ช๐Ÿ‡ธ + value: SPAIN + - title: Subscribe to GERMANY notifications ๐Ÿ‡ฉ๐Ÿ‡ช + value: GERMANY diff --git a/src/bot.js b/src/bot.js index a12b38e..3770f9a 100644 --- a/src/bot.js +++ b/src/bot.js @@ -4,29 +4,19 @@ const { readYaml } = require('./yaml') const { log } = require('./log') /** - * @param {string} rawText + * @param {Types.Storage} storage */ -// eslint-disable-next-line no-unused-vars -const getKeyword = rawText => { - const text = rawText.trim().toLocaleLowerCase() - /* if chain */ - if (text.includes('a')) { - return 'a' - } - return undefined -} - -const createBot = conversationReferences => { +const createBot = storage => { const bot = new ActivityHandler() const config = readYaml('bot') const cards = prepareCards({ config }) + log.info('[STARTUP]', 'bot.yaml config read') /** * A party (including the bot) joins or leaves a conversation */ bot.onConversationUpdate(async (context, next) => { - await context.sendTraceActivity('onConversationUpdate', context.activity) - conversationReferences.add(context.activity) + storage.add(context.activity) await next() }) @@ -38,9 +28,7 @@ const createBot = conversationReferences => { const membersAdded = context.activity.membersAdded for (let cnt = 0; cnt < membersAdded.length; cnt++) { if (membersAdded[cnt].id !== context.activity.recipient.id) { - const welcomeMessage = - 'Welcome to the Proactive Bot sample. Navigate to http://localhost:3978/api/notify to proactively message everyone who has previously messaged this bot.' - await context.sendActivity(welcomeMessage) + await context.sendActivities([cards.welcomeCard(), cards.menuCard()]) } } await next() @@ -50,15 +38,33 @@ const createBot = conversationReferences => { * Main handler: Message activity received */ bot.onMessage(async (context, next) => { - conversationReferences.add(context.activity) - - // const keyword = getKeyword(context.activity.text) + storage.add(context.activity) + const username = context.activity.from.name + const text = context.activity.text - await context.sendActivity(cards.menuCard()) + const { check, reset, subscriptions } = cards.registeredKeywords() + if (text === check) { + const info = storage.readUser(username) + await context.sendActivity(JSON.stringify(info)) + } else if (text === reset) { + // TODO + const info = storage.readUser(username) + await context.sendActivity(JSON.stringify(info)) + } else if (subscriptions.indexOf(text) > -1) { + storage.subscribe(context.activity, text) + const info = storage.readUser(username) + await context.sendActivity(JSON.stringify(info)) + } else { + await context.sendActivities([ + cards.unknownCard(), + cards.menuCard() + ]) + } await next() }) - log.info('[STARTUP] bot created and configured') + log.info('[STARTUP] bot ready') + return bot } diff --git a/src/cards.js b/src/cards.js index 472dd3a..aec34c5 100644 --- a/src/cards.js +++ b/src/cards.js @@ -5,19 +5,51 @@ const { CardFactory, MessageFactory, ActionTypes } = require('botbuilder') const asMessage = card => MessageFactory.attachment(card) +const simpleCard = ({ title, text }) => { + const card = CardFactory.heroCard(title, text) + return asMessage(card) +} + +/** + * @param {import('botbuilder').CardAction[]} cardActions + */ +const addDefaultButton = (cardActions, { title, value }) => { + cardActions.push({ + title, + value, + type: ActionTypes.ImBack + }) +} + const prepareCards = ({ config }) => { const menuCard = () => { - const cardActions = config.cards.menu.map(({ title, text }) => ({ + const subscriptionButtons = config.cards.menuCard.subscriptionButtons + const cardActions = subscriptionButtons.map(({ title, value }) => ({ title, - text, - type: ActionTypes.MessageBack + value, + type: ActionTypes.ImBack })) - const card = CardFactory.heroCard('title', 'text', null, cardActions) - return asMessage(card) + addDefaultButton(cardActions, config.cards.menuCard.checkButton) + addDefaultButton(cardActions, config.cards.menuCard.resetButton) + const title = config.cards.menuCard.title + const menuCard = CardFactory.heroCard(title, null, cardActions) + return asMessage(menuCard) + } + + const registeredKeywords = () => { + const menu = config.cards.menuCard + return { + check: menu.checkButton.value, + reset: menu.resetButton.value, + subscriptions: menu.subscriptionButtons.map(({ value }) => value) + } } return { - menuCard + menuCard, + registeredKeywords, + welcomeCard: () => simpleCard(config.cards.welcomeCard), + unknownCard: () => simpleCard(config.cards.unknownCard) } } diff --git a/src/storage.js b/src/storage.js index 041c7ac..71afbbd 100644 --- a/src/storage.js +++ b/src/storage.js @@ -1,15 +1,51 @@ const { TurnContext } = require('botbuilder') const { log } = require('./log') -const db = {} +/* memory object: playground */ +const db = { + conversations: {}, + users: {}, + subscriptions: {} +} const createStorage = () => { return { + /** + * @param {Types.Activity} activity + */ add: activity => { const ref = TurnContext.getConversationReference(activity) - const id = ref.conversation.id - log.debug('[refs] updating conversation #%s', id) - db[id] = ref + const conversationId = ref.conversation.id + log.debug('[db] updating conversation #%s', conversationId) + db.conversations[conversationId] = ref + if (activity.from.role === 'user') { + const username = activity.from.name + log.debug('[db] updating user "%s"', username) + db.users[username] = conversationId + } + }, + /** + * @param {Types.Activity} activity + * @param {string} topic + */ + subscribe: (activity, topic) => { + if (activity.from.role === 'user') { + const username = activity.from.name + log.debug('[db] updating pair: <%s, %s>', username, topic) + if (!db.subscriptions[username]) { + db.subscriptions[username] = [] + } + db.subscriptions[username].push(topic) + } + }, + readUser: username => { + log.debug('[db] reading info for user "%s"', username) + const conversationId = db.users[username] + return { + conversationId, + conversation: db.conversations[conversationId], + subscribedTo: db.subscriptions[username] + } } } } diff --git a/types.d.ts b/types.d.ts new file mode 100644 index 0000000..caaf7ca --- /dev/null +++ b/types.d.ts @@ -0,0 +1,15 @@ +declare namespace Types { + type Activity = import("botbuilder").Activity; + + interface Storage { + add: (activity: Activity) => void; + subscribe: (activity: Activity, topic: string) => void; + readUser: ( + username: string + ) => { + conversationId: string; + conversation: any; + subscribedTo: string[]; + }; + } +}