i am trying to connect to independentreserve api using google apps script - google-apps-script

i think the issue is with the signature, the request body is same as in the independentreserve api docs. i am using apps script to connect with api, i tried using python and it works fine, but i am new to javascript and google apps script.
this is my code. can someone help with this?
function myFunction() {
var key = 'api-key'
var secret = 'api-secret'
var url = 'https://api.independentreserve.com/Private/GetOpenOrders'
// initialize nonce to current unix time in milliseconds
nonce = (new Date()).getTime();
// Set custom User-Agent string
var headers = {"User-Agent": "Independent Reserve Javascript API Client"};
var nonce = nonce++;
console.info("hELLO")
var message = [url, 'apiKey=' + key, 'nonce=' + nonce].join(',') ;
//var signer = crypto.createHmac('sha256', Buffer(secret, 'utf8'));
var signer = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, secret);
var signature = signer
.map(function(byte) {
// Convert from 2's compliment
var v = (byte < 0) ? 256 + byte : byte;
// Convert byte to hexadecimal
return ("0" + v.toString(16)).slice(-2);
}).join(',');
var headers = {'Content-Type': 'application/json', 'muteHttpExceptions': true};
var options = {
"apiKey": key,
"nonce": nonce,
"signature": signature,
"primaryCurrencyCode": "Xbt",
"secondaryCurrencyCode": "Usd",
"pageIndex": 1,
"pageSize": 25
}
var r = UrlFetchApp.fetch(url, options)
console.info(r.getContentText())
}

I believe your goal is as follows.
In order to request to api.independentreserve.com, you want to convert the following sample script of python. Ref
import time
import requests
import hmac
import hashlib
import json
from collections import OrderedDict
url = 'https://api.independentreserve.com/Private/GetOpenOrders'
key = 'api_key'
secret = 'api_secret'
nonce = int(time.time())
# make sure that parameter order is correct as specified in method documentation
parameters = [
url,
'apiKey=' + key,
'nonce=' + str(nonce),
'primaryCurrencyCode=Xbt',
'secondaryCurrencyCode=Usd',
'pageIndex=1',
'pageSize=10'
]
message = ','.join(parameters)
signature = hmac.new(
secret.encode('utf-8'),
msg=message.encode('utf-8'),
digestmod=hashlib.sha256).hexdigest().upper()
# make sure this collection ordered in the same way as parameters
data = OrderedDict([
('apiKey', key),
('nonce', nonce),
('signature', str(signature)),
('primaryCurrencyCode', 'Xbt'),
('secondaryCurrencyCode', 'Usd'),
('pageIndex', 1),
('pageSize', 10)])
headers = {'Content-Type': 'application/json'}
r = requests.post(url, data=json.dumps(data, sort_keys=False), headers=headers)
print(r.content)
When I saw your script, I thought the following modification points:
Utilities.computeDigest will be Utilities.computeHmacSha256Signature.
(new Date()).getTime() cannot be directly used. In this case, I thought that it's (new Date()).getTime().toString().slice(0, 10).
muteHttpExceptions cannot be used in the request header.
'Content-Type': 'application/json' can be used as contentType: "application/json". And in this case, it is required to convert the JSON object to the string value.
When the above python script is converted to Google Apps Script by modifying these points, it becomes as follows.
Sample script:
function myFunction() {
var key = 'api_key';
var secret = 'api_secret';
var url = 'https://api.independentreserve.com/Private/GetOpenOrders';
var nonce = (new Date()).getTime().toString().slice(0, 10);
var parameters = [
url,
'apiKey=' + key,
'nonce=' + nonce.toString(),
'primaryCurrencyCode=Xbt',
'secondaryCurrencyCode=Usd',
'pageIndex=1',
'pageSize=10'
];
var message = parameters.join(",");
var signature = Utilities.computeHmacSha256Signature(message, secret, Utilities.Charset.UTF_8).map(b => ('0' + (b & "0xFF").toString(16)).slice(-2)).join('');
var payload = {
"apiKey": key,
"nonce": nonce,
"signature": signature,
"primaryCurrencyCode": "Xbt",
"secondaryCurrencyCode": "Usd",
"pageIndex": 1,
"pageSize": 25
};
var options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify(payload),
muteHttpExceptions: true,
};
var r = UrlFetchApp.fetch(url, options);
console.log(r.getContentText());
}
Note:
I confirmed that this request of the above Google Apps Script is the same as the sample python script. So, when an error occurs, please confirm your key, secret, and the values of parameters, again.
References:
Airbridge API
computeHmacSha256Signature(value, key, charset)
fetch(url, params)

