Facebook Messenger Bot Persistent Menu - node.js

I am generating my first bot working with node.js and heroku but finding some difficulties to understand the persistent menu functionalities.
Question 1) How do can I attach event as callbacks?
function persistentMenu(sender){
request({
url: 'https://graph.facebook.com/v2.6/me/thread_settings',
qs: {access_token:token},
method: 'POST',
json:{
setting_type : "call_to_actions",
thread_state : "existing_thread",
call_to_actions:[
{
type:"postback",
title:"FAQ",
payload:"DEVELOPER_DEFINED_PAYLOAD_FOR_HELP"
},
{
type:"postback",
title:"I Prodotti in offerta",
payload:"DEVELOPER_DEFINED_PAYLOAD_FOR_HELP"
},
{
type:"web_url",
title:"View Website",
url:"https://google.com/"
}
]
}
}, function(error, response, body) {
console.log(response)
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
}
})
}
Question 2) The only way I have found for empty the persistent menu and generating a new one is with a delete request via terminal ("as Facebook documented")m is there a possibily to clear inserting a refresh function on my app.js file?
curl -X DELETE -H "Content-Type: application/json" -d '{"setting_type":"call_to_actions","thread_state":"existing_thread"}' "https://graph.facebook.com/v2.6/me/thread_settingsaccess_token=PAGE_ACCESS_TOKEN"

The FB example robot is not well structured for call backs. I haven't found a good way to structure the example in Node callback or promise model. I'm sure a Node expert can reorg it.
As for the persistent menu, if you send an empty call_to_actions array the menu will disappear. The menu seems a bit 'sticky' however as it does not immediately appear/disappear when the message is sent.
I incorporated your snippet into my example robot. You can see it at
https://messenger.com/t/dynamicmemorysolutions
The source is at:
https://github.com/matthewericfisher/fb-robot
See the add/remove menu commands and functions.
EDIT: The persistent menu API has been updated. See this question for more details.

this worked for me:
function menuButton() {
var messageData = {
setting_type : "call_to_actions",
composerinputdisabled :"TRUE",
thread_state : "existing_thread",
call_to_actions:[
{
type:"postback",
title:"¿Tiempo de espera?",
payload:"ACTUALIZAR"
},
{
type:"postback",
title:"Ver Promociones",
payload:"DEVELOPER_DEFINED_PAYLOAD_FOR_START_ORDER"
}
]
}
request({
uri: 'https://graph.facebook.com/v2.6/me/thread_settings',
qs: { access_token: PAGE_ACCESS_TOKEN },
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
console.log("Successfully sent generic message with id %s to recipient %s",
messageId, recipientId);
} else {
console.error("Unable to send message.");
console.error(response);
console.error(error);
}
});
}
And I call this function at the beggining
app.post('/webhook', function(req, res){
var data = req.body;
if (data.object == 'page') {
menuButton();
data.entry.forEach(function(entry) {
var pageID = entry.id;
var timeOfEvent = entry.time;
// Iterate over each messaging event
entry.messaging.forEach(function(event) {
if (event.message) {
receivedMessage(event);
}else if (event.postback) {
receivedPostback(event);
} else {
console.log("Webhook received unknown event: ", event);
}
});
});
res.sendStatus(200);
}
})
What I have not being able to do is to remove the option of free text input. Facebook claimed now is possible yet have found no instructions or examples on how to do it. Any clues?

If you want to disable the free text input, you shoud add the following parameter to your persistent menu request:
"composer_input_disabled": true
and not
composerinputdisabled :"TRUE"

