How to properly handle context.sendActivity? - node.js

I just want to ask two simple questions and then show the card. Problem is, in the second "sendActivity" keeps on repeating "please give password" just forever. I tried to place another onTurn after and even inside the function, with worst or same results. Dont want to implement a whole waterfall just for 2 questions. Which ActivityHandler fits better what am trying to achieve?
async processLogin(context, next, res) {
await context.sendActivity({
text: 'please give username'
})
const SelectedCard2 = CARDS2[0];
this.onTurn(async (context, next, res) => {
let txt = `"${context.activity.text}"`;
if (txt) {
var name = JSON.parse(txt);
console.log(name)
}
await context.sendActivity({
text: 'please give password'
})
let txt2 = `"${context.activity.text}"`;
if (txt2) {
var password = JSON.parse(txt2);
console.log(password)
res = password;
}
await next();
});
}

enter link description hereIf you just want to collect some info from user by an easy , you can use adaptive card in one step, try the code below :
const { ActivityHandler,CardFactory } = require('botbuilder');
class EchoBot extends ActivityHandler {
constructor() {
super();
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
var adaptiveCard = {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": 2,
"items": [
{
"type": "TextBlock",
"text": "Pls type your info here . Don't worry, we'll never share or sell your information.",
"isSubtle": true,
"wrap": true,
"size": "Small"
},
{
"type": "TextBlock",
"text": "Username",
"wrap": true
},
{
"type": "Input.Text",
"id": "username",
"placeholder": "your user name here"
},
{
"type": "TextBlock",
"text": "Password",
"wrap": true
},
{
"type": "Input.Text",
"id": "password",
"placeholder": "makre sure no one is around you ..."
}
]
}
]
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
]
};
this.onMessage(async (context, next) => {
if(context.activity.text==="login"){
await context.sendActivity({ attachments: [CardFactory.adaptiveCard(adaptiveCard)] });
}else if(context.activity.value != undefined){
var user = context.activity.value;
await context.sendActivity("hello , your username : " + user.username + ",password :" + user.password);
}else {
await context.sendActivity("send login to do test");
}
await next();
});
this.onMembersAdded(async (context, next) => {
const membersAdded = context.activity.membersAdded;
for (let cnt = 0; cnt < membersAdded.length; ++cnt) {
if (membersAdded[cnt].id !== context.activity.recipient.id) {
await context.sendActivity('Hello and welcome!');
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
}
module.exports.EchoBot = EchoBot;
This code is based on official nodejs echo bot , just cover the content of bot.js file to test it :
Hope it helps .

Related

Delete Method in Swagger mit SQLite3 und NodeJS

I have OPENAPI, which are build with Swagger. GET und POST methods works finei. but not DELETE.
index.ts
`
app.use("/deleteProduct/{id}", deleteProduct);
delete.ts
import { Router } from "express";
import { Database } from "sqlite3";
import database from "./databaseConnection";
const deleteProduct = Router()
function removeProduct(id: string, db: Database) {
console.log(id);
return new Promise((resolve, reject) => {
db.serialize(() => {
db.run(`DELETE FROM product WHERE product_id = ?`, id, (err) => {
if (err) {
reject (err);
}
resolve ("Success");
})
})
})
}
deleteProduct.delete("/:id", async (req, res) => {
try {
res.json(await removeProduct(req.params.id, database));
} catch (err) {
console.error(`Error removing the product`, err.message);
}
});
export default deleteProduct
`
after pressing "execute" button nothing happens..
Can someone please help me or give a hint, where a mistake can lie.
Here is a swagger.json. May there is something wrong
"/deleteProduct/{id}": {
"delete": {
"tags": ["Delete"],
"description": "Removes product from database",
"produces": "application/json",
"parameters": [
{
"in": "path",
"name": "id",
"description": "Id of the product",
"required": true,
"schema": {
"type": "integer"
}
}],
"responses": {
"200": {
"description": "Product was deleted",
"content": {
"application/json": {
"schema": {
"type": "array"
}

Set Parameters Output Context from Looping on Dialogflow

i make some loop for calling data from firebase, how to set document id as parameters for my output context when i selected the data from document?
this my code for function daftaKota
function daftarKota(agent){
const query = db.collection('kota');
return query.get().then(s =>{
if (s.empty){
agent.add('belum ada kota yang didaftarkan oleh Pemilik');
agent.add('untuk mengakses menu lainnya silahkan ketikan "menu"');
agent.context.set('menu',2);
} else {
agent.add('berikut daftar kota');
s.forEach(doc =>{
agent.add(new Suggestion(doc.data().nama_kota));
agent.context.set('lihat-toko',5,{'id_kota' : doc.id,'nama_kota' : doc.data().nama_kota});
});
}
});
this my code for function daftarToko
function daftarToko (agent){
const context = agent.context.get('lihat-toko');
const idKota = context.parameters.id_kota;
const nKota = agent.parameters.kota;
const query = db.collection('toko').where('id_kota','==',idKota);
return query.get().then(s =>{
if (s.empty){
agent.add('Belum ada Toko yang didaftarkan di kota ini');
agent.add('untuk mengakses kota lainnya silahkan ketikan "kembali"');
agent.context.set('order',2);
}else{
agent.add('berikut daftar toko di kota '+nKota);
s.forEach(doc => {
agent.add(new Card({title : doc.data().nama_toko, imageUrl : doc.data().gambar_toko}));
agent.add(new Suggestion(doc.data().nama_toko));
agent.context.set('lihat-kue',5,{'id_toko' : doc.id});
});
}
});
and this the Intent Map
intentMap.set('Daftar Kota',daftarKota);
intentMap.set('Daftar Toko',daftarToko);
this my intent "Daftar Kota"
this intent show the city from database using suggestion
when i selected the other suggestion city like Yogyakarta, Jakarta, or Bandung, the parameters still set on Banjarmasin.
this my API response after i select Yogyakarta
{
"responseId": "9e1daa4d-31f8-4a62-a939-813be357a634-19db3199",
"queryResult": {
"queryText": "Yogyakarta",
"parameters": {
"kota": "Yogyakarta"
},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [
{
"text": {
"text": [
"Belum ada Toko yang didaftarkan di kota ini"
]
}
},
{
"text": {
"text": [
"untuk mengakses kota lainnya silahkan ketikan \"kembali\""
]
}
}
],
"outputContexts": [
{
"name": "projects/jastip-21e34/agent/sessions/771d2ffc-b490-51f3-7da7-78b91faa8ad3/contexts/order",
"lifespanCount": 2
},
{
"name": "projects/jastip-21e34/agent/sessions/771d2ffc-b490-51f3-7da7-78b91faa8ad3/contexts/lihat-toko",
"lifespanCount": 4,
"parameters": {
"kota": "Yogyakarta",
"nama_kota": "Banjarmasin",
"id_kota": "qCjS54XPf1lAtECUFTTw",
"kota.original": "Yogyakarta"
}
}
],
"intent": {
"name": "projects/jastip-21e34/agent/intents/f14ab0fa-b506-419d-a360-a8eb7cd84b93",
"displayName": "Daftar Toko"
},
"intentDetectionConfidence": 1,
"diagnosticInfo": {
"webhook_latency_ms": 236
},
"languageCode": "id"
},
"webhookStatus": {
"message": "Webhook execution successful"
}
}
see at paramers :
i selected "kota : Yogyakarta",
but the id_kota is the document id of nama_kota "Banjarmasin", not the id of "Yogyakarta"
You're not showing the query that you're using, or where you're storing the parameters you get, but in your loop you're not actually checking to see if nama_kota matches the kota that is sent through the parameters. So it is changing the context every time it goes through the loop, and ends up with the new parameters from the last time through.
One solution would be to check if they match and, when they do, set the context.
s.forEach(doc =>{
agent.add(new Suggestion(doc.data().nama_kota));
if( parameters.kota === doc.data().nama_kota ){
agent.context.set('lihat-toko',5,{'id_kota' : doc.id,'nama_kota' : doc.data().nama_kota});
}
});

Microsoft teams bot adaptive card carousel deleting a card

I am using Microsoft teams bot with nodejs. I am rendering a carousel of adaptive cards with action on each card. My requirement is to delete an individual card out on which the action was clicked. Is it possible?
Current code looks like below. i have given a try to deleteActive but that deletes entire carousel
const {
TurnContext,
TeamsActivityHandler,
CardFactory,
AttachmentLayoutTypes,
ActionTypes
} = require('botbuilder');
class TeamsConversationBot extends TeamsActivityHandler {
constructor() {
super();
this.onMessage(async (context:any, next:any) => {
TurnContext.removeRecipientMention(context.activity);
console.log("context activigty at the begin is:" + JSON.stringify(context.activity))
let msg = context.activity.text
let action = context.activity.value
if(msg.startsWith('lead')){
msg = 'lead'
}
if(action !== undefined){
console.log("user did some action on a card")
msg = action.action
}
switch (msg) {
case 'lead':
await this.lead(context)
break;
case 'qualify_lead':
await this.qualifyLead(context)
break;
}
await next();
});
}
/**
*
* #param context this method does a lead qualification
*/
async qualifyLead(context:any){
console.log("in qualifyLead:" + JSON.stringify(context.activity))
//await context.deleteActivity(context.activity.replyToId)
const leadId = context.activity.value.objectId
console.log("Lead to qualify is:" + leadId)
await context.sendActivity('Lead is qualified')
}
/**
* Search contact by name
* #param context
* #param keyword
*/
async lead(context:any){
console.log("Start of lead with context:" + JSON.stringify(context))
const cardArr = []
let items = [
{"Name": 'x', "LeadId": "1"},
{"Name": 'a', "LeadId": "2"},
{"Name": 'b', "LeadId": "3"},
{"Name": 'c', "LeadId": "4"},
{"Name": 'd', "LeadId": "5"}
]
for(const item of items){
const header = {
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": item.Name
}
const actions = [
{
"type": "Action.Submit",
"title": "Qualify",
"data": { "action" : "qualify_lead", "objectId" : item.LeadId }
}
]
const acard = CardFactory.adaptiveCard(
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
header,
''
],
"actions": actions
}
)
cardArr.push(acard)
console.log("payload is::::" + JSON.stringify(acard))
}
const reply = {
"attachments" : cardArr,
"attachmentLayout" : AttachmentLayoutTypes.Carousel
}
await context.sendActivity(reply);
}
}
module.exports.TeamsConversationBot = TeamsConversationBot;
As with this other answer, the answer will be similar to this one. I can see you're trying to use TypeScript but your code deviates very little from JavaScript so I'll just write my answer in JavaScript.
First, you'll need a way of saving state for your [carousel] so you can update the [carousel]'s activity.
this.carouselState = this.conversationState.createProperty('carouselState');
You'll want a consistent way to generate your [carousel] that you can use when you send the [carousel] initially and when you update the [carousel].
createCarousel(batchId, leads)
{
const cardArr = [];
let items = [
{ "Name": 'x', "LeadId": 1 },
{ "Name": 'a', "LeadId": 2 },
{ "Name": 'b', "LeadId": 3 },
{ "Name": 'c', "LeadId": 4 },
{ "Name": 'd', "LeadId": 5 }
];
items = items.filter(item => leads.includes(item.LeadId));
for (const item of items) {
const header = {
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": item.Name
};
const actions = [
{
"type": "Action.Submit",
"title": "Qualify",
"data": { [KEYACTION]: ACTIONQUALIFYLEAD, [KEYOBJECTID]: item.LeadId, [KEYBATCHID]: batchId }
}
];
const acard = CardFactory.adaptiveCard(
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
header
],
"actions": actions
}
);
cardArr.push(acard);
}
return {
"type": "message",
"attachments": cardArr,
"attachmentLayout": AttachmentLayoutTypes.Carousel
};
}
This is similar to your code but there are some important differences. First, I'm filtering the items array to allow for fewer items, which is how you'll end up deleting cards from your carousel. Second, I'm including a "batch ID" in each action's data, which is how your bot will know which activity to update when it receives the action's payload. Also, this isn't relevant to your question but I'm using string constants instead of string literals most everywhere I expect to use that string more than once, which is a practice I follow to avoid typo-related bugs etc.
Using this function, you can send the [carousel] initially like this
async testCarousel(turnContext) {
const batchId = Date.now();
const leads = [1, 2, 3, 4, 5];
const reply = this.createCarousel(batchId, leads);
const response = await turnContext.sendActivity(reply);
const dict = await this.carouselState.get(turnContext, {});
dict[batchId] = {
[KEYACTIVITYID]: response.id,
[KEYLEADS]: leads
};
}
And you can update the [carousel] in response to the card's [qualify] submit action like this
async handleSubmitAction(turnContext) {
const value = turnContext.activity.value;
switch (value[KEYACTION]) {
case ACTIONQUALIFYLEAD:
const dict = await this.carouselState.get(turnContext, {});
const batchId = value[KEYBATCHID];
const info = dict[batchId];
if (info) {
const leads = info[KEYLEADS];
const objectId = value[KEYOBJECTID];
var index = leads.indexOf(objectId);
if (index !== -1) leads.splice(index, 1);
const update = this.createCarousel(batchId, leads);
update.id = info[KEYACTIVITYID];
if (update.attachments.length) {
await turnContext.updateActivity(update);
} else {
await turnContext.deleteActivity(update.id);
}
}
break;
}
}

Null value in model.findById when I make a get request [mongodb]

Problem
Hi dev,
I have the problem that when I try to make a get request to the series by id it shows me null.
I have noticed from the Atlas Mongos platform that I created the collection but it does not show me the data, only the structure of the scheme shows me
Function.js
const fs = require('fs');
const fetch = require('node-fetch');
const BASE_URL = " http://localhost:8081/api/v1/"
async function getSeries() {
return new Promise((resolve , reject) =>{
setTimeout(() => {
const res = require('./simple_database/series/1.json' , 'utf8');
resolve(res)
}, 1000);
})
}
module.exports = {
getSeries
}
Router
The route allseries allows me to access all the content. What I want to do is pass that content to the SeriesModel, maybe it is there where I have the problem that the data is not being inserted correctly.
In the route series/:id is where the null value is returning to me
const express = require('express');
const router = express.Router();
const f = require('./function');
const SeriesModel = require('./models/series');
router.get('/allseries', (req, res) => {
f.getSeries().then((series) =>{
res.status(200).json({
series
})
}).then((doc) =>{
SeriesModel.insertMany(doc , function(err , docs){
if(err){
console.error(err)
}else{
console.log(docs);
console.info('%d serie were successfully stored.', docs.length);
}
})
})
});
router.get('/series/:id' , (req , res , next) =>{
const id = req.params.id;
SeriesModel.findById(id)
.exec()
.then((doc) =>{
console.log("From database " , doc);
res.status(200).json(doc)
}).catch((err) =>{
console.error(err);
res.status(500).json({error: err})
})
})
module.exports = router;
Model/series.js
const mongoose = require('mongoose');
const serieSchema = mongoose.Schema({
"_id": {
"$oid": {
"type": "ObjectId"
}
},
"series_id": {
"type": "String"
},
"aggregateRating": {
"reviewCount": {
"type": "Number"
},
"ratingCount": {
"type": "Number"
},
"#type": {
"type": "String"
},
"ratingValue": {
"type": "Number"
}
},
"episodes": {
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
},
"metadata": {
"description": {
"type": "String"
},
"url": {
"type": "String"
},
"image": {
"type": "String"
},
"type": {
"type": "String"
},
"id": {
"type": "String"
},
"name": {
"type": "String"
}
},
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
});
module.exports = mongoose.model("cr_series" , serieSchema);
It is because findById takes it's parameter in form of object like this
SeriesModel.findById({_id:id})
You need to tell your query to which json object you want to match your incoming object.

Can we add text field dynamically

I've created an adaptive card(using json) in my chatbot that takes input from users. I want to add a button that enables the user to add a new text field every time the user clicks on the insert field. (i.e., the user can click on insert button to enter details of education (school, college etc.))
Can this be achieved in adaptive cards?
I also wanted to know, can adaptive cards be designed in any other language (excluding json)
The easiest way to do this is with Action.ShowCard:
{
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 1",
"id": "text1"
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Add field",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 2",
"id": "text2"
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Add field",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 3",
"id": "text3"
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Add field",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 4",
"id": "text4"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
You may not like the way that looks, but there is an alternative. Microsoft Teams allows you to update messages, so you can update the card with more input fields in response to a submit action. First, you'll need a way of saving state for your card so you can update the card's activity. In C# you can declare a state property accessor like this:
public IStatePropertyAccessor<Dictionary<string, (string ActivityId, int InputCount)>> InputCardStateAccessor { get; internal set; }
Then you can instantiate it like this:
InputCardStateAccessor = _conversationState.CreateProperty<Dictionary<string, (string, int)>>("cardState");
In Node.js you won't need to declare anything but you can instantiate it like this:
this.inputCardState = this.conversationState.createProperty('cardState');
You'll want a consistent way to generate your card that you can use when you send the card initially and when you update the card. I'm using the AdaptiveCards NuGet package in C#:
public static IActivity GenerateAdaptiveCardActivityWithInputs(int inputCount, object valueObject)
{
var cardData = JObject.FromObject(valueObject);
var cardId = Convert.ToString(cardData[KEYCARDID]);
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0))
{
Body = Enumerable.Range(0, inputCount).Select(i =>
{
var inputId = $"text{i}";
return new AdaptiveTextInput
{
Id = inputId,
Value = Convert.ToString(cardData[inputId]),
};
}).ToList<AdaptiveElement>(),
Actions = new List<AdaptiveAction>
{
new AdaptiveSubmitAction
{
Title = "Add field",
Data = new Dictionary<string, string>
{
{ KEYCARDID, cardId },
{ KEYSUBMITACTIONID, ACTIONSUBMITADDFIELD },
},
},
new AdaptiveSubmitAction
{
Title = "Submit",
},
},
};
return MessageFactory.Attachment(new Attachment(AdaptiveCard.ContentType, content: JObject.FromObject(card)));
}
Node.js:
generateAdaptiveCardActivityWithInputs(inputCount, cardData) {
var cardId = cardData[KEYCARDID];
var body = [];
for (let i = 0; i < inputCount; i++) {
var inputId = `text${i}`;
body.push({
type: "Input.Text",
id: inputId,
value: cardData[inputId]
});
}
var card = {
type: "AdaptiveCard",
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
version: "1.0",
body,
actions: [
{
type: "Action.Submit",
title: "Add field",
data: {
[KEYCARDID]: cardId,
[KEYSUBMITACTIONID]: ACTIONSUBMITADDFIELD
},
},
{
type: "Action.Submit",
title: "Submit",
}
]
};
return MessageFactory.attachment(CardFactory.adaptiveCard(card));
}
Using this function, you can send the card initially like this in C#:
var inputCount = 1;
var cardId = Guid.NewGuid().ToString();
var reply = GenerateAdaptiveCardActivityWithInputs(inputCount, new Dictionary<string, string> { { KEYCARDID, cardId } });
var response = await turnContext.SendActivityAsync(reply, cancellationToken);
var dict = await InputCardStateAccessor.GetAsync(turnContext, () => new Dictionary<string, (string, int)>(), cancellationToken);
dict[cardId] = (response.Id, inputCount);
Node.js:
var inputCount = 1;
var cardId = Date.now().toString();
var reply = this.generateAdaptiveCardActivityWithInputs(inputCount, { [KEYCARDID]: cardId });
var response = await turnContext.sendActivity(reply);
var dict = await this.inputCardState.get(turnContext, {});
dict[cardId] = {
activityId: response.id,
inputCount: inputCount
};
await this.inputCardState.set(turnContext, dict);
And you can update the card in response to the card's "add field" submit action like this in C#:
private async Task AddFieldAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
if (activity.ChannelId == Channels.Msteams)
{
var value = JObject.FromObject(activity.Value);
var cardId = Convert.ToString(value[KEYCARDID]);
var dict = await InputCardStateAccessor.GetAsync(turnContext, () => new Dictionary<string, (string, int)>(), cancellationToken);
if (dict.TryGetValue(cardId, out var cardInfo))
{
var update = GenerateAdaptiveCardActivityWithInputs(++cardInfo.InputCount, value);
update.Id = cardInfo.ActivityId;
update.Conversation = activity.Conversation;
await turnContext.UpdateActivityAsync(update, cancellationToken);
dict[cardId] = cardInfo;
}
}
}
Node.js:
async addField(turnContext) {
var activity = turnContext.activity;
if (activity.channelId == 'msteams') {
var value = activity.value;
var cardId = value[KEYCARDID];
var dict = await this.inputCardState.get(turnContext, {});
var cardInfo = dict[cardId];
if (cardInfo) {
var update = this.generateAdaptiveCardActivityWithInputs(++cardInfo.inputCount, value);
update.id = cardInfo.activityId;
update.conversation = activity.conversation;
update.serviceUrl = activity.serviceUrl;
dict[cardId] = cardInfo;
await this.inputCardState.set(turnContext, dict);
await turnContext.updateActivity(update);
}
}
}
yes this is possible you can look about the addRow in javascript

Resources