skip to Main Content

I’m building a facebook bot in nodejs with facebook messenger API. I’m trying to send a image from the bot by directly uploading the image file from the heroku server itself (not through URL) and it does not work.

Here is a error log from the console.

Failed calling Send API 400 Bad Request { message: ‘(#100) Incorrect
number of files uploaded. Must upload exactly one file.’,type:
‘OAuthException’, code: 100,error_subcode: 2018005,fbtrace_id:
‘E32ogm/ofxd’ }

The official facebook document only contains an example in curl format and I’dont know how to replicate this curl into node format.

I’ve tested with curl and it worked like a charm.

curl     -F 'recipient={"id":"recipientId"}'    -F 'message={"attachment":{"type":"image", "payload":{}}}'    -F 'filedata=@resource/pdf_img/sample.jpg;type=image/jpeg'    "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"

This is my node implementation that seems to be problematic,

//file_loc = __dirname+"/resource/pdf_img/sample.jpg"
function sendImageMessage(recipientId, file_loc){
    let fs = require('fs');
    var readStream = fs.createReadStream(file_loc);
    var messageData = {
        recipient : {
            id : recipientId
        },
        message : {
            attachment : {
                type : "image",
                payload :{}
            }
        },
        filedata:readStream
    }
    callSendAPI(messageData);
}
function callSendAPI(messageData) {
    request({
        uri: "https://graph.facebook.com/v2.6/me/messages",
        qs: {access_token: process.env.PAGE_ACCESS_TOKEN},
        method: "POST",
        json: messageData
    }, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            var recipientId = body.recipient_id;
            var messageId = body.message_id;

            if (messageId) {
                console.log("Successfully sent message with id %s to recipient %s", 
                    messageId, recipientId);
            } else {
                console.log("Successfully called Send API for recipient %s", 
                    recipientId);
            }
        } else {
            console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error);
        }
    });
} 

Please any help with fixing my node implementation or translating that curl in to node would be appreciated.

2

Answers


  1. I think sending variable messagedata as formdata would solve the problem.

    var FormData = require('form-data');
    var fs = require('fs');
    var https = require('https');
    
    function sendImageMessage(recipientId, file_loc){
     var readStream = fs.createReadStream(file_loc);
     var messageData = new FormData();
     messageData.append('recipient', '{id:' +recipientId+ '}');
     messageData.append('message', '{attachment :{type:"image", payload:{}}}');
     messageData.append('filedata', readStream);
     callSendAPI(messageData);
    }
    

    Secondly you need to change request a bit since now you are using formdata. I have done it using module https and so have changed the callSendAPI() code accordingly. You can find out how to send the formdata using request module.

    function callSendAPI(messageData) {
        var options = {
        method: 'post',
        host: 'graph.facebook.com',
        path: '/v2.6/me/messages?access_token=' + pagetoken,
        headers: messageData.getHeaders()
      };
      var request = https.request(options);
      messageData.pipe(request);
    
      request.on('error', function(error) {
      console.log("Unable to send message to recipient %s", recipientId);
        return;
      });
      request.on('response', function(res) {
        if (res.statusMessage == "OK") {
          console.log("Successfully sent message to recipient %s", recipientId);
        } else {
    
          console.log("Unable to send message to recipient %s", recipientId);
        }
        return;
      }); 
    }
    
    Login or Signup to reply.
  2. You can use forms /form-data/ in the request module (which has integrated module ‘form-data’).
    But all requests needs to be stringified.
    So, based on your example, the following should do the job >>

    function sendImageMessage(recipientId, file_loc){
        let fs = require('fs');
        var readStream = fs.createReadStream(file_loc);
        var messageData = {
            recipient : {
                id : recipientId
            },
            message : {
                attachment : {
                    type : "image",
                    payload :{}
                }
            },
            filedata:readStream
        }
        callSendAPI(messageData);
    }
    
    function callSendAPI(messageData) {
        var endpoint = "https://graph.facebook.com/v2.6/me/messages?access_token=" + process.env.PAGE_ACCESS_TOKEN;
        var r = request.post(endpoint, function(err, httpResponse, body) {
            if (err) {return console.error("upload failed >> n", err)};
            console.log("upload successfull >> n", body); //facebook always return 'ok' message, so you need to read error in 'body.error' if any
        });
        var form = r.form();
        form.append('recipient', JSON.stringify(messageData.recipient));
        form.append('message', JSON.stringify(messageData.message));
        form.append('filedata', messageData.filedata); //no need to stringify!
    }
    

    Details here https://github.com/request/request#forms

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search