Telegram bot - Remove keyboard - Nodejs - node.js

I'm facing a very strange issue that is probably linked to cache.
So here it is .
I have developped a bot in nodejs for telegram.
This bot HAD in the past a custom keyboard that was not a "inline_keyboard"
I decided to change that behaviour and have implemented inline_keyboard.
current code is something like that :
var options = {
parse_mode: "Markdown",
disable_web_page_preview: true,
reply_markup: JSON.stringify({
inline_keyboard: [
[{
text: '🇫🇰 English',
callback_data: "SET ENGLISH"
},{
text: '🇫🇷 Français',
callback_data: "SET FRENCH"
}]
]
})
};
bot.sendMessage(msg.chat.id, "Please choose your language",options);
Inline_keyboard is working fine but my old code (that has been deleted) is still appearing to my users and is very anoying.
Here it is; it keeps on appearing when my users log into my chat.
I have been the following ressources :
https://core.telegram.org/bots/api#replykeyboardremove
How do you remove reply keyboard without sending a message in Telegram?
https://core.telegram.org/bots/api#editmessagereplymarkup
But i don't see how to implement it so I can remove this annoying chat for my users.
Any suggestions ?
Thx for your support

there may be different solutions to do so, my suggestion:
you can use the very first answer of each user to remove keyboard, first use editMessageText to remove keyboard and then send him the appropriate answer.(note that persist chatIDs that you have removed their keyboard, so you will do this for each user just once)
bot.on('callback_query', function onCallbackQuery(callbackQuery) {
if(!didWeRemoveHisKeyboard(callbackQuery.from.id))
removeHisKeyboard(callbackQuery)
//then handle the user response
})
removeHisKeyboard = function(callbackQuery){
bot.editMessageText(callbackQuery.message.text,
{message_id:callbackQuery.message.message_id , chat_id:callbackQuery.from.id,
reply_markup: {
remove_keyboard: true
}}).catch((err) => {
//some error handling
}).then(function(res){
if(res)
addThisChatToHandledList(callbackQuery.from.id)
})
}
note that you may need some modification on this code based on the node_module you're using.

Related

Problems with v-select and data

Good afternoon friends, then I'm going to tell you my mistake. I am working for a users screen, where you have your registration and editing. When you enter the register section you don't have to show the role you already have in the database. But on the edit screen, it already has a role assigned, so I would have to bring it to it (as it does with the text-fields) But I can't get it to load them.
attached images of the screen:
When I do a debugger as soon as I enter the screen, I realize that it is returning the roles as an array. And it shows me the role well, but it doesn't place it in the select.
Attached image of the error, below the images is the code that I use.
<v-select
label="Rol"
v-model="form.rolId"
:items="roles"
item-value="value"
item-text="text"
:rules="[(v) => !!v || 'Este campo es requiredo']"
required
>
</v-select>
data() {
return {
form: {},
roles: [
{ value: "Agente", text: "Agente" },
{ value: "Administrador", text: "Administrador" },
],
};
},
You have to put a default value on your v-model for it to load the data in the select.
see codepen here

How to send hero cards to the fb messenger using microsoft bot framework while using waterfall dialogs (Node js)

