Messenger bot using Rails: setup for multiple pages
Asked Answered
I

1

8

I want to create a Messenger Bot used by different users for their Facebook pages. I created a rails app and use the facebook-messenger gem.

I successfully created the bot and it works when I do the set up for one page. Now, I follow the instructions to make my bot live on multiple Facebook Pages (see "Make a configuration provider" section).

I'm new to rails and I'm not sure where to put the class ExampleProvider? I put it in my config/application.rb file:

require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module BotletterApp
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    config.paths.add File.join('app', 'bot'), glob: File.join('**', '*.rb')
    config.autoload_paths += Dir[Rails.root.join('app', 'bot', '*')]
  end

  class BotProvider < Facebook::Messenger::Configuration::Providers::Base
    def valid_verify_token?(verify_token)
      bot.exists?(verify_token: verify_token)
    end

    def app_secret_for()
      ENV['APP_SECRET']
    end

    def access_token_for(page_id)
      bot.find_by(user_id: page_id).page_access_token
    end

    private

    def bot
      MyApp::Bot
    end
  end

  Facebook::Messenger.configure do |config|
    config.provider = BotProvider.new
  end
end

Then I have my app/bot/setup.rb file to create the bot. I don't know how to use the provider I created in place of the ENV variables?

require "facebook/messenger"

include Facebook::Messenger

Facebook::Messenger::Subscriptions.subscribe(access_token: ENV["ACCESS_TOKEN"])

Facebook::Messenger::Profile.set({
  get_started: {
    payload: 'GET_STARTED_PAYLOAD'
  }
}, access_token: ENV['ACCESS_TOKEN'])

I searched in the Rails documentation how to make it work but unfortunately could not find anything.

UPDATE:

Now I'm able to set up the bots for different pages. Unfortunately, the following line of my ConfigProvider is getting an error:

config/initializers/config_provider.rb

def bot
    Rails.application.class.parent::Bot
  end
I'm getting the following error:

NameError (uninitialized constant BotletterApp::Bot):

config/initializers/config_provider.rb:17:in bot' config/initializers/config_provider.rb:7:inapp_secret_for'

Do you know how should I name my app?

My BotModule:

module BotletterApp
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    config.paths.add File.join('app', 'listen'), glob: File.join('**', '*.rb')
    config.autoload_paths += Dir[Rails.root.join('app', 'listen', '*')]
  end
end

UPDATE, it works with ::Application, here is the new file:

class ConfigProvider < Facebook::Messenger::Configuration::Providers::Base
  def valid_verify_token?(verify_token)
    ENV['VERIFY_TOKEN']
  end

  def app_secret_for(page_id)
    ENV['APP_SECRET']
  end

  def access_token_for(page_id)
    CoreBot.find_by_page_id(page_id).page_access_token
  end

  private

  def bot
    BotletterApp::Application
  end
end

Facebook::Messenger.configure do |config|
  config.provider = ConfigProvider.new
end

The problem is I get the following error unless my db query seems working (it works in the rails console):

ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: page_id.id: SELECT "core_bots".* FROM "core_bots" WHERE "page_id"."id" = ? LIMIT ?):

Intelligent answered 2/6, 2017 at 13:51 Comment(11)
Just wondering: why are you using Rails to run this gem. Your app will be much lighter if you skip the Rails part and just use plain ruby + rack (as suggested in the gem's README.md). Typically you'd place a class like BotProvider in lib/bot_provider.rb, though.Fibrillation
I'm using a rails app because I need to build a dashboard to help my users manage the settings of the bot, sign up etc. It's easier than doing it in NodeJs or plain rubyIntelligent
Ok, then it makes sense :) Have you already made a model that contains the codes? rails g model bot access_token app_secret verify_token page_id:integer ... the MyApp::Bot is expected to be something like an ActiveRecord::Model, which implements exists? and find_byFibrillation
Yes, I already created the database and the models.Intelligent
And you placed the BotProvider in lib? what errors are you getting?Fibrillation
Don't try to make it overly generic, just write: BotletterApp::Bot instead of Rails.application ... but that's not the problem. It can't find the class 'Bot' in the module BotletterApp... try if plain Bot works. btw. if you've found an answer to your initial question, you can post it below.Fibrillation
What do you mean by "try if plain Bot works"?Intelligent
By the way, I added the module BotletterApp, indeed there is no bot class in it...Intelligent
It works with ::Application, I updated the questionIntelligent
Regarding 'plain'... Instead of def bot BotletterApp::Application end use def bot Bot endFibrillation
It seems it's working! Thanks. But I still get the error mentioned at the bottom of the updated question. That's weird, my db query works well in the Rails console...Intelligent
F
1

Moving to an answer for improved readability ;)

Regarding 'plain'... Instead of

def bot
  BotletterApp::Application
end

use

def bot
  Bot
end

or (it looks like you named your model containing all pages CoreBot(?) (assuming you have a typical ActiveRecord model in /app/models/core_bot.rb, I was assuming Bot)

def bot
  CoreBot
end

Then you should be able to use the template code from the README.md

As for your latest problem: it seems like the access_token_for-method gets called with a hash, searching with something like {id: 1}. You might want to check where that value is coming from. I would suggest to take a few steps back, and stay closer to the template code.

Fibrillation answered 13/6, 2017 at 8:21 Comment(1)
It works well! The right way to write the method was "CoreBot.find_by_page_id(page_id['id']).page_access_token"Intelligent

© 2022 - 2024 — McMap. All rights reserved.