I'm trying to add a NESTED persistent menu to my chatbot. Facebook has a limit of 3 buttons but you can have a nested button with a maximum of 5 buttons.
This is the error I get when I run my code
response body error
type: 'OAuthException',
Error: { message: '(#100) Invalid keys "call_to_actions" were found in param "call_to_actions[0]".', code: 100}
Here is my code:
function addPersistentMenu(){
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: "nested",
title: "Menu Item One",
call_to_actions: [
{
type: "postback",
title: "Nested Item One",
payload: "NESTED_ONE"
},
{
type: "postback",
title: "Nested Item Two",
payload: "NESTED_TWO"
}
]
},
{
type: "postback",
title: "Menu Item Two",
payload: "TWO"
},
{
type: "postback",
title: "Menu Item Three",
payload: "THREE"
}
]
}
}, function(error, response, body) {
if(error){
console.log('sending error')
console.log('Error sending messages: ', error)
}else if(response.body.error){
console.log('response body error')
console.log('Error: ', response.body.error)
}
});
}
When I remove the nested button, the persistent menu appears so I'm not sure what the error is. My code is pretty similar to the sample posted by facebook in their persistent menu doc. I'm programing using node.js, hosted on heroku and I modeled my menu function after the code found here.
Question: Has anyone done this using a nodejs webhook using the npm request package to send requests to messenger? How do I add my nested persistent menu and what does this error mean?
Edit:
When I use a direct CURL POST via the terminal using the exact command in the persistent menu documentation, the nested persistent menu is added. I'm not sure what to add to my nodejs webhook version of this request to make it work.
This is the CURL command:
curl -X POST -H "Content-Type: application/json" -d '{
"persistent_menu":[
{
"locale":"default",
"composer_input_disabled":true,
"call_to_actions":[
{
"title":"My Account",
"type":"nested",
"call_to_actions":[
{
"title":"Pay Bill",
"type":"postback",
"payload":"PAYBILL_PAYLOAD"
},
{
"title":"History",
"type":"postback",
"payload":"HISTORY_PAYLOAD"
},
{
"title":"Contact Info",
"type":"postback",
"payload":"CONTACT_INFO_PAYLOAD"
}
]
},
{
"type":"web_url",
"title":"Latest News",
"url":"http://petershats.parseapp.com/hat-news",
"webview_height_ratio":"full"
}
]
},
{
"locale":"zh_CN",
"composer_input_disabled":false
}
]
}' "https://graph.facebook.com/v2.6/me/messenger_profile?access_token=YOUR_ACCESS_TOKEN_HERE"
The Facebook Messenger API has been updated for nested persistent menus. The 'call_to_actions' style appears to still work for a non-nested menu.
A nested menu needs a different API call however. The difference appears to be the URL must be to the 'messenger_profile' rather than 'thread_settings'. A 'get_started' handler is also required for some reason. Finally, the json array is named 'persistent_menu'.
I updated the example bot on gitub. Type 'add menu' and 'remove menu' to see the persistent menu appear/disappear. A page reload or two may be required on some browsers.
Here is some sloppy nodejs code that should do the trick.
function addPersistentMenu(){
request({
url: 'https://graph.facebook.com/v2.6/me/messenger_profile',
qs: { access_token: PAGE_ACCESS_TOKEN },
method: 'POST',
json:{
"get_started":{
"payload":"GET_STARTED_PAYLOAD"
}
}
}, 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)
}
})
request({
url: 'https://graph.facebook.com/v2.6/me/messenger_profile',
qs: { access_token: PAGE_ACCESS_TOKEN },
method: 'POST',
json:{
"persistent_menu":[
{
"locale":"default",
"composer_input_disabled":true,
"call_to_actions":[
{
"title":"My Account",
"type":"nested",
"call_to_actions":[
{
"title":"Pay Bill",
"type":"postback",
"payload":"PAYBILL_PAYLOAD"
},
{
"title":"History",
"type":"postback",
"payload":"HISTORY_PAYLOAD"
},
{
"title":"Contact Info",
"type":"postback",
"payload":"CONTACT_INFO_PAYLOAD"
}
]
},
{
"type":"web_url",
"title":"Latest News",
"url":"http://foxnews.com",
"webview_height_ratio":"full"
}
]
},
{
"locale":"zh_CN",
"composer_input_disabled":false
}
]
}
}, 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)
}
})
}
Related
I'm trying to use Github Gist API to create a gist. The code is shown below:
var result = {
'a.md':'aaa',
'b.md':'bbb',
}
/*
P.S. I also tried this, but it doesn't work, either
var result = {
'a.md' :{
content: 'aaa',
},
'b.md': {
content: 'bbb',
},
}
*/
$.ajax({
type: "POST",
url: 'https://api.github.com/gists',
headers: {
accept: "application/vnd.github.v3+json",
Authorization: `token ${TOKEN}`,
},
data: {
files: result,
description: 'demo',
public: false,
},
success: (response) => {
console.log(response);
},
});
but I received an error: POST https://api.github.com/gists 400 (Bad Request).
the request is:
{
"message": "Problems parsing JSON",
"documentation_url": "https://developer.github.com/v3/gists/#create-a-gist"
}
And I wonder why and how to fix it.
(This is not English native speaking, so don't mind my grammar mistakes)
I want to make a call to my server, which will then call the PayPal Payouts REST API to make a specific payment. I set up the necessary accounts and when I try it with Postman everything works great! I call the URL: https://api.sandbox.paypal.com/v1/payments/payouts with the token and Content-Type set to application/json and the body
{
"sender_batch_header": {
"sender_batch_id": "Payouts_2018_100013",
"email_subject": "You have a payout!",
"email_message": "You have received a payout from ****! Thanks for using our service!"
},
"items": [
{
"recipient_type": "EMAIL",
"amount": {
"value": "9.87",
"currency": "EUR"
},
"receiver": "****#*****.com"
}]
}
This will return something like
{
"batch_header": {
"payout_batch_id": "F5YLETFEWFLUS",
"batch_status": "PENDING",
"sender_batch_header": {
"sender_batch_id": "Payouts_2018_100012",
"email_subject": "You have a payout!",
"email_message": "You have received a payout from realnote! Thanks for using our service!"
}
},
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payouts/F5YLETFEWFLUS",
"rel": "self",
"method": "GET",
"encType": "application/json"
}
]
}
with the HHTP Code 201 (Created). This is completely correct and I can see the status of my payment when calling the appropriate URL with the payout_batch_id from the answer.
However, if I try the same call from my NodeJS server there are problems. I get the token and everything works but then I create my request like this:
const options = {
url: "https://api.sandbox.paypal.com/v1/payments/payouts",
headers: {
'Content-Type': 'application/json'
},
auth: {
'bearer': token
},
form: {
"sender_batch_header": {
"sender_batch_id": "***_payment_***",
"email_subject": "You have a payout!",
"email_message": "You have received a payout from ****! Thanks for using our service!"
},
"items": [
{
"recipient_type": "EMAIL",
"amount": {
"value": "10.0",
"currency": "EUR"
},
"receiver": "*****#*****.com"
}]
}
};
I then send the request using the request module with this code:
request.post(options, function(err,httpResponse,body){
if (err) {
console.error('Sending money failed:', err);
} else {
console.log('Response from PayPal successful! Server responded with:', body);
//var payPalAnswer = JSON.parse(body);
}
})
But this will result in getting an answer with the status code 204 (No content) and it contains, no wonder, no content, so there is no possibility to get the status of my payment like with the server answer I got using Postman. Where is my error?
request.post(options, {data: "..."},function(err,httpResponse,body){
if (err) {
console.error('Sending money failed:', err);
} else {
console.log('Response from PayPal successful! Server responded with:', body);
//var payPalAnswer = JSON.parse(body);
}
})
You have to use your data as second parameter
The correct answer was to link the body in the request like this:
var dataObject = {"sender_batch_header": {
"sender_batch_id": "payment_",
"email_subject": "You have a payout!",
"email_message": "You have received a payout from *****! Thanks for using our service!"
},
"items": [
{
"recipient_type": "EMAIL",
"amount": {
"value": '10.0',
"currency": "EUR"
},
"receiver": "*******#personal.example.com"
}]
};
const options = {
url: "https://api.sandbox.paypal.com/v1/payments/payouts",
headers: {
'Content-Type': 'application/json',
'Content-Length': dataObject.length
},
auth: {
'bearer': token
},
body: dataObject,
json: true
};
Now PayPal returns the correct server answer 201.
I have an API endpoint that exists, and I know it works correctly (I test frequently, successfully using PostMan). The problem is that when I am posting to it via my chrome extension, I am able to connect, however, no data values are being sent.
Here's my chrome manifest:
{
"background": {
"scripts": ["eventPage.js","jquery-3.3.1.min.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["https://*.amazon.com/*"],
"js": ["content.js", "jquery-3.3.1.min.js", "inject.js"],
"css": ["content.css"]
}
],
"permissions": [
"tabs",
"storage",
"https://*.amazon.com/*",
"https://*.cloudfunctions.net/*"
] }
Here is the event page that makes the call:
// listen to all messages at runtime
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
if (request.todo == 'sendLoad') {
console.log("MADE IT! Request Data", request.data)
// FYI the request.data output is correct; everything is there
// AJAX
$.ajax({
url: "https://prefix.cloudfunctions.net/endpoint",
type: "post",
data: {productName: encodeURIComponent(jQuery.trim(request.data.productName)), productImg: encodeURIComponent(request.data.productImg), productUrl: encodeURIComponent(request.data.productUrl), price: request.data.price, byLineValue: request.data.byLineValue, byLineUrl: encodeURIComponent(request.data.byLineUrl), amazonPath: encodeURIComponent(request.data.amazonPath[2]), uid: request.data.uid, tid: "1"} ,
success: function (response) {
console.log("Send complete: ", response);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
}
});
};
});
(FYI, I also just tried to send request.data alone and was unsuccessful.)
I also tried to make the call using an xmlhttp request:
xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","https://prefix.cloudfunctions.net/endpoint",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send(request.data);
And I also tried using a jQuery post:
$.post("https://prefix.cloudfunctions.net/endpoint",{{productName: encodeURIComponent(jQuery.trim(request.data.productName)), productImg: encodeURIComponent(request.data.productImg), productUrl: encodeURIComponent(request.data.productUrl), price: request.data.price, byLineValue: request.data.byLineValue, byLineUrl: encodeURIComponent(request.data.byLineUrl), amazonPath: encodeURIComponent(request.data.amazonPath[2]), uid: request.data.uid, tid: "1"})
.done(function (data) {
console.log("Send complete: " + data);
})
.fail(function() {
console.log("Error sending data")
})
.always(function() {
console.log("jQuery Post complete")
})
For each of these my API is returning a message that the values do not exist:
Error: Value for argument "data" is not a valid Firestore document. Cannot use "undefined" as a Firestore value (found in field productImg).
at Object.validateUserInput (/srv/node_modules/#google-cloud/firestore/build/src/serializer.js:273:15)
at Object.validateDocumentData (/srv/node_modules/#google-cloud/firestore/build/src/write-batch.js:622:26)
at CollectionReference.add (/srv/node_modules/#google-cloud/firestore/build/src/reference.js:1743:23)
at exports.addToTrunk.functions.https.onRequest (/srv/lib/index.js:27:14)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:57:9)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
As I mentioned earlier, the endpoint works successfully but is not finding the data values that are sent.
UPDATE:
It's looking like the firebase "add" function that's not working correctly. Here is the code in the firebase function:
newTrunk.add({
created_at: created_at,
uid: "CG8B0mdqzMed3PPCiwsKkBtXmiA2", //request.body.uid, //TODO: pull in uid from localstorage
tid: '1', //TODO: provide list of trunks for person to select from when adding via extension
productImg: JSON.stringify(request.body.productImg),
price: JSON.stringify(request.body.price),
productUrl: JSON.stringify(request.body.productUrl),
byLineValue: JSON.stringify(request.body.byLineValue),
byLineUrl: JSON.stringify(request.body.byLineUrl),
amazonPath: JSON.stringify(request.body.amazonPath),
source: JSON.stringify(request.body.source),
productName: JSON.stringify(request.body.productName)
// Tried to stringify and see if it would help ... it didn't
// productImg: request.body.productImg,
// price: request.body.price,
// productUrl: request.body.productUrl,
// byLineValue: request.body.byLineValue,
// byLineUrl: request.body.byLineUrl,
// amazonPath: request.body.amazonPath,
// source: request.body.source,
// productName: request.body.productName
})
.then(function(docRef) {
// console.log("TCL: docRef", docRef)
console.log("New item in trunk added with ID: ", docRef.id)
response.status(200).send("Successful")
})
.catch(function(error) {
console.error("Error adding newTrunk: ", error)
response.status(500).send(error)
});
I am trying to automate the response which is getting generated in the postman tool but getting the error 401 unqutorized error. tried to resolve in many ways by giving the exact security token to the authorization-token but it didnt worked out.
//In this method the security token passed is newly generated and it works fine and produces response 200 in postman tool
async claimsetResponseOP(SecurityToken:string){
var options = {
method: 'POST',
url: 'http://nhsappchna8086/LMHIAService/api/v3/ValueSet/$expand',
//Giving header to the request
headers: {
'Authorization-Token': SecurityToken, // Here is the place where the security token is passed in the headers
'Accept': 'application/json+fhir',
'Content-Type' : 'application/json+fhir',
'Authorization-Type' : 'Native'
},
body: {
"resourceType ": "Parameters",
"parameter": [
{
"name": "ValueSet",
"resource": {
"resourceType": "ValueSet",
"status": "active",
"compose": {
"include": [{
"system": "http://snomed.info/sct",
"filter": [{
"property": "constraint",
"op": "=",
"value": "<<(*:R363698007=271629002)"
}
]
}
]
}
}
}
]
},
json:true,
resolveWithFullResponse: true
};
//Error gets displayed in below line as Failed 401 unautorized error
await requestp(options).then(async function(response){
await requestp(options).then(async function(Content){
if (await response.statusCode == 200){
var info = Content.body;
}
})
})
}
Below error gets displayed while executing: Can anyone please help in resolving
Failed: 401 - {"resourceType":"OperationOutcome","id":"AUTH-01","text":{"status":"additional","div":"Unauthorised"},"issue": [{"severity":"error","code":"security","details":{"text":"9254 - Unauthorised"}}]}
No. Manually when i run it is working fine.
I'm writing a telegram bot using node JS ( meteor ) and that's what's happening when I am creating a url button with a tg://protocol, it gives an error:
Error in polling: Error: failed [400] {"ok":false,"error_code":400,"description":"Bad Request: unsupported URL protocol"}
Here is my code:
TelegramBot.addListener('/button', function(command, username, original) {
TelegramBot.method('sendMessage', {
chat_id: original.chat.id,
text: 'Here is you proxy!',
parse_mode: "HTML",
reply_markup: JSON.stringify({
inline_keyboard: [
[{ text: 'Add & Connect', url: 'tg://resolve?domain=socks%26server=185.211.245.136%26port=1080%26user=106402828%26pass=jr5udGLf' }]
]
})
});
});
replace Add & Connect with Add & Connect
if the problem doesn't solve, replace %26 with &
Also i offer you to use normal https protocol:
TelegramBot.addListener('/button', function(command, username, original) {
TelegramBot.method('sendMessage', {
chat_id: original.chat.id,
text: 'Here is you proxy!',
parse_mode: "HTML",
reply_markup: JSON.stringify({
inline_keyboard: [
[{ text: 'Add & Connect', url: 'https://t.me/socks?server=*&port=*&user=*&pass=*' }]
]
})
});
});