The FB API document states that the API link to hit for applying persistent menu to the page specific bot is:
https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>
Notice the me after version number i.e v2.6 in this specific case. However, this did not worked for a lot of people
There is small change in the API link to hit:
graph.facebook.com/v2.6/Page ID/messenger_profile?access_token=PAGE ACCESS TOKEN
Notice that me is replaced with the fb Page Id.
And the sample payload can still be the same:
{
"get_started": {
"payload": "Get started"
},
"persistent_menu": [
{
"locale": "default",
"composer_input_disabled": false,
"call_to_actions": [
{
"title": "Subscribe",
"type": "postback",
"payload": "subscribe"
},
{
"title": "Stop notifications",
"type": "nested",
"call_to_actions": [
{
"title": "For 1 week",
"type": "postback",
"payload": "For_1_week"
},
{
"title": "For 1 month",
"type": "postback",
"payload": "For_1_month"
},
{
"title": "For 1 year",
"type": "postback",
"payload": "For_1_year"
}
]
},
{
"title": "More",
"type": "nested",
"call_to_actions": [
{
"title": "Fresh jobs",
"type": "postback",
"payload": "fresh jobs"
},
{
"title": "Like us",
"type": "web_url",
"url": "https://www.facebook.com/onlysoftwarejobs/"
},
{
"title": "Feedback",
"type": "web_url",
"url": "https://docs.google.com/forms/d/e/1FAIpQLScjgFRbfBLznO55kFIskcH_eFc23zRSUUxzIgv_o44uj0GMpw/viewform"
}
]
}
]
}
]
}
Notice that it is mandatory to configure get_started button before setting up the persistent_menu.

Related

Can't get a succesful jira create issue api response

I am trying to call jira rest api POST /rest/api/3/issue from a node js application. I am getting the following error even though am passing correct details. I am passing issueType as 10103 and remaining sensitive params as required. Here is my
nodejs code.
app.post("/webhook", function(req,res,next){
console.log(req.body);
let options = {
method: 'POST',
url: req.body.tags["jira:endpointURL"]+"/rest/api/3/issue",
auth: {
user: req.body.tags["jira:user"],
password: req.body.tags["jira:token"]
},
headers: {
'Content-Type': 'application/json'
},
json: {
"update": {},
"fields": {
//"summary": req.body["subject"],
"summary": "Test",
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": "body",
"type": "text"
}
]
}
]
},
"issuetype": {
"id": req.body.tags["jira:issueType"]
},
"project": {
"key": req.body.tags["jira:project"]
}
}
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(
'Response: ' + response.statusCode + ' ' + response.statusMessage
);
console.log(body);
res.send("OK");
});
});
Error thrown
summary: "Field 'summary' cannot be set. It is not on the appropriate screen, or unknown.",
description: "Field 'description' cannot be set. It is not on the appropriate screen
You have to configure the fields to the screens first.
See:
https://support.atlassian.com/jira-cloud-administration/docs/add-a-custom-field-to-a-screen/
https://support.atlassian.com/jira-cloud-administration/docs/configure-issue-screens/#Configure-a-screen--x27-s-tabs-and-fields

How to make Paypal Payouts on a Node.Js environment

I am trying to execute a PayPal Payout on Node.Js environment, but I can't seem to wrap my head around it.
Could you please help me get the parameters right, and maybe where I should re-organize the code, and eventually start heading in the right direction?
Thank you all in advance.
var PAYPAL_API = "https://api-m.sandbox.paypal.com";
app.post("/my-api/execute-payment/", function (req, res) {
// 2. Get the payment ID and the payer ID from the request body.
var paymentID = req.body.paymentID;
var payerID = req.body.payerID;
let requestBody = {
"sender_batch_header": {
"sender_batch_id": "Payouts_2018_100007",
"email_subject": "You have a payout!",
"email_message": "You have received a payout! Thanks for using our service!",
},
"items": [
{
"recipient_type": "EMAIL",
"amount": {
"value": "9.87",
"currency": "USD",
},
"note": "Thanks for your patronage!",
"sender_item_id": "201403140001",
"receiver": "rev_buyer#personal.example.com",
"alternate_notification_method": {
"phone": {
"country_code": "91",
"national_number": "9999988888",
},
},
"notification_language": "fr-FR",
},
{
"recipient_type": "PAYPAL_ID",
"amount": {
"value": "5.32",
"currency": "USD",
},
"note": "Thanks for your patronage!",
"sender_item_id": "201403140003",
"receiver": "xXXXXXX-50mQSYBJpg_OWguVkwYYYYYYY-ZAFupw-aaaaa-nnnnnnn",
},
],
};
request.post(
PAYPAL_API + "/v1/payments/payouts",
{
auth: {
user: CLIENT,
pass: SECRET,
},
requestBody,
json: true,
},
function (err, response) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
// 4. Return a success response to the client
res.json({
status: "success",
});
}
);
});
Have you tried the Payouts-NodeJS-SDK ?
The main issue with the custom code above is that you aren't first requesting an OAuth 2.0 access_token, and then using the access_token for your REST API call.
See the documentation: https://developer.paypal.com/docs/api/overview/#get-an-access-token
The SDK abstracts this step for you.

