Search code examples
ruby-on-railsfacebook-messengernet-httpfacebook-messenger-bot

POST to Facebook Messenger with Ruby Net::HTTP error 100


I'm trying to make a direct POST request to Facebook, from my model (user.rb), to send a simple message to this user in Messenger. But I get this error:

{"error":{"message":"(#100) param recipient must be non-empty.","type":"OAuthException","code":100,"fbtrace_id":"F07BNxzgciW"}} 

Here is my method:

def send_message(msg)
    url = "https://graph.facebook.com/v2.6/me/messages?access_token="+ENV['PAGE_ACCESS_TOKEN']
    data = {
      'recipient': {
        'id': self.facebook_id
      },
      'message': {
        'text': msg
      }
    }
    #data = JSON.pretty_generate(data)
    puts data
    uri = URI(url)
    res = Net::HTTP.post_form(uri, data)
    puts res.body
end

Seems like data is not in the right format, so the user id is not beeing recognized.

Uncommenting the #data line I get this error, because data is a string, and not a Hash:

NoMethodError: undefined method `map' for #<String:0x000000034bd248>

This is the request example from Facebook Docs that I'm trying to reproduce:

curl -X POST -H "Content-Type: application/json" -d '{
  "recipient": {
    "id": "USER_ID"
  },
  "message": {
    "text": "hello, world!"
  }
}' "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"

Solution

  • Try it with this:

    Ruby/Rails:

    uri = URI("https://graph.facebook.com/v2.6/me/messages?access_token="+ENV['PAGE_ACCESS_TOKEN'])
    req = Net::HTTP::Post.new(uri)
    req["Accept"] = "application/json"
    req["Content-Type"] = "application/json"
    req.body = {
      'recipient': {
        'id': self.facebook_id
       },
       'message': {
         'text': msg
       }
     }.to_json
    
     res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
       http.request(req)
     end
     puts res.body
    

    This can be refactored to a function and then be called whenever you need to. This function is a bit more advanced than what needed for the previous request

    def http_post(url, payload='', headers={}, auth=false) #basic auth is passed as an array of ['user', 'password']
        uri = URI(url)
        req = Net::HTTP::Post.new(uri)
        headers.each do |k,v|
          req[k.to_s] = v
        end
        req.body = payload.is_a?(Hash) ? payload.to_json : payload.to_s
        req.basic_auth(*auth) if auth
        res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
          log_this(req.inspect)
          http.request(req)
        end
        return res
      end
    
    # you can now use like:
    url = "https://graph.facebook.com/v2.6/me/messages?access_token="+ENV['PAGE_ACCESS_TOKEN']
    headers = {
      "Accept": "application/json"
      "Content-Type": "application/json"
    }
    payload = {
      'recipient': {
        'id': self.facebook_id
      },
      'message': {
        'text': msg
      }
    }
    response = http_post(url, payload, options)
    

    With curl you can do the same request with:

    curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"recipient\": { \"id\": \"substitute_with_user_id\" }, \"message\": { \"text\": \"Your message goes here\" } }" https://graph.facebook.com/v2.6/me/messages?access_token=substitute_with_your_access_token
    

    Usually you don't need to escape ( "\" ) the double quotes, instead you can just use single quotes ( ' ) as delimiter of the json body because curl lib will do it for you in most OS where its used, but on windows it seems you need to, and effectively, it will end up being escaped under the hood so that's the correct form I guess.