Posting images to twitter in Node.js using Oauth - node.js

I'm trying to post images to Twitter using the Oauth module. Here is what I have:
It throws a 403 error, I know im doing something wrong with how I add the media to the post but Im just not sure where to go from here.
var https = require('https');
var OAuth= require('oauth').OAuth;
var keys = require('./twitterkeys');
var twitterer = new OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
keys.consumerKey,
keys.consumerSecret,
"1.0",
null,
"HMAC-SHA1"
);
var params = {
status : "Tiger!",
media : [("data:" + mimeType + ";base64,") + fs.readFileSync(path,'base64')]
};
//function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback)
twitterer.post("https://upload.twitter.com/1/statuses/update_with_media.json",
keys.token, keys.secret, params, "multipart/form-data",
function (error, data, response2) {
if(error){
console.log('Error: Something is wrong.\n'+JSON.stringify(error)+'\n');
}else{
console.log('Twitter status updated.\n');
console.log(response2+'\n');
}
});
Here is what I belive im supose to be doing but I don't know how to do that in the Node.js Oauth module.
Posting image to twitter using Twitter+OAuth

Reviewing the code, it looks like there's no multipart/form-data handling at all in the node-oauth package right now. You can still use the node-oauth function to create the authorization header, but you'll have to do the multipart stuff on your own.
There are probably third-party libraries that can help out with that, but here's how I got it to work constructed by hand.
var data = fs.readFileSync(fileName);
var oauth = new OAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
twitterKey, twitterSecret,
'1.0', null, 'HMAC-SHA1');
var crlf = "\r\n";
var boundary = '---------------------------10102754414578508781458777923';
var separator = '--' + boundary;
var footer = crlf + separator + '--' + crlf;
var fileHeader = 'Content-Disposition: file; name="media"; filename="' + photoName + '"';
var contents = separator + crlf
+ 'Content-Disposition: form-data; name="status"' + crlf
+ crlf
+ tweet + crlf
+ separator + crlf
+ fileHeader + crlf
+ 'Content-Type: image/jpeg' + crlf
+ crlf;
var multipartBody = Buffer.concat([
new Buffer(contents),
data,
new Buffer(footer)]);
var hostname = 'upload.twitter.com';
var authorization = oauth.authHeader(
'https://upload.twitter.com/1/statuses/update_with_media.json',
accessToken, tokenSecret, 'POST');
var headers = {
'Authorization': authorization,
'Content-Type': 'multipart/form-data; boundary=' + boundary,
'Host': hostname,
'Content-Length': multipartBody.length,
'Connection': 'Keep-Alive'
};
var options = {
host: hostname,
port: 443,
path: '/1/statuses/update_with_media.json',
method: 'POST',
headers: headers
};
var request = https.request(options);
request.write(multipartBody);
request.end();
request.on('error', function (err) {
console.log('Error: Something is wrong.\n'+JSON.stringify(err)+'\n');
});
request.on('response', function (response) {
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log(chunk.toString());
});
response.on('end', function () {
console.log(response.statusCode +'\n');
});
});

Related

HTTP file upload with node.js is changing the file

I have a problem uploading an jpg file with node.js to my tomcat server - somewhere in the upload the file content is changed. I think, it is something with the encoding, but I hav no idea what I'm missing.
The node.js code is:
var agentOptions;
var agent;
agentOptions = {
host: 'this.is.my.server'
, port: '8443'
, path: '/'
, rejectUnauthorized: false
};
agent = new https.Agent(agentOptions);
var binaryFilename = "image.jpg";
var fContent = fs.readFileSync( binaryFilename );
console.log( "file size = " + fContent.length );
var boundary = '69b2c2b9c464731d'
var content = "--"+boundary+"\r\n"
+ "Content-Disposition: form-data; name=\"file\"; filename=\"image.jpg\"\r\n"
+ "Content-Type: application/octet-stream\r\n"
+ "Content-Transfer-Encoding: BINARY\r\n"
+ "\r\n"
+ fContent + "\r\n"
+ "--"+boundary+"--\r\n"
postOptions = {
headers: {
'Content-Type': 'multipart/form-data; boundary='+boundary ,
'Content-Length': Buffer.byteLength(content) ,
'Authorization': 'Basic ABCEDFE..'
} ,
host: 'this.is.my.server' ,
port: '8443' ,
path: '/restservices/uploadimage?data=value&data2=value2' ,
method: 'POST' ,
strictSSL: false ,
agent: agent
} ;
// Set up the request
post_req = https.request(
postOptions,
function(res) {
//
res.setEncoding('utf8') ;
res.on('data', function (chunk) {
console.log('Response: ' + chunk) ;
}) ;
}) ;
// post the data
post_req.write(content) ;
post_req.end() ;
Java code on the server starts with
#RequestMapping(value = "/uploadimage", method = RequestMethod.POST )
public #ResponseBody String uploadpreview(#RequestParam String data, #RequestParam String data1, #RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
....
Start of the image before upload:
Start of the image after upload:
(source: dataplan.de)
Every byte with the highest bit set is converted to three bytes EF BF BD
I tried several things, maybe I'm missing something simple - at the moment I have no idea what I'm doing wrong.
Thanks for any ideas and tipps
Klaus
Here's another attempt:
const https = require('https');
const formData = require('form-data')();
var binaryFilename = process.argv[2] || 'image.jpg';
let request = https.request({
host: 'this.is.my.server'
port: '8443',
path: '/',
method: 'POST',
headers: formData.getHeaders()
}, (res) => {
res.on('data', (data) => {
console.log('Data received: ', data.toString());
});
});
request.on("error", (e) => {
console.error(e);
});
formData.append('image_file', require("fs").createReadStream(binaryFilename));
formData.pipe(request);

