How to Convert cURL POST Request in PHP to Google Apps Script - google-apps-script

VoIP.ms offers the ability to send SMS text messages from its API. They provide the following sample code:
<?
$postfields = array(
'api_username'=>'john#domain.com',
'api_password'=>'password',
'method'=>'getServersInfo',
'server_pop'=>'1'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt($ch, CURLOPT_POST, true );
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
curl_setopt($ch, CURLOPT_URL, "https://voip.ms/api/v1/rest.php");
$result = curl_exec($ch);
curl_close($ch);
$data=json_decode($result,true);
echo "<pre>";
print_r($data);
echo "</pre>";
?>
When I execute the following command from the command line in Terminal, I'm able to successfully send an SMS text message from VoIP.ms:
curl -X POST -F 'api_username=john#domain.com' -F 'api_password=password' -F 'method=sendSMS' -F 'did=1234567890' -F 'dst=0987654321' -F 'message=Hello' https://voip.ms/api/v1/rest.php
By searching around on Google, I've cobbled together the following Google Apps Script:
function sendSMS() {
var formData = {
api_username : "john#domain.com",
api_password : "password",
method : "sendSMS",
did : 1234567890,
dst : 0987654321,
message : "Hello"
};
var options = {
method : "POST",
payload : formData
};
UrlFetchApp.fetch("https://voip.ms/api/v1/rest.php", options);
}
When I run the script, I get the following error:
Exception: Request failed for https://voip.ms returned code 500. Truncated server response: > xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">env:Bodyenv:Faultenv:Codeenv:Value... (use muteHttpExceptions option to examine full response)
sendSMS # Code.gs:14
Any suggestions on where I'm going wrong?

I believe your goal is as follows.
You want to convert the following curl command to Google Apps Script. And, your curl command works fine.
curl -X POST -F 'api_username=john#domain.com' -F 'api_password=password' -F 'method=sendSMS' -F 'did=1234567890' -F 'dst=0987654321' -F 'message=Hello' https://voip.ms/api/v1/rest.php
Modification points:
From your sample curl command, it seems that did : 1234567890 and dst : 0987654321 should be did : "1234567890" and dst : "0987654321". This has already been mentioned in the TheMaster's comment.
In your curl command, -F is used. In this case, the content type of the request header is multipart/form-data. Unfortunately, UrlFetchApp uses application/x-www-form-urlencoded as the default content type.
When these points are reflected in tha Google Apps Script, how about the following modification?
Modified script:
function sendSMS() {
var formData = {
api_username: "john#domain.com",
api_password: "password",
method: "sendSMS",
did: "1234567890",
dst: "0987654321",
message: "Hello"
};
var options = { payload: Object.entries(formData).reduce((o, [k, v]) => (o[k] = Utilities.newBlob(v), o), {}) };
UrlFetchApp.fetch("https://voip.ms/api/v1/rest.php", options);
}
Note:
This modified script supposes that your URL of https://voip.ms/api/v1/rest.php can be accessed from the Google side, and the values of your formData are valid values. Please be careful about this.
At UrlFetchApp, the request body of multipart/form-data is automatically created in the internal server side. But, if this request body couldn't be used to your API (There is sometimes a case that this request body cannot be used.), please test the request using FetchApp (Author: me).
Reference:
fetch(url, params)

Related

How to parse a simple JSON object (accessing single elements)?

I'm trying to process simple JSON data in a Google-Apps-Script (a Webhook Receiver). For a test I send data from the console:
curl -d "{"result":true,"count":42,"exchange":"Kroakex"}" -H "Content-Type: application/json" -X POST https://script.google.com/macros/s/xxx/exec
... but I can't access the json elements in my processing function:
function doPost(e) {
var jsonString = JSON.stringify(e.postData.contents);
var jsonObj = JSON.parse(jsonString);
console.log(jsonObj); // ---> "{result:true,count:42,exchange:Kroakex}"
console.log(jsonObj.count); // ---> "undefined"
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = Math.max(sheet.getLastRow(),1);
sheet.insertRowAfter(lastRow);
sheet.getRange(lastRow + 1, 1).setValue(jsonObj['count']); // ---> nothing
sheet.getRange(lastRow + 1, 2).setValue(jsonObj['exchange']); // ---> nothing
sheet.getRange(lastRow + 1, 3).setValue(jsonObj); // ---> {result:true,count:42,exchange:Kroakex}
SpreadsheetApp.flush();
return HtmlService.createHtmlOutput("post request received");
}
When I instead create the JSON string manually with this line, it all works fine:
const jsonString = '{"result":true, "count":42, "exchange":"Kroakex"}';
Can anyone help?
I would like to propose the following modification.
Modification points:
I think that in your curl command, there are the modification points.
When your curl command is used, e.postData.contents is "{result:true,count:42,exchange:Kroakex}". Because at -d "{"result":true,"count":42,"exchange":"Kroakex"}", " is used in "{,,,}". In this case, your Google Apps Script cannot parse the values as JSON object. Please escape " or enclose by '. By this, e.postData.contents becomes "{\"result\":true,\"count\":42,\"exchange\":\"Kroakex\"}".
In this case, please use -L and -X POST is not required.
The modified curl command is as follows.
curl -L -d '{"result":true,"count":42,"exchange":"Kroakex"}' -H "Content-Type: application/json" https://script.google.com/macros/s/###/exec
or
curl -L -d "{\"result\":true,\"count\":42,\"exchange\":\"Kroakex\"}" -H "Content-Type: application/json" https://script.google.com/macros/s/###/exec
By above modification, your Google Apps Script becomes as follows.
Modified script:
function doPost(e) {
var jsonObj = JSON.parse(e.postData.contents); // <--- Modified
console.log(jsonObj);
console.log(jsonObj.count);
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = Math.max(sheet.getLastRow(),1);
sheet.insertRowAfter(lastRow);
sheet.getRange(lastRow + 1, 1).setValue(jsonObj['count']);
sheet.getRange(lastRow + 1, 2).setValue(jsonObj['exchange']);
sheet.getRange(lastRow + 1, 3).setValue(JSON.stringify(jsonObj)); // <--- Modified
SpreadsheetApp.flush();
return HtmlService.createHtmlOutput("post request received");
}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
When you use the curl command, I think that the returned value might be suitable for return ContentService.createTextOutput("post request received"); instead of return HtmlService.createHtmlOutput("post request received");.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
Added:
When you want to access to Web Apps using the URL of https://script.google.com/macros/s/###/dev, it is required to use the access token. The modified curl command is as follows.
curl -L \
-H "Authorization: Bearer ### access token ###" \
-H "Content-Type: application/json" \
-d '{"result":true,"count":42,"exchange":"Kroakex"}' \
"https://script.google.com/macros/s/#####/dev"
In this case, https://www.googleapis.com/auth/drive.readonly and https://www.googleapis.com/auth/drive can be used for the scope.
Reference:
How to use dev mode from outside

Upload file in BOX using BOX API using C#

I have BOX account and in API document
curl https://upload.box.com/api/2.0/files/content \
-H "Authorization: Bearer ACCESS_TOKEN" \
-F filename=#FILE_NAME \
-F parent_id=PARENT_FOLDER_ID
I am stuck with this filename and parent_id how to pass the filename and parent_id
i have tried lots of way, but nothing is working for me.
Following is the code:
httpWReq.Method = "POST";
httpWReq.Headers.Add("Authorization", "Bearer ");
httpWReq.ContentType = "multipart/form-data";
//{\"parent_id\":\""+parentID +"\"}
//byte[] file = File.ReadAllBytes(postData);
httpWReq.ContentLength = data.Length;
using (Stream reqStream = httpWReq.GetRequestStream())
{
reqStream.Write(data, 0, data.Length);
//reqStream.Close();
}
using (HttpWebResponse response = (HttpWebResponse)httpWReq.GetResponse())
{
//Console.WriteLine("HTTP/{0} {1} {2}", response.ProtocolVersion, (int)response.StatusCode, response.StatusDescription);
response.Close();
}
Need Help
Thanks in Advance
Vaibhav,
Please see example of how official Box C# SDK is doing upload:
https://github.com/box/box-windows-sdk-v2/blob/master/Box.V2/Managers/BoxFilesManager.cs
Is there any reason why you don't want to use Box Windows SDK?

How can I POST JSON data to a remote server in Laravel 4?

I am trying to HTTP POST JSON encoded data to a remote server.
Using cURL, this would be done as
curl -X POST -H "Content-Type: application/json" -d '{"name":"Jeff"}' http://somesite/someuri
I can not find a Laravel approach to doing this.
[ * * * UPDATE: My Solution * * * ]
I ended up using PHP's HttpRequest
$httpRequest_OBJ = new httpRequest($server, HTTP_METH_POST, NULL);
$httpRequest_OBJ->setBody($jsonEncodedData);
$httpRequest_OBJ->setContentType('application/json');
$result = $httpRequest_OBJ->send();
$reply = $result->getBody();
In this SO post I answered a very similar question, and it's a direct solution to your problem, please follow the link for detailed instructions.
How to send JSON by POST in Laravel 4 via CURL (jyggen/curl)
To put it in a nutshell, after you install the jyggen/curl package in your laravel app with composer, all you have to do is:
use jyggen\Curl;
$url = "http://some.url/"; //the gateway to which you want to post the json payload
$file = 'path/to/file.json';
$data = File::get($file); //or wherever else you get your json from
$request = new Request($url); // jyggen\Curl\Request
$request->setOption(CURLOPT_FOLLOWLOCATION, true);
$request->setOption(CURLOPT_RETURNTRANSFER, true);
$request->setOption(CURLOPT_POST, true);
$request->setOption(CURLOPT_POSTFIELDS, $data);
$request->setOption(CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
);
$request->execute();
if ($request->isSuccessful()) {
return $request->getRawResponse();
} else {
throw new Exception($resquest->getErrorMessage());
}
Above #Gadoma example works pretty well in Laravel 5 also. But I had to change use to:
use Jyggen\Curl\Request;
and also remove line:
$request->setOption(CURLOPT_RETURNTRANSFER, true);
since there were some errors. Now it works like charm.

Error file_get_contents with twitter search api

I have the following problem: when this script runs
$url = 'http://search.twitter.com/search.json?q=obama';
$tw = file_get_contents($url,0,null,null);
I receive this message.
Warning (2): file_get_contents(http://search.twitter.com/search.json?q=obama) [function.file-get-contents]: failed to open stream: HTTP request failed! HTTP/1.0 420 Client Error (420)
But it's very strange because it has always been working!
Can you help me?
Adding...I tried to use CURL in this way
$url = 'http://search.twitter.com/search.json?q=obama';
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_URL, $url);
$contents = curl_exec($c);
echo $contents;
curl_close($c);
but call returned this message
"error":"You have been rate limited. Enhance your calm."} and it seemed absurd because I didn't make any request before.
I found everywhere that the best practice in this case is use CURL.
I don't know why but then I tried to pass parameter ('obama') with urlencode and it seems to be that syntax (previously incorrect) the main cause of this warning.

