diff --git a/docs/open_graph.png b/docs/open_graph.png new file mode 100644 index 0000000..d7899e2 Binary files /dev/null and b/docs/open_graph.png differ diff --git a/lib/rubotnik.rb b/lib/rubotnik.rb index 3d732b2..c9f73bb 100644 --- a/lib/rubotnik.rb +++ b/lib/rubotnik.rb @@ -6,8 +6,11 @@ require 'rubotnik/cli' require 'rubotnik/generator' require 'rubotnik/autoloader' +require 'ui/base_ui_element' +require 'ui/common/has_buttons' require 'ui/fb_button_template' require 'ui/fb_carousel' +require 'ui/fb_open_graph_template' require 'ui/image_attachment' require 'ui/quick_replies' require 'sinatra' diff --git a/lib/ui/base_ui_element.rb b/lib/ui/base_ui_element.rb new file mode 100644 index 0000000..7f1293a --- /dev/null +++ b/lib/ui/base_ui_element.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# rubocop:disable Metrics/MethodLength + +module UI + class BaseUiElement + # Sends the valid JSON to Messenger API + def send(user) + formed = build(user) + Bot.deliver(formed, access_token: ENV['ACCESS_TOKEN']) + end + + # Use this method to return a valid hash and save it for later + def build(user) + @template[:recipient][:id] = user.id + @template + end + end +end diff --git a/lib/ui/common/has_buttons.rb b/lib/ui/common/has_buttons.rb new file mode 100644 index 0000000..125dfef --- /dev/null +++ b/lib/ui/common/has_buttons.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module UI + module Common + module HasButtons + private + + def parse_buttons(buttons) + return [] if buttons.nil? || buttons.empty? + buttons.map do |button| + button[:type] = button[:type].to_s + button + end + end + end + end +end diff --git a/lib/ui/fb_button_template.rb b/lib/ui/fb_button_template.rb index 598fce5..7dea0db 100644 --- a/lib/ui/fb_button_template.rb +++ b/lib/ui/fb_button_template.rb @@ -3,7 +3,7 @@ module UI ########################### BUTTON TEMPLATE ############################# # https://developers.facebook.com/docs/messenger-platform/send-api-reference/button-template - class FBButtonTemplate + class FBButtonTemplate < UI::BaseUiElement def initialize(text, buttons) @template = { recipient: { @@ -21,27 +21,5 @@ def initialize(text, buttons) } } end - - # Sends the valid JSON to Messenger API - def send(user) - formed = build(user) - Bot.deliver(formed, access_token: ENV['ACCESS_TOKEN']) - end - - # Use this method to return a valid hash and save it for later - def build(user) - @template[:recipient][:id] = user.id - @template - end - - private - - def parse_buttons(buttons) - return [] if buttons.nil? || buttons.empty? - buttons.map do |button| - button[:type] = button[:type].to_s - button - end - end end end diff --git a/lib/ui/fb_carousel.rb b/lib/ui/fb_carousel.rb index 49ba2ab..8491cb0 100644 --- a/lib/ui/fb_carousel.rb +++ b/lib/ui/fb_carousel.rb @@ -2,7 +2,9 @@ module UI ################## GENERIC TEMPLATE (aka CAROUSEL) ####################### # https://developers.facebook.com/docs/messenger-platform/send-api-reference/generic-template - class FBCarousel + class FBCarousel < UI::BaseUiElement + include Common::HasButtons + def initialize(elements) @template = { recipient: { id: nil }, @@ -19,18 +21,6 @@ def initialize(elements) } end - # Sends the valid JSON to Messenger API - def send(user) - template = build(user) - Bot.deliver(template, access_token: ENV['ACCESS_TOKEN']) - end - - # Use this method to return a valid hash and save it for later - def build(user) - @template[:recipient][:id] = user.id - @template - end - # set image aspect ratio to 'square' def square_images @template[:message][:attachment][:payload][:image_aspect_ratio] = 'square' @@ -53,13 +43,5 @@ def parse_elements(elements) elt end end - - def parse_buttons(buttons) - return [] if buttons.nil? || buttons.empty? - buttons.map do |button| - button[:type] = button[:type].to_s - button - end - end end end diff --git a/lib/ui/fb_open_graph_template.rb b/lib/ui/fb_open_graph_template.rb new file mode 100644 index 0000000..ca810ae --- /dev/null +++ b/lib/ui/fb_open_graph_template.rb @@ -0,0 +1,42 @@ +# rubocop:disable Metrics/MethodLength + +module UI + ########################### OPEN GRAPH TEMPLATE ############################# + # https://developers.facebook.com/docs/messenger-platform/send-messages/template/open-graph + class FBOpenGraphTemplate < UI::BaseUiElement + include UI::Common::HasButtons + + def initialize(url, buttons = []) + @url = url + @buttons = buttons + + @template = { + recipient: { + id: nil + }, + message: { + attachment: { + type: 'template', + payload: { + template_type: 'open_graph', + elements: elements + } + } + } + } + end + + private + + attr_reader :url, :buttons + + def elements + res = { url: url } + + buttons_payload = parse_buttons(buttons) + res[:buttons] = buttons_payload if buttons_payload.any? + + [res] + end + end +end diff --git a/lib/ui/image_attachment.rb b/lib/ui/image_attachment.rb index 9880158..fa7126b 100644 --- a/lib/ui/image_attachment.rb +++ b/lib/ui/image_attachment.rb @@ -1,7 +1,7 @@ # rubocop:disable Metrics/MethodLength module UI # https://developers.facebook.com/docs/messenger-platform/send-api-reference/image-attachment - class ImageAttachment + class ImageAttachment < UI::BaseUiElement def initialize(url) @template = { recipient: { @@ -17,15 +17,5 @@ def initialize(url) } } end - - def send(user) - formed = build(user) - Bot.deliver(formed, access_token: ENV['ACCESS_TOKEN']) - end - - def build(user) - @template[:recipient][:id] = user.id - @template - end end end diff --git a/rubotnik.gemspec b/rubotnik.gemspec index 6c3a2f4..f01e003 100644 --- a/rubotnik.gemspec +++ b/rubotnik.gemspec @@ -30,4 +30,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", "~> 1.15" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "factory_bot", "~> 4.0" + spec.add_development_dependency "pry" end diff --git a/spec/factories/user.rb b/spec/factories/user.rb new file mode 100644 index 0000000..77f8a57 --- /dev/null +++ b/spec/factories/user.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :user, class: User do + skip_create + initialize_with { new(attributes[:id].to_s) } + + sequence(:id) { |n| n } + + trait :with_commands do + transient do + commands { [] } + end + + after(:build) do |user, evaluator| + evaluator.commands.each { |c| user.assign_command(c) } + end + end + end +end diff --git a/spec/rubotnik_spec.rb b/spec/rubotnik_spec.rb index d166bf3..701021a 100644 --- a/spec/rubotnik_spec.rb +++ b/spec/rubotnik_spec.rb @@ -4,8 +4,4 @@ it "has a version number" do expect(Rubotnik::VERSION).not_to be nil end - - it "does something useful" do - expect(false).to eq(true) - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c655e32..944816a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ -require "bundler/setup" -require "rubotnik" +require 'bundler/setup' +require 'rubotnik' +require 'factory_bot' +require 'pry' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure @@ -8,4 +10,10 @@ config.expect_with :rspec do |c| c.syntax = :expect end + + config.include FactoryBot::Syntax::Methods + + config.before(:suite) do + FactoryBot.find_definitions + end end diff --git a/spec/ui/fb_open_graph_template_spec.rb b/spec/ui/fb_open_graph_template_spec.rb new file mode 100644 index 0000000..1daca71 --- /dev/null +++ b/spec/ui/fb_open_graph_template_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe UI::FBOpenGraphTemplate do + let(:url) { 'https://github.com/progapandist/rubotnik' } + let(:user) { FactoryBot.create(:user) } + + describe 'build' do + let(:result) { described_class.new(url).build(user) } + let(:elements) { result[:message][:attachment][:payload][:elements].first } + let(:recipient_id) { result[:recipient][:id] } + + it 'returns correct template' do + expect(recipient_id).to eq(user.id) + expect(elements.length).to eq(1) + expect(elements).to eq(url: url) + end + + context 'when buttons passed' do + let(:buttons) do + [ + { + type: 'web_url', + url: url, + title: 'abc' + } + ] + end + let(:result) { described_class.new(url, buttons).build(user) } + + it 'adds buttons to template' do + expect(elements[:buttons].first[:type]).to eq('web_url') + expect(elements[:buttons].first[:title]).to eq('abc') + expect(elements[:buttons].first[:url]).to eq(url) + end + end + end + + describe 'send' do + it 'sends the element' do + expect(Bot).to receive(:deliver) + + described_class.new(url).send(user) + end + end +end