I am trying to create a messenger bot using Microsoft bot framework
I am using a waterfall dialog to create the flow of the structure.
In this, I have multiple steps were in a particular step I need to send a carousel of four hero cards with buttons for each.
I have used the answer by steven,
Handling HeroCards responses In Microsoft Bot Framework v4 for NodeJS
I work fine while testing in bot emulator and webchat
But produces an error while testing in messenger bot
can anyone help me to rectify this error, Thanks in advance
async locationStep(step) {
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
// Running a prompt here means the next WaterfallStep will be run when the user's response is received.
await this.sendIntroCard(step)
await step.context.sendActivity("How often do you use surface on the move?")
let acard =CardFactory.heroCard(
" ",
[`https://scontent.fmaa1-4.fna.fbcdn.net/v/t1.0-9/89121134_2372258766207358_5255590702309441536_n.jpg?_nc_cat=109&_nc_sid=8024bb&_nc_ohc=1cHak5WO_yoAX-VdtfO&_nc_ht=scontent.fmaa1-4.fna&oh=fd002544bc74bf53ae0185f4c192efe6&oe=5E82E09B`],
[{ type: ActionTypes.PostBack,
title: 'Never',
value: 'Never'}]
);
let bcard =CardFactory.heroCard(
" ",
['https://i.imgur.com/m2DWB7m.jpg'],
[{ type: ActionTypes.PostBack,
title: 'Once in a while',
value: 'Once in a while'}]
);
let ccard =CardFactory.heroCard(
" ",
['https://i.imgur.com/Kwn0FBn.jpg'],
[{ type: ActionTypes.PostBack,
title: 'A few days a week',
value: 'A few days a week'}]
);
let dcard =CardFactory.heroCard(
" ",
['https://i.imgur.com/mAlW0Bv.jpg'],
[{ type: ActionTypes.PostBack,
title: 'Every day',
value: 'Every day'}]
);
await step.context.sendActivity( {attachments:[acard,bcard,ccard,dcard],attachmentLayout: AttachmentLayoutTypes.Carousel
});
return await { status: DialogTurnStatus.waiting };
}
Your issue is caused by the space you have included as the title of your hero cards: " ". Fixing your problem is simple. You can using an actually empty string without the space ("") or even omitting the title altogether.
EDIT: As you've seen, the Bot Framework will add "Options" as the card's title if you haven't provided one because it uses Facebook Messenger's generic template which requires a title. There is nothing the Bot Framework can do and there's nothing you can do to bypass Facebook's API restrictions. However, if you really want to send a card with an image and buttons then you can use a media template. This will be inconvenient because you'll need to upload the image attachment beforehand so you can get an attachment ID using this API: https://developers.facebook.com/docs/messenger-platform/reference/attachment-upload-api
Rather than having your bot upload the images every time it needs to use them, you should be able upload each image once on your own and then give the ID's to your bot. Once you've uploaded your attachments, you can send a media template directly using the Send API or using the Bot Framework activity's channel data according to these instructions: https://blog.botframework.com/2017/03/28/custom-channel-data/
await step.context.sendActivity( {
"channelData": {
"attachment": {
"type": "template",
"payload": {
"template_type": "media",
"elements": [
{
"media_type": "image",
"attachment_id": "<YOUR_ATTACHMENT_ID>",
"buttons": [
{
"type": "postback",
"payload": "Never",
"title": "Never"
}
]
},
// More media templates ...
]
}
}
}
} );
Since this may be more complicated than you'd like, you might consider an alternative design like Messenger's quick replies.

Is it possible to remove title in Dialogflow Facebook quick replies and print only the quick replies?