How to encode zip files as part of multipart/form-data

tldr I'm having encoding issues when sending zip files as part of a multipart/form-data request body. Help please :/
I'm working on a bot that should be able to upload zip files to Slack (via their file api), but I'm running into some issues that I believe are related to encoding.
So, I'm creating my request body as follows:
var form_string = "\n--abcdefghijklmnop\nContent-Disposition: form-data; filename=\"" + filename + "\"; name=\"file\";\nContent-Type:application/octet-stream;\nContent-Transfer-Encoding:base64;\n\n" + data;
form_string += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"token\";\n\n" + token;
form_string += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"filetype\";\n\n" + filetype;
form_string += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"filename\";\n\n" + filename;
form_string += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"channels\";\n\n" + channel;
form_string += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"title\";\n\n" + title;
form_string += "\n--abcdefghijklmnop--";
var form = Buffer.from(form_string, "utf8");
var headers = {
"Content-Type": "multipart/form-data; boundary=abcdefghijklmnop",
"Content-Length": form.length,
"Authorization": "Bearer ....."
};
var options = {
"headers": headers,
"body": form
};
// using the sync-request node module.
var res = request("POST", url, options);
var res = request("POST", url, options);
(I've tried application/zip and application/x-zip-compressed as well. I've also tried both binary and base64 content transfer encodings.)
(And in case you're wondering, I need to make synchronous http requests...)
I created a really small zip file as a test. The base64 encoding of it is below:
UEsDBAoAAAAAAAqR+UoAAAAAAAAAAAAAAAAIABwAdGlueXppcC9VVAkAA1PBd1mDwXdZdXgLAAEE9QEAAAQUAAAAUEsDBAoAAAAAAAuR+Up6em/tAwAAAAMAAAAQABwAdGlueXppcC90aW55LnR4dFVUCQADVsF3WVzBd1l1eAsAAQT1AQAABBQAAABoaQpQSwECHgMKAAAAAAAKkflKAAAAAAAAAAAAAAAACAAYAAAAAAAAABAA7UEAAAAAdGlueXppcC9VVAUAA1PBd1l1eAsAAQT1AQAABBQAAABQSwECHgMKAAAAAAALkflKenpv7QMAAAADAAAAEAAYAAAAAAABAAAApIFCAAAAdGlueXppcC90aW55LnR4dFVUBQADVsF3WXV4CwABBPUBAAAEFAAAAFBLBQYAAAAAAgACAKQAAACPAAAAAAA=
What I'm getting from Slack seems to be similar to the original... maybe...
UEsDBAoAAAAAAArCkcO5SgAAAAAAAAAAAAAAAAgAHAB0aW55emlwL1VUCQADU8OBd1nCg8OBd1l1eAsAAQTDtQEAAAQUAAAAUEsDBAoAAAAAAAvCkcO5Snp6b8OtAwAAAAMAAAAQABwAdGlueXppcC90aW55LnR4dFVUCQADVsOBd1lcw4F3WXV4CwABBMO1AQAABBQAAABoaQpQSwECHgMKAAAAAAAKwpHDuUoAAAAAAAAAAAAAAAAIABgAAAAAAAAAEADDrUEAAAAAdGlueXppcC9VVAUAA1PDgXdZdXgLAAEEw7UBAAAEFAAAAFBLAQIeAwoAAAAAAAvCkcO5Snp6b8OtAwAAAAMAAAAQABgAAAAAAAEAAADCpMKBQgAAAHRpbnl6aXAvdGlueS50eHRVVAUAA1bDgXdZdXgLAAEEw7UBAAAEFAAAAFBLBQYAAAAAAgACAMKkAAAAwo8AAAAAAA==
Could someone explain what encoding is going on here and how I can correctly upload a file to Slack? Thanks!
How about following sample scripts? There are 2 patterns for this situation.
Sample script 1 :
For this, I modified the method you are trying. You can upload the zip file by converting to the byte array as follows. At first, it builds form-data. It adds the zip file converted to byte array and boundary using Buffer.concat(). This is used as body in request.
var fs = require('fs');
var request = require('request');
var upfile = 'sample.zip';
fs.readFile(upfile, function(err, content){
if(err){
console.error(err);
}
var token = '### access token ###';
var filetype = 'zip';
var filename = 'samplefilename';
var channel = 'sample';
var title = 'sampletitle';
var formString = "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"token\";\n\n" + token;
formString += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"filetype\";\n\n" + filetype;
formString += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"filename\";\n\n" + filename;
formString += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"channels\";\n\n" + channel;
formString += "\n--abcdefghijklmnop\nContent-Disposition: form-data; name=\"title\";\n\n" + title;
formString += "\n--abcdefghijklmnop\nContent-Disposition: form-data; filename=\"" + upfile + "\"; name=\"file\";\nContent-Type:application/octet-stream;\n\n";
var options = {
method: 'post',
url: 'https://slack.com/api/files.upload',
headers: {"Content-Type": "multipart/form-data; boundary=abcdefghijklmnop"},
body: Buffer.concat([
Buffer.from(formString, "utf8"),
new Buffer(content, 'binary'),
Buffer.from("\n--abcdefghijklmnop\n", "utf8"),
]),
};
request(options, function(error, response, body) {
console.log(body);
});
});
Sample script 2 :
This is a simpler way than sample 1. You can use fs.createReadStream() as a file for uploading to Slack.
var fs = require('fs');
var request = require('request');
request.post({
url: 'https://slack.com/api/files.upload',
formData: {
file: fs.createReadStream('sample.zip'),
token: '### access token ###',
filetype: 'zip',
filename: 'samplefilename',
channels: 'sample',
title: 'sampletitle',
},
}, function(error, response, body) {
console.log(body);
});
Result :
Both sample 1 and sample 2 can be uploaded zip file to Slack as follows. For both, even if filetype is not defined, the uploaded file is used automatically as a zip file.
If I misunderstand your question, I'm sorry.

How to upload assets to a github release from node.js

I am trying to automatically post some assets on a Github release I programmatically create.
Here is my upload function:
function uploadFile(fileName, uploadUrl, callback){
var uploadEndPoint = url.parse(uploadUrl.substring(0,uploadUrl.indexOf('{')));
options.host = uploadEndPoint.hostname;
options.path = uploadEndPoint.pathname+'?name=' + fileName;
options.method = 'POST';
options.headers['content-type'] = 'application/zip';
var uploadRequest = https.request(options, callback);
uploadRequest.on('error', function(e) {
console.log('release.js - problem with uploadRequest request: ' + e.message);
});
var readStream = fs.ReadStream(path.resolve(__dirname,'builds',fileName));
readStream.pipe(uploadRequest);
readStream.on('close', function () {
req.end();
console.log('release.js - ' + fileName + ' sent to the server');
});
}
At the end of this I get a 404 not found
I tried auth from token and user/password
I checked the url
I though it might be because of SNI, but I don't know how to check that.
Any clue ? Thanks !
I found a solution to that problem by NOT using the low level node.js modules and using instead restler which is a library that handles SNI.
Here is how is used it:
rest = require('restler'),
path = require('path'),
fs = require('fs');
fs.stat(path.resolve(__dirname,'builds',fileName), function(err, stats){
rest.post(uploadEndPoint.href+'?name=' + fileName, {
multipart: true,
username: GITHUB_OAUTH_TOKEN,
password: '',
data: rest.file(path.resolve(__dirname,'builds',fileName), null, stats.size, null, 'application/zip')
}).on('complete', callback);
});
Hope that will help someone :)
EDIT on 27/02/2015: We recently switched from restler to request.
var
request = require('request'),
fs = require('fs');
var stats = fs.statSync(filePath);
var options = {
url: upload_url.replace('{?name}', ''),
port: 443,
auth: {
pass: 'x-oauth-basic',
user: GITHUB_OAUTH_TOKEN
},
json:true,
headers: {
'User-Agent': 'Release-Agent',
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/zip',
'Content-Length': stats.size
},
qs: {
name: assetName
}
};
// Better as a stream
fs.createReadStream(filePath).pipe(request.post(options, function(err, res){
// Do whatever you will like with the result
}));
The upload_uri can be retrieved through a get request on an existing release or in the response directly after the release creation.