Related

Google sheets appscript - Pipedrive post

I am getting the error 'Exception: Request failed for https://api.pipedrive.com returned code 404. Truncated server response: {"status":false,"error":"Unknown method ."} (use muteHttpExceptions option to examine full response)' when attempting a put/post request to a Pipedrive deal. (Date Field) Below is the function producing the error. I am pretty sure this is something obvious but have not been able to determine the cause on my own. Any help would be appreciated.
function Update_pipedrive_Date(dealid, field_name){
var todays_date = Utilities.formatDate(new Date(), "GMT-5", "yyyy-MM-dd"); // "yyyy-MM-dd'T'HH:mm:ss'Z'"
var url = 'https://api.pipedrive.com/v1/deals/' + dealid + '?api_token='+oys_api_key
var data = {field_name: todays_date};
var options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(data)
};
var response = UrlFetchApp.fetch(url, options);
return response
}
def update_field(this_dealid, fieldid):
tday = now.strftime("%Y-%m-%d")
headers = {'content-type': 'application/json'}
updates = {fieldid: tday}
resp = requests.put(
'https://api.pipedrive.com/v1/deals/' + str(
this_dealid) + '?api_token=' + api_key,
data=json.dumps(updates), headers=headers)
return resp
I believe your goal is as follows.
Your provided python script works fine.
You want to convert your python script to Google Apps Script.
Modification points:
Your python script requests the PUT method. But your Google Apps Script requests the POST method.
In the case of {field_name: todays_date}, the key is always "field_name".
When these points are reflected in your script, how about the following modification?
Modified script:
var field_name = "###"; // Please set your field_name.
var dealid = "###"; // Please set your dealid.
var oys_api_key = "###"; // Please set your oys_api_key.
var todays_date = Utilities.formatDate(new Date(), "GMT-5", "yyyy-MM-dd");
var url = 'https://api.pipedrive.com/v1/deals/' + dealid + '?api_token=' + oys_api_key;
var data = { [field_name]: todays_date };
var options = {
'method': 'put',
'contentType': 'application/json',
'payload': JSON.stringify(data)
};
var response = UrlFetchApp.fetch(url, options);
Note:
I think that the request of this Google Apps Script is the same as your python script. But if this script occurs an error, please confirm your variables of field_name, dealid, oys_api_key again. Even when these variables are the valid values, when an error occurs, I'm worried that the URL might not be able to be accessed from the Google side.
Reference:
fetch(url, params)

Sign a call to Coinbase Pro API with Google Apps Script