Remote Transmission session doesn't respond after providing corrent session id

Well, that would be a rather obscure topic but I'll give it a try, maybe someone will know the answer.
I am writing a little remote Node.js client for the Transmission BitTorrent Client. Communication is handled via RPC using JSON objects.
Here is the specification.
And my code (written in CoffeeScript, if that is a problem I can provide the equivalent JavaScript code also, just didn't want to make this question too long to read), the important part:
runRemoteCommand: (params) ->
# convert JSON object to string
params = JSON.stringify params #, null, " "
# set request options
options =
host: #config.host
port: #config.port
path: #config.rpcPath
auth: "#{#config.username}:#{#config.password}"
headers:
'Content-Type': 'application/json'
'Content-Length': params.length
method: 'GET'
# we don't know the session id yet
sessionId = false
# wrapped in a function so it could be run two times, not really a finished solution
run = () =>
# If the session id is provided, set the header, as in 2.3.1 of the specs
if sessionId
options.headers["X-Transmission-Session-Id"] = sessionId
# define the request object and a callback for the response
request = #http.get options, (response) =>
# log everything for debug purposes
console.log "STATUS: #{response.statusCode}"
console.log "HEADERS: #{JSON.stringify response.headers}"
response.setEncoding 'utf8'
response.on "data", (data) =>
console.log "BODY: #{data}"
# if status code is 409, use provided session id
if response.statusCode == 409
sessionId = response.headers["x-transmission-session-id"]
console.log "sessionId: #{sessionId}"
# running it immediately sometimes caused the remote server to provide a 501 error, so I gave it a timeout
setTimeout run, 5000
# no output here
request.on "error", (e) =>
console.log "ERROR: #{e}"
# actually send the request
request.write params
request.end()
# run our function
run()
The params variable is defined as:
params =
"arguments":
"filename": link
"method": "torrent-add"
"tag": 6667
Everything works fine until I set a valid session id. On the first time the run function is called, I get the following output (formatted it a little to be more eye-friendly):
STATUS: 409
HEADERS:
{
"server":"Transmission",
"x-transmission-session-id":"io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR",
"date":"Wed, 04 Apr 2012 08:37:37 GMT",
"content-length":"580",
"content-type":"text/html; charset=ISO-8859-1"
}
sessionId:
io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR
BODY: 409:
ConflictYour request had an invalid session-id
header.To fix this, follow these steps: When reading a
response, get its X-Transmission-Session-Id header and remember it
Add the updated header to your outgoing requests When you get this
409 error message, resend your request with the updated
headerThis requirement has been added to help prevent CSRF
attacks.X-Transmission-Session-Id:
io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR
Which is exactly what should be returned by the remote server when no session id is provided. However, after setting the session id in the header, the server doesn't respond. The second run call is fired and the request is sent (confirmed by placing some useful console.logs), but the response callback is never fired. I receive no response from the remote server and my application freezes waiting.
I'm pretty sure the error is on my side, not on the server's, because an out-of-the-box remote client for android works just fine when connecting to the same remote session.
Am I performing the request correctly? Especially the JSON part?
EDIT: A little test
I have written a little php script to test if the JSON-encoded request is ok and used it as a "fake" remote transmission. Here it is:
$headers = apache_request_headers();
// Simulate transmission's behavior
if (!isset($headers['X-Transmission-Session-Id'])) {
header("HTTP/1.0 409 Conflict");
header("X-Transmission-Session-Id: test");
}
print_r($headers);
// Is there a nicer way to get the raw request?
print_r(file_get_contents('php://input'));
And, personally, I don't see anything wrong in the data outputted by this test. After returning the 409 status code, the Node.js app properly assigns the session id for the request. The first print_r prints an array:
Array
(
[Content-type] => application/json
[Content-length] => 152
[X-Transmission-Session-Id] => test
[Host] => tp.localhost
[Connection] => keep-alive
)
The second one prints a string, which is a properly formatted JSON string (nothing more in it):
{
"arguments": {
"filename": "http://link-to-torrent"
},
"method": "torrent-add",
"tag": 6667
}
I really can't see what am I doing wrong. Some third-party clients which I tested with the same remote server work properly.
Havng the same issue i've done this class. I'm thinking better way do a getData, method. But it works.
http = require "http"
_ = require "underscore"
class Connect
constructor: (#login, #password, #host='127.0.0.1', #port=9091, #headers={}) ->
getData: (params)->
key = "x-transmission-session-id"
options = {
host: #host
port: #port
path: '/transmission/rpc',
method: 'POST',
headers: #headers || {},
auth: "#{ #login }:#{ #password }"
}
_.extend options, params || {}
req = http.request(options, (res)=>
if res.statusCode == 401
console.log "Auth errror"
else if res.statusCode == 409
auth_header={}
auth_header[key] = res.headers[key]
_.extend #headers, auth_header
#getData(params)
else if res.statusCode == 200
res.setEncoding 'utf8'
res.on('data', (chunk)->
#here should be an emmit of data
console.log chunk
)
else
console.log "Error #{ res.statusCode }"
)
req.write('data\n')
req.write('data\n')
req.end()
connector = new Connect "transmission", "password"
connector.getData()
Well, I was able to circumvent - but not solve - the problem, using mikeal's request, which also simplified my code. The most recent version looks like this:
runRemoteCommand: (params, callback = false) =>
options =
uri: #uri
method: "POST"
json: params
if #sessionId
options.headers =
"X-Transmission-Session-Id": #sessionId
request options, (error, response, body) =>
retVal =
success: false
end = true
if error
retVal.message = "An error occured: #{error}"
else
switch response.statusCode
when 409
if response.headers["x-transmission-session-id"]
#sessionId = response.headers["x-transmission-session-id"]
end = false
#.runRemoteCommand params, callback
else
retVal.message = "Session id not present"
when 200
retVal.success = true
retVal.response = body
else retVal.message = "Error, code: #{response.statusCode}"
callback retVal if end && callback
I'll leave this answer unaccepted for the time being because I still don't know what was wrong with the "raw" version.
#!/bin/bash
#-----------------------------------------------------------------------
#
DEBUG=0
HOST="192.168.1.65"
PORT="8181"
TRURL="http://$HOST:$PORT/transmission/rpc"
USER="admin"
PASSWORD="password1"
XTSID=""
#-------------------------------------
#
function getSID ()
{
local S="$1"
S=${S##*X-Transmission-Session-Id: }
S=${S%%</code>*}
echo $S
}
#-------------------------------------
function getData ()
{
local REQUEST="$1"
local RET=$(curl --silent -H "X-Transmission-Session-Id: $XTSID" \
-H "Content-type: application/json" \
-X POST \
-d "$REQUEST" \
--user $USER:$PASSWORD $TRURL)
((DEBUG)) && echo $XTSID
if [[ "$RET" =~ "409: Conflict" ]]
then
XTSID=$(getSID "$RET")
((DEBUG)) && echo "XTSID $XTSID"
RET=$(curl --silent -H "X-Transmission-Session-Id: $XTSID" \
-H "Content-type: application/json" \
-X POST \
-d "$REQUEST" \
--user $USER:$PASSWORD $TRURL)
fi
echo $RET
}
#-------------------------------------
R='{"method":"session-stats"}'
RET=$(getData "$R")
echo $RET