Bad Request: can't parse reply keyboard markup JSON object - javascript

I want to create button in my telegram bot by javascript and I working in script google.
But it didn't fetch.
My code:
function a() {
const opts = {
reply_to_message_id: '690534265',
reply_markup: {
resize_keyboard: true,
one_time_keyboard: true,
keyboard: [ ['test'], ['test1'] ]
}
};
MainKeyBoard(opts, 'afs', '690534265');
}
function MainKeyBoard(tasti, toxt, chid) {
var url = link + '/sendMessage?parse_mode=Markdown' +
'&chat_id=' + chid +
'&text=' + toxt +
'&reply_markup=' + tasti;
var respose = UrlFetchApp.fetch(url);
}
This is my error:
Request failed for https://api.telegram.org/bot745193421:<token>/sendMessage?parse_mode=Markdown&chat_id=690534265&text=afs&reply_markup=[object%20Object] returned code 400. Truncated server response: {"ok":false,"error_code":400,"description":"Bad Request: can't parse reply keyboard markup JSON object"} (use muteHttpExceptions option to examine full response)
I tried to add JSON.parse but error :
SyntaxError: Unexpected token: o

Since opts is an object when you are trying to concatenate it will use the toString() method which will just return [object Object] that you can see in the request.
In order to solve this issue serialize your object using JSON.stringify in your MainKeyboard function.
function MainKeyBoard(tasti, toxt, chid) {
var url = link + '/sendMessage?parse_mode=Markdown&chat_id=' + chid +
'&text=' + toxt + '&reply_markup=' + JSON.stringify(tasti);
var response = UrlFetchApp.fetch(url);
}

Related

Telegram bot inline keyboard via AppScript