I'm going mad trying to send my first API call to Coinbase Pro using Google Apps Script. In node.js is pretty easy (https://docs.pro.coinbase.com/#signing-a-message) but doing the same with Google scripts is just returning again and again "Invalid Signature".
This is the code I'm using:
function GetMyAccounts () {
var globalvars_CB = {
'apikey' : 'f7d20a*******18c',
'secret' : '******pIIitRbWCv9N/mMWaR*****mGQMuI+m/vSbU1zuh5U6WFiFw==',
'passphrase' : 'ceacdsewfcsa',
'uri' : 'https://api.pro.coinbase.com'
}
var requestPath = '/accounts';
var timestamp = Math.floor(Date.now() / 1000);
var options = {
'method' : 'GET',
'muteHttpExceptions' : true,
'headers' : {
'Content-Type': 'application/json',
'CB-ACCESS-KEY' : globalvars_CB.apikey,
'CB-ACCESS-SIGN' : SignAPICall(globalvars_CB.secret, timestamp, 'GET', requestPath, ''),
'CB-ACCESS-TIMESTAMP' : timestamp,
'CB-ACCESS-PASSPHRASE' : globalvars_CB.passphrase,
}
}
var responseJson = UrlFetchApp.fetch(globalvars_CB.uri+requestPath, options);
Logger.log(responseJson);
}
function SignAPICall(secret, timestamp, method, requestPath, body) {
var what = (timestamp + method + requestPath + body);
var decodedsecret = Utilities.base64Decode(secret).toString();
var hmac = Utilities.computeHmacSha256Signature(what, decodedsecret);
hmac = Utilities.base64Encode(hmac);
return (hmac);
}
I really need help :-) - Thanks!
In your script, it supposes that your request headers except for the value of CB-ACCESS-SIGN and endpoint are correct. Please be careful this.
Modification point:
In the case of Utilities.base64Decode(secret).toString(), the array is converted to the string. I think that this might be the reason of your issue.
When above point is reflected, it becomes as follows.
Modified script:
In this case, the function SignAPICall is modified.
function SignAPICall(secret, timestamp, method, requestPath, body) {
var what = (timestamp + method + requestPath + body);
var decodedsecret = Utilities.base64Decode(secret); // Modified
var res = Utilities.computeHmacSha256Signature(Utilities.newBlob(what).getBytes(), decodedsecret); // Modified
hmac = Utilities.base64Encode(res);
return hmac;
}
In this case, value and key of computeHmacSha256Signature(value, key) are the byte array.
Note:
When I checked above modified script by comparing the sample scripts of the official document, I could confirm that the same result can be obtained.
Unfortunately, I cannot test the request to the API using above modified script while I can confirm that the same signature from the sample script at the official document is retrieved from the above modified script. So please test the request in you environment. When you requested to the API using above modified script, when an error occurs, please check the request headers, endpoint and secret, again.
References:
Signing a Message
base64Decode(encoded)
computeHmacSha256Signature(value, key)
I finally found the solution to it. It was a problem with the type of data I submitted to "Utilities.computeHmacSha256Signature". In the code you can find the function working well.
function SignAndCallAPI(method, requestPath, body) {
var timestamp = Math.floor(Date.now() / 1000).toString();
var what = Utilities.base64Decode(Utilities.base64Encode(timestamp + method + requestPath + body));
var decodedsecret = Utilities.base64Decode(globalvars_CB.secret);
var hmac = Utilities.base64Encode(Utilities.computeHmacSha256Signature(what, decodedsecret));
var options = {
'method' : method,
'muteHttpExceptions' : true,
'headers' : {
'Content-Type': 'application/json',
'CB-ACCESS-KEY' : globalvars_CB.apikey,
'CB-ACCESS-SIGN' : hmac,
'CB-ACCESS-TIMESTAMP' : timestamp,
'CB-ACCESS-PASSPHRASE' : globalvars_CB.passphrase,
}
}
var responseJson = UrlFetchApp.fetch(globalvars_CB.uri+requestPath, options);
Logger.log(responseJson);
return(responseJson);
}

calling Drive REST API Version 3 via URL-fetch in apps script delivers only default attributes