Twilio autopilot send response as image to users whatsapp

I am building a bot so whenever a specific task is initiated I want to send an image to the user.
exports.handler = function(context, event, callback) {
let response = {
"actions": [
{
"show": {
"body": "Twilio Owls",
"images": [{
"label": "Original Owl",
"url": "https://demo.twilio.com/owl.png"
}]
}
}]
}
callback(null, response)
}
I tried using the above code inside my function and I have linked it to my Twilio autopilot task, It is working in the simulator, but when I am testing it on Whatsapp Image does not appear in the Whatsapp chat only body get displayed.
I tested and received the owl image. I noticed have a rogue {, maybe that is it?
exports.handler = function(context, event, callback) {
let response = {
"actions": [
{
"show": {
"body": "Twilio Owls",
"images": [{
"label": "Original Owl",
"url": "https://demo.twilio.com/owl.png"
}]
}
}]
}
callback(null, response)
};

Atlassian Connect-Express: JIRA REST API authentication within the JIRA plugin

i am using the atlassian-connect-express toolkit for creating Atlassian Connect based Add-ons with Node.js.
It provides Automatic JWT authentication of inbound requests as well as JWT signing for outbound requests back to the host.
The add-on is authenticated when i install it in the JIRA dashboard and return the following pay-load:
{ key: 'my-add-on',
clientKey: '*****',
publicKey: '********'
sharedSecret: '*****'
serverVersion: '100082',
pluginsVersion: '1.3.491',
baseUrl: 'https://myaccount.atlassian.net',
productType: 'jira',
description: 'Atlassian JIRA at https://myaccount.atlassian.net ',
eventType: 'installed' }
But i am not able to authenticate the JIRA Rest Api with the JWT token generated by the framework. It throws below error message.
404 '{"errorMessages":["Issue does not exist or you do not have permission to see it."],"errors":{}}'
below is the code when i send a GET request:
app.get('/getissue', addon.authenticate(), function(req, res){
var request = require('request');
request({
url: 'https://myaccount.atlassian.net/rest/api/2/issue/ABC-1',
method: 'GET',
}, function(error, response, body){
if(error){
console.log("error!");
}else{
console.log(response.statusCode, body);
}
});
res.render('getissue');
});
Below is the code for my app descriptor file:
{
"key": "my-add-on",
"name": "Ping Pong",
"description": "My very first add-on",
"vendor": {
"name": "Ping Pong",
"url": "https://www.example.com"
},
"baseUrl": "{{localBaseUrl}}",
"links": {
"self": "{{localBaseUrl}}/atlassian-connect.json",
"homepage": "{{localBaseUrl}}/atlassian-connect.json"
},
"authentication": {
"type": "jwt"
},
"lifecycle": {
"installed": "/installed"
},
"scopes": [
"READ",
"WRITE"
],
"modules": {
"generalPages": [
{
"key": "hello-world-page-jira",
"location": "system.top.navigation.bar",
"name": {
"value": "Hello World"
},
"url": "/hello-world",
"conditions": [{
"condition": "user_is_logged_in"
}]
},
{
"key": "getissue-jira",
"location": "system.top.navigation.bar",
"name": {
"value": "Get Issue"
},
"url": "/getissue",
"conditions": [{
"condition": "user_is_logged_in"
}]
}
]
}
}
I am pretty sure this is not the correct way i am doing, Either i should use OAuth. But i want to make the JWT method for authentication work here.
Got it working by checking in here Atlassian Connect for Node.js Express Docs
Within JIRA ADD-On Signed HTTP Requests works like below. GET and POST both.
GET:
app.get('/getissue', addon.authenticate(), function(req, res){
var httpClient = addon.httpClient(req);
httpClient.get('rest/api/2/issue/ABC-1',
function(err, resp, body) {
Response = JSON.parse(body);
if(err){
console.log(err);
}else {
console.log('Sucessful')
}
});
res.send(response);
});
POST:
var httpClient = addon.httpClient(req);
var postdata = {
"fields": {
"project":
{
"key": "MYW"
},
"summary": "My Story Name",
"description":"My Story Description",
"issuetype": {
"name": "Story"
}
}
}
httpClient.post({
url: '/rest/api/2/issue/' ,
headers: {
'X-Atlassian-Token': 'nocheck'
},
json: postdata
},function (err, httpResponse, body) {
if (err) {
return console.error('Error', err);
}
console.log('Response',+httpResponse)
});
You should be using global variable 'AP' that's initialized by JIRA along with your add-on execution. You may explore it with Chrome/Firefox Debug.
Have you tried calling ?
AP.request(..,...);
instead of "var request = require('request');"
You may set at the top of the script follwing to pass JS hinters and IDE validations:
/* global AP */
And when using AP the URL should look like:
url: /rest/api/2/issue/ABC-1
instead of:
url: https://myaccount.atlassian.net/rest/api/2/issue/ABC-1
My assumption is that ABC-1 issue and user credentials are verified and the user is able to access ABC-1 through JIRA UI.
Here is doc for ref.: https://developer.atlassian.com/cloud/jira/software/jsapi/request/

Error when trying to send transactional email from template using sendGrid

I'm trying to send a transactional email via node using sendGrid. Below is an example of my code.
const subject = 'Email subject';
const templateId = 'templateId';
const sg = require('sendgrid')(secret);
const request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: {
"personalizations": [
{
"bcc": userEmails,
"substitutions": {
"-userName-": userDetails.name,
"-productPrice-": productDetails.price,
"-productUrl-": productDetails.url,
"-productPercentageDrop-": productDetails.percentageDrop,
"-productName-": productDetails.name,
"-productOriginalPrice-": productDetails.origPrice,
"-productDroppedPrice-": productDetails.dropPrice,
"-productImageUrl-": productDetails.imageUrl
},
"subject": subject.substring(0, 75)
}
],
"from": {
"email": "myemail",
"name": "myname"
},
"content": [
{
"type": "text/html"
}
],
"template_id": templateId
}
});
sg.API(request, function (error, response) {
if (error) {
console.log('Error response received');
}
console.log(response.body.errors);
});
But every time I run the code I get the following error message.
400
message: 'Bad Request', field: null, help: null
Which isn't really that helpful when trying to find out why its erroring.
Body JSON being sent:
{
"host":"",
"method":"POST",
"path":"/v3/mail/send",
"headers":{
},
"body":{
"personalizations":[
{
"bcc":[
{
"email":"name1#hotmail.com",
"name":"name1"
},
{
"email":"name2#hotmail.com",
"name":"name2"
}
],
"substitutions":{
"-productPrice-":189.5,
"-productUrl-":"http://www.tesco.com/direct/humax-fvp-4000t500-m-smart-freeview-play-hd-digital-tv-recorder-with-wi-fi-500gb/483-1785.prd",
"-productName-":"Tesco direct: Humax FVP-4000T/500 (M) Smart Freeview Play HD Digital TV Recorder with Wi-Fi - 500GB"
},
"subject":"Product Tesco direct: Humax FVP-4000T/500 (M) Smart Freeview Play HD Digita"
}
],
"from":{
"email":"email#pricetracker.io",
"name":"Pricetracker"
},
"content":[
{
"type":"text/html"
}
],
"template_id":"XXXXXX"
},
"queryParams":{
},
"test":false,
"port":""
}
don't know if this could still be helpful for you, but I've had some similar issues when using substitutions with Sendgrid. I'm using the sendgrid-php library, but internally it sends the same format. What I found was that all substitution values should be converted to string. For instance your value for productPrice should be like this:
"-productPrice-": "189.5",
With quotes. In my case I had integers, and when I converted them to strings, all worked correctly.
If you are not passing string value to the template file then SendGrid will give you
{"errors":[{"message":"Bad Request","field":null,"help":null}]}
Convert your all non-string value to string before sending to the template file.
I got same issue, then I fix using this below syntax, change only you message format.
"value": "'.html_entity_decode((str_replace('"',"'",$message))).'"

Resources