I am making a Telegram bot using Appscript and I want to copy/send message with an inline_keyboard but when I send the URL I get an invalid parameter error and I don't know what is wrong, because everything worked until I add the reply_markup parameter.
function enviarMnesjePruebaCanal() {//ENVIAR MENSAJE
var urlusuarioconid = "tg://userid?=" + miID
var keyboard = {
"inline_keyboard": [
[
{
"text": "Usuario",
"url": urlusuarioconid
}
]
]
};
var url = telegramUrl + '/sendMessage?chat_id=' + miID + '&text=' + "texto" + "&reply_markup=" + JSON.stringify(keyboard);
Logger.log(url);
UrlFetchApp.fetch(url);
}
`
This is my code and this is what I send to telegram and what I get back:
(I have deleted the data of my bot and my id for obvious reasons, but that part is ok because when I run it without the last part it sends me the message)
I have already tried several things and they all give the same problem, I don't know if anyone can help me or see what could be happening. Thanks!
The issue is caused by the reply_markup containing non-url tokens.
You should encode that part of the url using encodeURIComponent()
Change the code to:
function enviarMnesjePruebaCanal() {
var myID = 123456;
var urlusuarioconid = "tg://userid?=" + myID
var keyboard = {
"inline_keyboard": [
[
{
"text": "Usuario",
"url": urlusuarioconid
}
]
]
};
var url = telegramUrl + '/sendMessage?chat_id=' + myID + '&text=' + "texto" + "&reply_markup=" + encodeURIComponent(JSON.stringify(keyboard));
Logger.log(url);
var response = UrlFetchApp.fetch(url);
Logger.log(response.getContentText());
}
The important change here is adding the encodeURIComponent:
encodeURIComponent(JSON.stringify(keyboard))

How to pass optional parameters using fetch vanilla JS

I'm using JavaScript fetch GET method to call an API. The API returns data; however, there are optional parameters I'd like to pass in to format the data response in a different way. How do you pass optional parameters using the fetch method?
async function getText(){
let passageParam = randomPassage();
//API credit
let Url = 'https://api.esv.org/v3/passage/text?q=' + passageParam + params;
console.log(Url);
//Await - Used with Async
//Suspend function exeeuction until the Async promise settles and returns its result
let response = await fetch(Url, {
method: 'GET',
headers: {
'Authorization': 'myToken'
},
params = {
'indent-poetry': False,
'include-headings': False,
'include-footnotes': False,
'include-verse-numbers': False,
'include-short-copyright': False,
'include-passage-references': False
}
});
if(response.ok){ // if HTTP-status is 200-299
// get the response body
let passage = await response.json();
populateUI(passageParam, passage.passages[0]);
//console.log(passage);
} else{
alert("HTTP-Error: " + response.status);
}
//Function to input json response to HTML
function populateUI(ref, verse){
//strip verse
document.getElementById('reference').innerHTML = ref;
document.getElementById('verse').innerHTML = verse;
}
}
When using fetch with GET, it's generally expecting parameters be sent via a Query String.
You can try something like this:
let passageParam = randomPassage();
let extraParams = '&indent-poetry=False&include-headings=False' +
'&include-footnotes=False&include-verse-numbers=False' +
'&include-short-copyright=False&include-passage-references=False';
let Url = 'https://api.esv.org/v3/passage/text?q=' + passageParam + extraParams;
console.log(Url);
Alternatively you can do something like this:
let passageParam = randomPassage();
let extraParams = {
'indent-poetry': 'False',
'include-headings': 'False',
'include-footnotes': 'False',
'include-verse-numbers': 'False',
'include-short-copyright': 'False',
'include-passage-references': 'False'
}
let Url = 'https://api.esv.org/v3/passage/text?q=' + passageParam +
'&' + (new URLSearchParams(extraParams)).toString();
console.log(Url);
And also delete the params expression.
Since you are making a GET request to a URL-EndPoint using fetch. The URL-EndPint will always return the same data format every time you call it.
And formatting a response is not in our hands in this case. To check all the response details, go to the network tab of Developer Console (do Ctrl+Shift+I), you can see the response headers and other related stuff that you have received in the response and see if any information is useful to you there itself.

Cordova app has internet connection via emulator, but not on device [update: ajax problem]

I am trying to build a small app via cordova that sends data to a PERL-Script on a server. The debug and release build work fine on the genymotion emulator, however sending the data from Android phones does not work. There is no error message from the app (either, which is supposed to show up when the connection fails).
Running USB debugging, I do get the follwoing invoke error message (the savedataLastpage funtion is supposed to send the data):
Uncaught TypeError: Illegal invocation
at e (jquery-2.1.3.min.js:4)
at Ac (jquery-2.1.3.min.js:4)
at Function.n.param (jquery-2.1.3.min.js:4)
at Function.ajax (jquery-2.1.3.min.js:4)
at Object.saveDataLastPage (index.js:631)
at Object.renderLastPage (index.js:461)
at Object.recordResponse (index.js:597)
at HTMLButtonElement.<anonymous> (index.js:357)
at HTMLButtonElement.dispatch (jquery-2.1.3.min.js:3)
at HTMLButtonElement.r.handle (jquery-2.1.3.min.js:3)
The pertaining code is the following:
index.js:631
saveDataLastPage:function() {
$.ajax({
type: 'post',
url: '/URL/',
data: localStore,
crossDomain: true,
success: function (result) {
var pid = localStore.participant_id, snoozed = localStore.snoozed, uniqueKey = localStore.uniqueKey, pause_time=localStore.pause_time;
localStore.clear();
localStore.participant_id = pid;
localStore.snoozed = snoozed;
localStore.uniqueKey = uniqueKey;
localStore.pause_time=pause_time;
$("#question").html("<h3>Thank you, your responses have been sent.</h3>");
},
error: function (request, error) {
console.log(error);
$("#question").html("<h3>Error: Please check your internet connection.</h3><br><button>Send again</button>");
$("#question button").click(function () {app.saveDataLastPage();});
}
});
},
index.js:461
else {
var datestamp = new Date();
var year = datestamp.getFullYear(), month = datestamp.getMonth(), day=datestamp.getDate(), hours=datestamp.getHours(), minutes=datestamp.getMinutes(), seconds=datestamp.getSeconds(), milliseconds=datestamp.getMilliseconds();
localStore[uniqueKey + '.' + "completed" + "_" + "completedSurvey" + "_" + year + "_" + month + "_" + day + "_" + hours + "_" + minutes + "_" + seconds + "_" + milliseconds] = 1;
app.saveDataLastPage();
}
As stated before, on the genymotion emulator the ajax script works fine without the error and sends the data to the script.
I'm not sure why the emulator would work just fine but the error Uncaught TypeError: Illegal invocation suggests that it is a problem with the ajax post call. Specifically, the default setting of processing the data into a query string likely fails.
From the ajax documentation:
By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.
That means you can either turn off the processing processData: false which will post the data in the body of the request or you have to transform the localStore data into a proper object (from whatever it was before).
If it is like an object you can do the following:
var data = {};
for (var elem in localStore) {
data[elem] = localStore[elem];
}
or possibly in brief:
var data = {};
localStore.each(function(elem) { data[elem.name] = elem.value; });

Datatype of tabs.url in Chrome Extension with Firebase throws error

I'm trying to write a tab url I get from a chrome extension to the Firebase database. However, FB throws an error, namely:
extensions::uncaught_exception_handler:8 Error in response to tabs.query: Error: Firebase.update failed: First argument contains an invalid key ([object MouseEvent]) in path /user-posts/[object MouseEvent]/-KM8uJZHiNsgtQbNh13I. Keys must be non-empty strings and can't contain ".", "#", "$", "/", "[", or "]"
The corresponding code in my popup.js:
function writeNewPost(uid) {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
var urlvar = tabs[0].url;
var postData = {
uid: uid,
url: urlvar
};
var newPostKey = firebase.database().ref().child('posts').push().key;
var updates = {};
updates['/posts/' + newPostKey] = postData;
updates['/user-posts/' + uid + '/' + newPostKey] = postData;
return firebase.database().ref().update(updates);
});
};
So does it complain because the url contains forbidden characters? It must be somehow possible to convert it in order to then write it to the database.
The function gets called through this:
document.addEventListener("DOMContentLoaded", function() {
document.getElementById('clickme').addEventListener('click', writeNewPost);
});
The uid comes from the background.js, where the login is handled.
var uid = firebase.auth().currentUser.uid;
And it does return something.
What troubles me is that this same line in my popup.js returns null.
Well, the offending part is uid and has nothing to do with chrome.tabs API call - it comes from the outside (that you do not show).
It looks like you set writeNewPost as a handler for a mouse click - so it's getting passed a MouseEvent event object as its first parameter. That's where your error is.
It probably makes most sense to concentrate all your Firebase operations in the background page context - so instead of trying to call it directly, pass a Message to the background to perform the operation for you - because otherwise you have 2 instances of Firebase API and you need to do auth in both.
The problem was the uid is null during firebase.initializeApp() in the beginning of my popup.js. Calling firebase.auth().currentUser.uid in the function above returns the actual uid:
function writeNewPost() {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
var urlvar = tabs[0].url;
var uid = firebase.auth().currentUser.uid; //This
var postData = {
uid: uid,
url: urlvar
};
var newPostKey = firebase.database().ref().child('posts').push().key;
var updates = {};
updates['/posts/' + newPostKey] = postData;
updates['/user-posts/' + uid + '/' + newPostKey] = postData;
return firebase.database().ref().update(updates);
});
};

Netsuite POST data from a Portlet to a RESTlet in JSON

In NetSuite, I'm trying to create a form Portlet POST data to a RESTlet in JSON. I've checked the documentation and internet and all the examples I've been able to find are GET requests or they post to a backend Suitelet instead.
I've come to a point where I can make the request reach the RESTlet but it's not being formatted in JSON, so I get the following error:
Account: xxxxxxxxxxxxx
Environment: Production Date & Time: 6/11/2015 5:09 pm
Execution Time: 0.06s
Script Usage: 0
Script: gw_SS_FormBackend
Type: RESTlet
Function: postWMForm
Error: UNEXPECTED_ERROR
SyntaxError: Empty JSON string (null$lib#3)
I'm using the following code to set the submit button and it's working fine:
var headers = new Array();
headers['User-Agent-x'] = 'SuiteScript-Call';
headers['Authorization'] =
'NLAuth nlauth_account=' + cred.account +
', nlauth_email=' + cred.email +
', nlauth_signature=' + cred.password +
', nlauth_role=' + cred.role;
headers['Content-Type'] = 'application/json';
portlet.setSubmitButton(nlapiRequestURL(getRESTletURL(), null, headers, 'POST'), 'Submit', '_hidden');
My problem is I don't know how to convert the form data to JSON before submitting it.
I'd appreciate any help.
Why would you want to use a RESTlet? If you are in a portlet then you already have a valid NS session so you'd be better off using a Suitelet. A Suitelet you know is set up to handle JSON would be called thus:
nlapiRequestURL(suiteletURL', JSON.stringify{test:'it', when:new Date(), by:'Brett'}), {"content-type":'application/json'}, function(resp){console.log(resp.getBody());}, 'POST');
and your Suitelet code might include something like:
var body = request.getBody();
nlapiLogExecution('DEBUG', 'posted body', body);
var asJSON = JSON.parse(body);
var user = nlapiGetContext().getUser(); // system already knows who this is.
...
var obj = {
success:true,
stuff: asProcessed
};
response.setContentType('JAVASCRIPT');
response.writeLine( JSON.stringify(obj) );
Not quite as clean as a RESTlet but you avoid having to hack the credentials.
you can use JSON.stringify() function.
var headers = new Array();
headers['User-Agent-x'] = 'SuiteScript-Call';
headers['Authorization'] =
'NLAuth nlauth_account=' + cred.account +
', nlauth_email=' + cred.email +
', nlauth_signature=' + cred.password +
', nlauth_role=' + cred.role;
headers['Content-Type'] = 'application/json';
var myJsonHeader = JSON.stringify(headers);
portlet.setSubmitButton(nlapiRequestURL(getRESTletURL(), null, myJsonHeader, 'POST'), 'Submit', '_hidden');
Regards

Categories