I am calling Drive REST API Version 3 via URL-fetch in apps script. I am calling Files list method and query for files. Querying is working, but I only get the default attributes of the files in the api's response. If I am using fields parameter to get more fields it is just ignored.
var retVal =[];
var baseUrl = "https://www.googleapis.com/drive/v3/files";
var token = ScriptApp.getOAuthToken();
var options = {
method: "GET",
headers: {"Authorization": "Bearer " + token},
}
var maxResults = 100;
var params = {
q: query,
pageSize: maxResults,
fields: 'nextPageToken,incompleteSearch,files(kind,id,name,mimeType,starred,trashed)',
};
do {
var queryString = Object.keys(params).map(function(p) {
return [encodeURIComponent(p), encodeURIComponent(params[p])].join("=");
}).join("&");
var apiUrl = baseUrl + "?" + queryString;
Logger.log(apiUrl);
var response = JSON.parse(UrlFetchApp.fetch( apiUrl,
options).getContentText());
//Logger.log(response);
response.files.forEach(function(fileObj) {
retVal.push(fileObj);
})
params['pageToken'] = response.nextPageToken;
} while (params.pageToken);
Logger.log(retVal);
return retVal
Encoded Query: https://www.googleapis.com/drive/v3/files?q=name%20contains%20%22Test%20%2F%20Blub%2033%22%20and%20not%20mimeType%20%3D%20%22application%2Fvnd.google-apps.folder%22%20and%20trashed%20%3D%20false&pageSize=100&fields=nextPageToken%2CincompleteSearch%2Cfiles(kind%2Cid%2Cname%2CmimeType%2Cstarred%2Ctrashed)&orderBy=folder%2CmodifiedTime%20desc%2Ctitle&supportsTeamDrives=false&includeTeamDriveItems=false
test results from API: [{kind=drive#file, name=Kopie von Test / Blub 33, id=1oTbd78Bn7R7Xjo6TEAAyZmE5CjwdgRMT, mimeType=application/json}, {kind=drive#file, name=Test / Blub 33, id=12IpttBvSY-Z31ueNqG_Dmb46dXH5udcl, mimeType=application/json}, {kind=drive#file, name=Test / Blub 33, id=1FqKyDFT0bpp1JuAj3WeSV6AL-b12X4vb, mimeType=application/json}]
can someone help why the api is ignoring fields parameter?
Try replacing & with & when evaluation the queryString value.
var queryString = Object.keys(params).map(function(p) {
return [encodeURIComponent(p), encodeURIComponent(params[p])].join("=");
}).join("&");

Spotify API authorisation via Google Apps Script

I am using the following code to make requests to the Spotify API via Google Apps Script:
function search() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var artist = sheet.getRange(1,1).getValue();
artist = encodeURIComponent(artist.trim());
var result = searchSpotify(artist);
Logger.log(result);
}
function searchSpotify(artist) {
//searches spotify and returns artist ID
var response = UrlFetchApp.fetch("https://api.spotify.com/v1/search?q=" + artist + "&type=artist&limit=1",
{ method: "GET",
headers:{
"contentType": "application/json",
'Authorization': "Bearer BQBnpSUdaEweirImw23yh2DH8OGhTwh5a_VnY_fgb2BPML0KvFvYd04CaEdUhQN9N4ZUXMIVfJ1MjFe1_j0Gl0UoHDhcoC_dklluZyOkq8Bo6i2_wfxSbGzP3k5EUjUKuULAnmTwCdkdZQnl-SNU0Co"
},
});
json = response.getContentText();
var data = JSON.parse(json);
var uri = data.artists.items[0].uri.slice(15);
var getArtists = getRelatedArtists(uri);
Logger.log(getArtists);
return getArtists;
}
function getRelatedArtists(uri) {
//searches related artists with the returned ID
var response = UrlFetchApp.fetch("https://api.spotify.com/v1/artists/" + uri + "/related-artists",
{ method: "GET",
headers:{
"contentType": "application/json",
'Authorization': "Bearer BQBnpSUdaEweirImw23yh2DH8OGhTwh5a_VnY_fgb2BPML0KvFvYd04CaEdUhQN9N4ZUXMIVfJ1MjFe1_j0Gl0UoHDhcoC_dklluZyOkq8Bo6i2_wfxSbGzP3k5EUjUKuULAnmTwCdkdZQnl-SNU0Co"
},
});
json = response.getContentText();
var data = JSON.parse(json);
var listArtists = [];
for(var i = 0, len = data.artists.length; i < len; i++){
listArtists.push(data.artists[i].name);
}
return listArtists;
}
This works fine using the temporary Authorisation token from the Spotify website but this token refreshes every hour and so is obviously useless.
I am trying to use my own Authorisation token and ID which I have setup on Spotify however I'm struggling to make this work. As I understand it I may need to add an extra step at the beginning to start the authorisation process but I've tried all methods suggested but keep receiving server errors.
From the document, it seems that "Client Credentials Flow" uses the basic authorization.
In order to use this, at first, you are required to retrieve "client_id" and "client_secret".
Sample script:
var clientId = "### client id ###"; // Please set here.
var clientSecret = "### client secret ###"; // Please set here.
var url = "https://accounts.spotify.com/api/token";
var params = {
method: "post",
headers: {"Authorization" : "Basic " + Utilities.base64Encode(clientId + ":" + clientSecret)},
payload: {grant_type: "client_credentials"},
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText())
From curl sample, grant_type is required to send as form.
Result:
The document says that the response is as follows.
{
"access_token": "NgCXRKc...MzYjw",
"token_type": "bearer",
"expires_in": 3600,
}
Note:
This is a simple sample script. So please modify this for your situation.
I prepared this sample script by the sample curl in the document.
Reference:
Client Credentials Flow
Edit:
As your next issue, you want to retrieve the access token from the returned value. If my understanding is correct, how about this modification? Please modify my script as follows.
From:
Logger.log(res.getContentText())
To:
var obj = JSON.parse(res.getContentText());
Logger.log(obj.access_token)
When the value is returned from API, it returns as a string. So it is required to parse it as an object using JSON.parse().

Hmac256 Signature invalid error Google App Script

I am attempting to retrieve trades from a service called 3Commas in Google Apps Script. I've worked with public endpoints before, but this is the first time I've attempted to work with signed endpoints. I'm currently receiving an error that states:
[19-01-09 16:46:24:592 EST] {"error":"signature_invalid","error_description":"Provided signature is invalid"}
I'm guessing this is a formatting issue on my part. I'm using jsSHA to build the HMAC part. I've tried following the example in the docs. But I haven't quite got it yet. Any suggestions on what it could be?
3Commas Docs: https://github.com/3commas-io/3commas-official-api-docs#signed--endpoint-security
function main() {
var key = 'apikey';
var secret = 'apisecret';
var baseUrl = "https://3commas.io/public/api";
var endPoint = "/ver1/smart_trades";
var pointParams = "?limit=10&offset=&account_id=&scope=&type="
//base url + end point + params
var queryString = baseUrl+endPoint+pointParams;
var message = queryString;
var secret = secret;
var shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.setHMACKey(secret, "B64");
shaObj.update(message);
var signature = shaObj.getHMAC("B64");
//headers
var hparams = {
'method': 'get',
'headers': {'APIKEY': key,
'Signature': signature},
'muteHttpExceptions': true
};
//call
var data = UrlFetchApp.fetch(queryString , hparams).getContentText();
Logger.log(data)
}
How about this modification? From 3Commas Docs in your question, I propose the modification points as follows.
Modification points:
It seems that the value which is required to encrypt is after https://3commas.io.
You can encrypt the values using the method of computeHmacSha256Signature() in Class Utilities of GAS. In this case, jsSHA is not required to be used.
But when computeHmacSha256Signature() is used, the value becomes the bytes array of the signed hexadecimal. So it is required to convert it to the unsigned hexadecimal.
Modified script:
function main() {
var key = 'apikey';
var secret = 'apisecret';
var baseUrl = "https://3commas.io"; // Modified
var endPoint = "/public/api/ver1/smart_trades"; // Modified
var pointParams = "?limit=10&offset=&account_id=&scope=&type="; // or "?limit=10"
var queryString = endPoint + pointParams; // Modified
var signature = Utilities.computeHmacSha256Signature(queryString, secret); // Added
signature = signature.map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join(""); // Added
//headers
var hparams = {
'method': 'get',
'headers': {'APIKEY': key,
'Signature': signature},
'muteHttpExceptions': true
};
//call
var data = UrlFetchApp.fetch(baseUrl + queryString , hparams).getContentText(); // Modified
Logger.log(data)
}
Note:
About var pointParams = "?limit=10&offset=&account_id=&scope=&type=", in the case of the endpoint you use, limit, offset, account_id, scope and type are no mandatory. So it might be var pointParams = "?limit=10". If the error occurs, please try it.
References:
computeHmacSha256Signature(value, key)
Public Rest API for 3commas.io (2018-10-26)
This document is more detail.
I cannot confirm whether this modified script works. I'm sorry for this situation. So if it didn't work, I apologize. At that time, can you provide the detail information of the situation?