Foursquare Check-in Reply

I am trying to Connect my application to foursquare and I want to display a message when a user checks in to certain places. I am trying to use their real time api https://developer.foursquare.com/overview/realtime
Everything works fine until the very end, ( when I have to send a reply post request https://developer.foursquare.com/docs/checkins/reply) I am using express and node.js. Here is what my post request looks like.
app.post('/handlepush', function(req, res) {
var checkin_id =req.param('checkin');
console.log(checkin_id);
var obj = JSON.parse(checkin_id);
var id = obj.id;
res.end('It worked!');
var token = "********************************";
var post_data = querystring.stringify({text : "awesome"});
var options = {
host: 'api.foursquare.com',
path: '/v2/checkins/' + id + '/reply?oauth_token=' + token,
port: 443,
method: 'POST'
};
var req2 = https.request(options, function(res2) {
res2.setEncoding('utf8');
res2.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
req2.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
});
req2.write(post_data);
req2.end();
});
this is the error I get, for some reason I am not able to add parameters for my post:
BODY: {"meta":{"code":400,"errorType":"other","errorDetail":"Must provide parameter text"},"response":{}}
You need to actually send your request. See: How to make an HTTP POST request in node.js?
var req2 = http.request(options, function(res2) {
res2.setEncoding('utf8');
res2.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req2.end();

node.js paypal https request

first question here, so apologies if it turns out to be something very obvious
I am trying to call the paypal adaptive payments api via node.js and am getting a 580001 invalid request error. I can make a successfull call via curl with the below message and headers, but not through node.
any help would be much appreciated.
var API_endpoint = "svcs.sandbox.paypal.com";
var API_user = '';
var API_pass = '';
var API_sig = '';
message='requestEnvelope.errorLanguage=en_US&actionType=PAY&senderEmail=test_1320882990_per#gmail.com&receiverList.receiver(0).email=test2_1320887729_biz#gmail.com& receiverList.receiver(0).amount=100.00&currencyCode=USD&cancelUrl=http://your_cancel_url& returnUrl=http://your_return_url'
//var params = qs.parse(message);
//params = qs.stringify(params);
var req_options = {
host: API_endpoint,
method: 'POST',
path: '/AdaptivePayments/Pay',
headers: {
'Host': API_endpoint,
'Content-Type': 'application/x-www-form-urlencoded',
//'Content-Type': 'text/namevalue',
'Content-Length': message.length,
'X-PAYPAL-REQUEST-DATA-FORMAT:':'NV',
'X-PAYPAL-RESPONSE-DATA-FORMAT':'NV',
'X-PAYPAL-SECURITY-USERID':API_user,
'X-PAYPAL-SECURITY-PASSWORD':API_pass,
'X-PAYPAL-SECURITY-SIGNATURE':API_sig,
'X-PAYPAL-APPLICATION-ID':'APP-80W284485P519543T'
}
}
fs.readFile('/home/dev/.ssh/sandbox-paypal-private.pem', 'ascii', function(err, key){
fs.readFile('/home/dev/.ssh/sandbox-paypal-public.pem', 'ascii', function(err, cert){
req_options.key=key
req_options.cert=cert
var req = https.request(req_options, function(res){
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.on('data', function(d){
var response = d.toString();
console.log(response)
});
});
req.write(message);
req.end();
req.on('error', function request_error(e) {
console.log(e);
});
});
});
I'm not sure if it's only a copy paste problem, but your message variable does not seem to contain properly formatted string. It has white spaces and the special characters are not encoded.
requestEnvelope.errorLanguage=en_US&actionType=PAY&senderEmail=test_1320882990_per#gmail.com&receiverList.receiver(0).email=test2_1320887729_biz#gmail.com&receiverList.receiver(0).amount=100.00&currencyCode=USD&cancelUrl=http://your_cancel_url& returnUrl=http://your_return_url'.
It should look like this:
requestEnvelope.errorLanguage=en_US&actionType=PAY&senderEmail=test_1320882990_per%40gmail.com&receiverList.receiver(0).email=test2_1320887729_biz%40gmail.com& receiverList.receiver(0).amount=100.00&currencyCode=USD&cancelUrl=http%3A%2F%2Fyour_cancel_url&returnUrl=http%3A%2F%2Fyour_return_url
There's a trailing colon in one of your header fields; Rather than:
'X-PAYPAL-REQUEST-DATA-FORMAT:'
You should have:
'X-PAYPAL-REQUEST-DATA-FORMAT'

Resources