Is it possible to configure the response in DialogFlow in such way that
when I enter the Facebook quick reply I will leave the title blank?
I want the user to get only the options without a title.
For example if I have:
Title: Choose an item
Quick reply 1: A
Quick reply 2: B
Quick reply 3: C
Can I somehow remove the title from displaying on FB?
Please advise.
If you see quick reply payload docs ,
The properties are,
text String
Non-empty message text to send with the quick replies. text or
attachment must be set.
attachment Object
An attachment to send with the quick replies. text or attachment must
be set.
quick_replies Array
An array of objects the describe the quick reply buttons to send. A
maximum of 13 quick replies are supported.
Format must be like,
{
"text": "Pick a color:", // non-empty string
"quick_replies":[
{
"content_type":"text",
"title":"Red",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/red.png"
},{
"content_type":"text",
"title":"Green",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/green.png"
}
]
}
I little late but this is when I saw the post. If case you strill need a solution I accomplished the aforementioned by using a custom payload like this:
{
"facebook": {
"attachment":{
"type":"template",
"payload":{
"template_type":"generic"
}
},
"quick_replies":[
{
"content_type":"user_email",
"title":"Red",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/red.png"
},{
"content_type":"text",
"title":"Green",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/green.png"
}
]
}
In the attachment payload you can create any rich content you like, card, carousel, etc. and then attach some quick reply buttons without a title. Also in the quickReply section you can change the text for any other like user_email or user_phone_number.
Hope this help

How to build proposed order with what the user has selected?

I'm building an AOG (actions on google) project that will do basic transaction functionality. Since I'm still a bit new to AOG, I'm completely stuck on how to take what the user selects (whether it be a carousel, a basic card etc.) and pass that argument value/key that they selected into the proposed order or the order preview before they finish their transaction.
Here is basically what I have tried (This isn't the actual code because it's rather long, but it still gets the idea across)
app.intent('delivery_address_complete', (conv) => {
const arg = conv.arguments.get('DELIVERY_ADDRESS_VALUE');
if (arg.userDecision ==='ACCEPTED') {
conv.ask('Ok, what would you like to order?');
conv.ask(new Suggestions(intentSuggestions));
conv.ask(new Carousel({
items: {
// Add the first item to the carousel
SELECTION_KEY_COFFEE: {
synonyms: [
'Coffee'
],
title: 'Coffee',
description: 'Sweet cream and sugar coffee.',
image: new Image({
url: IMG_URL_COFFEE,
alt: 'Image alternate text',
}),
},
}));
}
});
const yesOrno = [
'Yes',
'No'
];
app.intent('actions.intent.OPTION', (conv ) => {
conv.ask('Okay, are you ready to proceed?');
conv.ask(new Suggestions(yesOrno));
});
app.intent('transaction_decision_action', (conv) => {
const order = {
id: UNIQUE_ORDER_ID,
cart: {
merchant: {
id: 'coffee',
name: 'Coffee Store',
},
lineItems: [
{
name: 'My Memoirs',
id: 'coffee_1',
price: {
amount: {
currencyCode: 'USD',
nanos: 990000000,
units: 3,
},
type: 'ACTUAL',
},
quantity: 1,
subLines: [
{
note: 'coffee',
},
],
type: 'REGULAR',
},
otherItems: [
{
name: 'Subtotal',
id: 'subtotal',
price: {
amount: {
currencyCode: 'USD',
nanos: 220000000,
units: 32,
},
type: 'ESTIMATE',
},
type: 'SUBTOTAL',
},
{
name: 'Tax',
id: 'tax',
price: {
amount: {
currencyCode: 'USD',
nanos: 780000000,
units: 2,
},
type: 'ESTIMATE',
},
type: 'TAX',
},
],
totalPrice: {
amount: {
currencyCode: 'USD',
nanos: 0,
units: 35,
},
type: 'ESTIMATE',
},
};
Please note: This is mostly dummy code, so if some things like over charging or prices not making sense is happening, it's not the problem I'm trying to fix.
How can I take what the user selected from whatever method, and get it so it will appear on the order preview or proposed order? I do not need help with anything regarding making carousels or basic cards ect. Just how to get this selected information to the order preview.
To be more specific:
I can create an order object that is required, and I know how to send it to Google (and then to the user) as part of a ProposedOrder object that becomes part of the TransactionDecision object. (The "transaction_decision_action" Intent handler in the code above.)
What I don't understand is how to build the order based on the user saying things or by selecting on carousel or list items that I've shown them. (What do I do in the "actions.intent.OPTION" Intent handler above, for example?)
edit: This also may clear up any confusion. This is a video representation of what I'm attempting to do (mentioned in comments below):
youtube.com/watch?v=LlgMcJBnNN8 from 1:02 to 1:29 I know how to do, I'm confused (In the video example) how they were able to get the 'turkey sandwich' and the 'Green smoothie' added to the order preview at 1:35 ish from the carousel selections
What you're looking to do is what Google refers to as building the order. As it notes at that link
Once you have the user information you need, you'll build a "cart
assembly" experience that guides the user to build an order. Every
Action will likely have a slightly different cart assembly flow as
appropriate for your product or service.
You could build a cart assembly experience that enables the user to
re-order their most recent purchase via a simple yes or no question.
You could also present the user a carousel or list card of the top
"featured" or "recommended" items. We recommend using rich responses
to present the user's options visually, but also design the
conversation such that the user can build their cart using only their
voice.
For more information on how to build a high-quality cart assembly
experience, see the Transactions Design Guidelines.
So there is no one way to do what you're asking about. However, there are a few tips of things you can and should be doing to build the proposed order.
Managing the order
The big thing you need to do is to keep track of all the things that the user is ordering as you go through the process. There are a number of ways you can store this information:
In a Dialogflow Context
In the user session store
In a database or data store for the session
In short, any of the current ways you have to store session information. All of the information below assumes you've picked some way to do this.
Since everything will become one of the lineItems, an easy solution is to build this array as you go along, and then you can just copy the array directly into the order object. Another approach is to just store a list of item IDs, and then populate the rest of the information later when we build the order.
For this example, we're going to go with this latter scheme (because its easier to show) and store it in the session storage object using the actions-on-google library.
So for starters, when we start the Action, or when we know we'll be taking the order, we need to initialize our list of items being ordered with something like
conv.user.data.items = [];
Now that we have our initial item list, we can explore different ways to add to this list.
Adding an item: "my regular"
For some types of orders, it may make sense for the user to be able to say "I'll have my usual". In cases like this, we want an Intent that handles this phrase (or handles a "yes" response to our prompting), and an Intent Handler that looks up the user's regular order and adds it to the items. Perhaps something like this:
app.intent('order.usual', conv => {
// Get their user profile from our database
// The "loadUser" function is up to you, and has little to do with AoG
return loadUser( conv )
.then( user => {
// Add each item in their usual order to the current items
let usualOrder = user.usualOrder;
usualOrder.forEach( item => conv.user.data.items.push( item ) );
// Send a message back to the user
conv.add( "You got it! Do you want anything else?" );
});
});
Adding an item from a list
If you've presented a carousel or a list to the user of possible items, your life is a little easier (although you may not think it at the moment). You do need to setup a Dialogflow Intent that handles the actions_intent_OPTION event (which I'll call order.option in this case).
In the handler for this, we'll assume that the key you used for the option also happens to be the item ID, so you can just add it to the list
app.intent('order.option', (conv, params, option) => {
// The item is the option sent
let item = option;
// Add the item to the list of items
conv.user.data.items.push( item );
// Send a message back to the user
conv.add( "I've put that in your cart. Anything else?" );
});
Adding an item by name
But remember, the user can take the conversation in any direction at any time. So they may ask for an item that you currently aren't showing in the carousel. The best way to handle this is by creating an Entity Type in Dialogflow (which I'll call item, as an example)
And then an Intent that captures some phrases that expresses the user asking to add them (which I'll call order.name and which has an itemName parameter that the user has to include).
[
In the handler, you need to get the name that they spoke, look up what the item is, and add this to the list of items they've ordered.
app.intent('order.name', (conv, params) => {
// Get the name
let itemName = params['itemName'];
// Look it up to find out what they ordered
// You need to implement the itemFromName function
return itemFromName( itemName )
.then( item => {
// Add the item
conv.user.data.items.push( item );
// And reply
conv.add( "You got it! Anything else?" );
});
});
Finish building the order
Once you've finished collecting everything they want, your Intent Handler should put the order together, assembling the full list of lineItems from the conv.user.data.items array that we've been putting together, calculating tax, totals, and all the other parts of the order.
We then need to propose the order by sending a TransactionDecision object that contains our order in the proposedOrder parameter. Clever, no? Possibly something like this:
app.intent('review', conv => {
// Get the items the user has saved
let items = conv.user.data.items;
// Turn these into more complete lineItems
// You will need to provide the "itemToLineItem" function
let lineItems = items.map( itemToLineItem );
// Get some other objects we need
// You'll need to define these functions, too
let orderId = generateOrderId();
let subtotal = computeSubtotal( lineItems );
let tax = computeTax( lineItems );
let total = computerTotal( subtotal, tax );
// Build the order object
let order = buildOrder( lineItems, subtotal, tax, total );
conv.ask(new TransactionDecision({
orderOptions: {
requestDeliveryAddress: false,
},
paymentOptions: {
googleProvidedOptions: {
prepaidCardDisallowed: false,
supportedCardNetworks: ['VISA', 'AMEX'],
// These will be provided by payment processor,
// like Stripe, Braintree, or Vantiv.
tokenizationParameters: {
tokenizationType: 'PAYMENT_GATEWAY',
parameters: {
'gateway': 'stripe',
'stripe:publishableKey': (conv.sandbox ? 'pk_test_key' : 'pk_live_key'),
'stripe:version': '2017-04-06'
},
},
},
},
proposedOrder: order,
}));
});
I broke most of the stuff out as a function since there is nothing specific about them, except the format of the order (which you illustrate in your example). You can really build it any way you want.
Conclusion
Much of what you need to do really boils down to
Collecting the information of what the user wants to order, mostly storing the IDs of these items
Turning this list of items into the complete order object
Sending this order for the user to review

Mongodb: updating multiple docs in a loop?

I did a great effort to find a solution to this common situation, without success. Hoping somebody can help please.
Background:
A board of messages.
Each message has its msg_id.
Each user has an id
I need to track which user watched which message.
For this I have a collection called viewedMessages like this:
{
{ _id: <message_id_X>,
viewedBy: [<user_id_?>,<user_id_?>,...]
},
{ _id: <message_id_Y>,
viewedBy: [<user_id_?>,<user_id_?>,user_id_?>,...]
},
...
}
The user is calling my node.js server once in a while, reporting which messages were viewed, like this:
{ user_id: <user_id_?>, viewed: [<message_id_?>,...] }
An entry for a message is only created when some user is reporting it as viewed. This is done using this command:
db.viewedMessages.update({"_id":<msg_id>},{$addToSet:{viewedBy:<user_id>}},{ upsert : true });
Now for the question.
The above update command is asynchronous. How do I run over an array of [msg_id,msg_id,msg_id,...], issuing the above update command for each msg_id, and finally get a callback telling me that everything was done ok OR failed for some reason?
Would highly appreciate any help here!

Resources