bobot 2.6.2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2654330704a5e64d8bae289b267366d1a911c75f
4
- data.tar.gz: b055bbed851a6dfea750281e87ec3af87d04f9db
3
+ metadata.gz: 640a621bd564127ef1480e17a4c2359d177503b6
4
+ data.tar.gz: 66e4fa77192b46535e5329652ea6a7e75d68d1cf
5
5
  SHA512:
6
- metadata.gz: d130c709d22e2b97a4ea0d226de55134f10bbe9636e2c6c72a43b6880cc81473109b2d8ded675a9ccbd0d793f8c0828e7d3586630edbf96c6e3fb6f14e95093b
7
- data.tar.gz: c2cd9e1dd269e41df7fe8021581392382a68e9b260fd952f59ee8c147d697a5e4472d1a1fe8e2a6ef34f0988d1f9eb8b99bd5d7f3ea3d877bad77192a4115119
6
+ metadata.gz: 0954163b016234b5efd7c5344281a81a2c6c873574db790be3e33a5abb85d51696c9124baa03838681e859294780d0c559d17e0f183b19f928f3bfeb130b0d0a
7
+ data.tar.gz: 1f7592dadac8b0125648a01c98b7bf3d9789a3e8bbacbb53c450109810ee5f96589662ad2280518ced9611a0b200a14fa79afb7bcbd1dd4bd43d4fbe43d09692
data/README.md CHANGED
@@ -1,76 +1,152 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/navidemad/bobot/master/assets/images/bobot-logo.png"/>
3
- </p>
4
-
5
- # Bobot [Gem under development !][![Build Status](https://travis-ci.org/navidemad/bobot.svg?branch=master)](https://travis-ci.org/navidemad/bobot) [![Gem Version](https://img.shields.io/gem/v/bobot.svg?style=flat)](https://rubygems.org/gems/bobot)
6
- `Bobot` is a Ruby wrapped framework to build easily a Facebook Messenger Bot.
7
-
8
- ## Installation
9
- Add this line to your application's Gemfile:
1
+ <img src="https://raw.githubusercontent.com/navidemad/bobot/master/assets/images/bobot-logo.png" width="300" height="205" />
2
+
3
+ [![Build Status](https://travis-ci.org/navidemad/bobot.svg?branch=master)](https://travis-ci.org/navidemad/bobot) [![Gem Version](https://img.shields.io/gem/v/bobot.svg?style=flat)](https://rubygems.org/gems/bobot)
4
+
5
+ > Bobot is a Ruby wrapped framework to build easily a Facebook Messenger Bot.</b>
10
6
 
11
7
  ```ruby
12
8
  gem 'bobot'
13
9
  ```
14
10
 
15
- And then execute:
16
-
17
- $ bundle
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install bobot
11
+ <details>
12
+ <summary>First steps to setup</summary>
13
+ <p>
14
+
15
+ Run the command to install basic files: rails g bobot:install
16
+ Then, add `bobot` section into `secrets.yml`:
17
+
18
+ development:
19
+ bobot:
20
+ app_id: "123"
21
+ app_secret: "456"
22
+ verify_token: "your token"
23
+ domains: "whitelisted-domain.com,second-whitelisted-domain.com"
24
+ async: false
25
+ pages:
26
+ - slug: "facebook_1"
27
+ language: "fr"
28
+ page_id: "789"
29
+ page_access_token: "abc"
30
+ get_started_payload: "get_started"
31
+
32
+ Now to can edit the workflow of your bot with the file:
33
+ - app/bobot/workflow.rb
34
+
35
+ </p>
36
+ </details>
37
+
38
+ <details>
39
+ <summary>Webhook url</summary>
40
+ <p>
41
+
42
+ Facebook wants an url where he can send me information to communicate with my server.
43
+
44
+ When you installed Bobot, a line has been added to your config/routes.rb
45
+
46
+ mount Bobot::Engine => "/XXXXXX", as: "bobot"
47
+
48
+ You have to setup as url on the webhook facebook interface:
49
+ - https://domain.ltd/XXXXXX/facebook
50
+
51
+ And as :verify_token, the one you set on your config/secrets.yml
52
+ </p>
53
+ </details>
54
+
55
+ <details>
56
+ <summary>Persistent Menu, Greeting Text, Whitelist domains, Get Started</summary>
57
+ <p>
58
+
59
+ After having define into your `config/application.rb` your I18n.available_locales.
60
+
61
+ Then, persistent menu and the greeting text will catch the content of them from `locales/bobot.{locale}.yml`
62
+ - config/locales/bobot.{locale}.yml
63
+
64
+ The whitelist domains and get_started button settings have to be set in:
65
+ - config/secrets.yml
66
+ </p>
67
+ </details>
68
+
69
+ <details>
70
+ <summary>Find a page</summary>
71
+ <p>
72
+
73
+ You can access to page settings:
74
+ - `page = Bobot::Page.find(facebook_page_id)`
75
+ - `page = Bobot::Page.find_by_slug(facebook_page_slug)`
76
+ - `page = Bobot::Page[facebook_page_id]`
77
+ - `page = Bobot::Page[facebook_page_slug]`
78
+
79
+ After fetching the page with command above, you have access to:
80
+ - `page.update_facebook_setup!`
81
+
82
+ Or one by one in a Rails console:
83
+ - `page.subscribe_to_facebook_page!`
84
+ - `page.unsubscribe_to_facebook_page!`
85
+ - `page.unset_greeting_text!`
86
+ - `page.set_greeting_text!`
87
+ - `page.unset_whitelist_domains!`
88
+ - `page.set_whitelist_domains!`
89
+ - `page.unset_get_started_button!`
90
+ - `page.set_get_started_button!`
91
+ - `page.unset_persistent_menu!`
92
+ - `page.set_persistent_menu!`
93
+ </p>
94
+ </details>
95
+
96
+ <details>
97
+ <summary>Page methods: </summary>
98
+ <p>
99
+
100
+ The parameter :to is the facebook uid of the target.
101
+ - page.sender_action(sender_action:, to: nil)
102
+ - page.show_typing(state:, to: nil)
103
+ - page.mark_as_seen(to: nil)
104
+ - page.send(payload_message:, to: nil)
105
+ - page.send_text(text:, to: nil)
106
+ - page.send_attachment(url:, type:, to: nil)
107
+ - page.send_image(url:, to: nil)
108
+ - page.send_audio(url:, to: nil)
109
+ - page.send_video(url:, to: nil)
110
+ - page.send_file(url:, to: nil)
111
+ - page.send_quick_replies(text:, quick_replies:, to: nil)
112
+ - page.send_buttons(text:, buttons:, to: nil)
113
+ - page.send_generic(elements:, image_aspect_ratio: 'square', to: nil)
114
+ - page.send_carousel(elements:, image_aspect_ratio: 'square', to: nil)
115
+ </p>
116
+ </details>
117
+
118
+ <details>
119
+ <summary>Event methods: </summary>
120
+ <p>
121
+
122
+ The event is the parameter that you receive in your block when you are hooking an event on your workflow.rb
123
+ - event.sender_action(sender_action:)
124
+ - event.show_typing(state:)
125
+ - event.mark_as_seen
126
+ - event.reply(payload_message:)
127
+ - event.reply_with_text(text:)
128
+ - event.reply_with_attachment(url:, type:)
129
+ - event.reply_with_image(url:)
130
+ - event.reply_with_audio(url:)
131
+ - event.reply_with_video(url:)
132
+ - event.reply_with_file(url:)
133
+ - event.reply_with_quick_replies(text:, quick_replies:)
134
+ - event.reply_with_buttons(text:, buttons:)
135
+ - event.reply_with_generic(elements:, image_aspect_ratio: 'square')
136
+ - event.reply_with_carousel(elements:, image_aspect_ratio: 'square')
137
+ </p>
138
+ </details>
139
+
140
+ -----
141
+
142
+ > You can find more informations on the workflow : [BOBOT_WORKFLOW](BOBOT_WORKFLOW.md)
22
143
 
23
144
  ## Requirement
24
- `bobot` has only requirement to have at least `ruby 2.3.1`.
25
-
26
- ## Setup
27
-
28
- Typing the following command in your Rails application will add the bot to your app.
29
- `rails g bobot:install`
145
+ `ruby >= 2.3.1`
30
146
 
31
- `config/bobot.yml` contains your bot keys
32
- `app/bobot/workflow.rb` contains the workflow of your bot
33
-
34
- You can access to page settings:
35
- ```
36
- Bobot.config.find_page_by_id(facebook_page_id)
37
- Bobot.config.find_page_by_slug(facebook_page_slug)
38
- ```
39
-
40
- After fetching the page with command above, you have access to:
41
- `update_facebook_setup!`
42
-
43
- Or one by one in a Rails console:
44
- - `subscribe_to_facebook_page!`
45
- - `unsubscribe_to_facebook_page!`
46
- - `unset_greeting_text!`
47
- - `set_greeting_text!`
48
- - `unset_whitelist_domains!`
49
- - `set_whitelist_domains!`
50
- - `unset_get_started_button!`
51
- - `set_get_started_button!`
52
- - `unset_persistent_menu!`
53
- - `set_persistent_menu!`
54
-
55
- Greeting Text and Persistent Menus are translated by I18n.
56
- You have to define into your `config/application.rb` your available_locales as I18n defined them.
57
- Then, Bobot when you will catch the content of them from `locales/bobot.{locale}.yml`
58
-
59
- ## Usage
60
- See example [Workflow](BOBOT_WORKFLOW.md)
61
147
  ## Wiki
62
148
  The [Messenger Platform - Facebook for Developers](https://developers.facebook.com/docs/messenger-platform) is available and provides full documentation for the API section.
63
149
  All informations related to webhook [Messenger Platform - Webhook](https://developers.facebook.com/docs/messenger-platform/webhook-reference).
64
150
 
65
- ## Contributing
66
- Bug reports and pull requests are welcome on GitHub at https://github.com/navidemad/bobot.
67
- This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
68
-
69
151
  ## License
70
- The gem is available as open source under the terms of the [MIT License](MIT-LICENSE).
71
-
72
- ## Code of Conduct
73
- Everyone interacting in the Bobot project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/navidemad/bobot/blob/master/CODE_OF_CONDUCT.md).
74
-
75
- ## Contributing
76
- Please refer to [CONTRIBUTING.md](CONTRIBUTING.md).
152
+ The gem is available as open source under the terms of the [MIT License](MIT-LICENSE).
@@ -1,5 +1,4 @@
1
1
  module Bobot
2
2
  class ApplicationController < ActionController::API
3
- # protect_from_forgery with: :exception
4
3
  end
5
4
  end
@@ -2,12 +2,6 @@ module Bobot
2
2
  class WebhookController < Bobot::ApplicationController
3
3
  class BadRequestError < Error; end
4
4
 
5
- include ActionView::Helpers::TextHelper
6
-
7
- skip_before_action :verify_authenticity_token, raise: false
8
-
9
- layout :bobot_layout rescue nil
10
-
11
5
  X_HUB_SIGNATURE_MISSING_WARNING = <<-HEREDOC.freeze
12
6
  The X-Hub-Signature header is not present in the request. This is
13
7
  expected for the first webhook requests. If it continues after
@@ -82,19 +76,11 @@ module Bobot
82
76
  end
83
77
 
84
78
  def trigger(events)
85
- events['entry'.freeze].to_a.inject([]) do |payloads_sent, entry|
79
+ events['entry'.freeze].to_a.each do |entry|
86
80
  entry['messaging'.freeze].to_a.each do |messaging|
87
- begin
88
- payloads_sent << Bobot::Commander.receive(messaging)
89
- rescue => e
90
- payloads_sent << e.to_s
91
- end
81
+ Bobot::Commander.receive(messaging)
92
82
  end
93
83
  end
94
84
  end
95
-
96
- def bobot_layout
97
- 'bobot/application'
98
- end
99
85
  end
100
86
  end
@@ -2,10 +2,10 @@ module Bobot
2
2
  class DeliverJob < ApplicationJob
3
3
  queue_as :default
4
4
 
5
- def perform(sender:, access_token:, payload_template:)
5
+ def perform(target_facebook_uid:, access_token:, payload_template:)
6
6
  Bobot::Commander.deliver(
7
7
  body: {
8
- recipient: sender,
8
+ recipient: { id: target_facebook_uid },
9
9
  }.merge(payload_template),
10
10
  query: {
11
11
  access_token: access_token,
data/lib/bobot.rb CHANGED
@@ -12,6 +12,7 @@ require 'bobot/user'
12
12
  require 'bobot/buttons'
13
13
  require 'bobot/commander'
14
14
  require 'bobot/event'
15
+ require 'bobot/page'
15
16
 
16
17
  module Bobot
17
18
  def self.configure
@@ -33,7 +33,7 @@ module Bobot
33
33
  def receive(payload)
34
34
  event = Bobot::Event.parse(payload)
35
35
  hooks.fetch(Bobot::Event::EVENTS.invert[event.class].to_sym)
36
- puts "[ActiveJob] << Bobot::HookJob with event #{event.class}" if Bobot.config.debug_log
36
+ Rails.logger.debug "[ActiveJob] << Bobot::HookJob with event #{event.class}"
37
37
  # event.mark_as_seen
38
38
  job = Bobot::CommanderJob
39
39
  if Bobot.config.async
@@ -42,19 +42,19 @@ module Bobot
42
42
  job.perform_now(payload: payload)
43
43
  end
44
44
  rescue KeyError
45
- warn "Ignoring #{event.class} (no hook registered)"
45
+ warn "[receive] Ignoring #{event.class} (no hook registered)"
46
46
  end
47
47
 
48
48
  def trigger(payload)
49
49
  event = Bobot::Event.parse(payload)
50
50
  hook = hooks.fetch(Bobot::Event::EVENTS.invert[event.class].to_sym)
51
- puts "[ActiveJob] >> Bobot::HookJob related to event #{name.class}" if Bobot.config.debug_log
51
+ Rails.logger.debug "[ActiveJob] >> Bobot::HookJob related to event #{name.class}"
52
52
  # event.show_typing(state: true)
53
53
  hook.call(event)
54
54
  # event.show_typing(state: false)
55
- [event, event.payloads_sent[1..-2]]
55
+ event
56
56
  rescue KeyError
57
- warn "Ignoring #{event.class} (no hook registered)"
57
+ warn "[trigger] Ignoring #{event.class} (no hook registered)"
58
58
  end
59
59
 
60
60
  def hooks
@@ -1,10 +1,11 @@
1
1
  module Bobot
2
2
  class Configuration
3
- attr_accessor :app_id, :app_secret, :verify_token, :debug_log, :async, :pages
3
+ attr_accessor :app_id, :app_secret, :verify_token, :domains, :async, :pages
4
4
 
5
5
  def domains=(rhs)
6
- return unless rhs.present?
7
- if rhs.respond_to?(:to_str)
6
+ if rhs.nil?
7
+ @domains = nil
8
+ elsif rhs.respond_to?(:to_str)
8
9
  @domains = rhs.split(",").map(&:strip)
9
10
  elsif rhs.is_a?(Array)
10
11
  @domains = rhs
@@ -12,193 +13,5 @@ module Bobot
12
13
  raise Bobot::InvalidParameter.new(:domains, "should be a string or an array")
13
14
  end
14
15
  end
15
-
16
- def domains
17
- @domains
18
- end
19
-
20
- def find_page_by_id(page_id)
21
- pages.find { |page| page.page_id.to_s == page_id.to_s }
22
- end
23
-
24
- def find_page_by_slug(slug)
25
- pages.find { |page| page.slug.to_s == slug.to_s }
26
- end
27
-
28
- class Page
29
- attr_accessor :slug, :language, :page_id, :page_access_token, :get_started_payload
30
-
31
- def initialize(options = {})
32
- self.slug = options[:slug]
33
- self.language = options[:language]
34
- self.page_id = options[:page_id]
35
- self.page_access_token = options[:page_access_token]
36
- self.get_started_payload = options[:get_started_payload]
37
- end
38
-
39
- def update_facebook_setup!
40
- subscribe_to_facebook_page!
41
- set_greeting_text!
42
- set_whitelist_domains!
43
- set_get_started_button!
44
- set_persistent_menu!
45
- end
46
-
47
- ## == Subcribe your bot to your page ==
48
- def subscribe_to_facebook_page!
49
- raise Bobot::InvalidParameter.new(:page_id) unless page_id.present?
50
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
51
- Bobot::Subscription.set(
52
- query: {
53
- page_id: page_id,
54
- access_token: page_access_token,
55
- },
56
- )
57
- end
58
-
59
- ## == Unsubcribe your bot from your page ==
60
- def unsubscribe_to_facebook_page!
61
- raise Bobot::InvalidParameter.new(:page_id) unless page_id.present?
62
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
63
- Bobot::Subscription.unset(
64
- query: {
65
- page_id: page_id,
66
- access_token: page_access_token,
67
- },
68
- )
69
- end
70
-
71
- ## == Set bot description (only displayed on first time). ==
72
- def set_greeting_text!
73
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
74
- greeting_texts = []
75
- if language.nil?
76
- # Default text
77
- greeting_text = I18n.t("bobot.#{slug}.config.greeting_text", locale: I18n.default_locale, default: nil)
78
- greeting_texts << { locale: 'default', text: greeting_text } if greeting_text.present?
79
- # Each languages
80
- I18n.available_locales.each do |locale|
81
- greeting_text = I18n.t("bobot.#{slug}.config.greeting_text", locale: locale, default: nil)
82
- next unless greeting_text.present?
83
- facebook_locales = I18n.t("bobot.#{slug}.config.facebook_locales", locale: locale, default: nil)
84
- facebook_locales.to_a.each do |locale_long|
85
- greeting_texts << { locale: locale_long, text: greeting_text }
86
- end
87
- end
88
- else
89
- greeting_text = I18n.t("bobot.#{slug}.config.greeting_text", locale: language, default: nil)
90
- greeting_texts << { locale: 'default', text: greeting_text } if greeting_text.present?
91
- end
92
- if greeting_texts.present?
93
- Bobot::Profile.set(
94
- body: { greeting: greeting_texts },
95
- query: { access_token: page_access_token },
96
- )
97
- end
98
- end
99
-
100
- def unset_greeting_text!
101
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
102
- Bobot::Profile.unset(
103
- body: { fields: %w[greeting] },
104
- query: { access_token: page_access_token },
105
- )
106
- end
107
-
108
- ## == Set bot whitelist domains (only displayed on first time) ==
109
- ## == Some features like Messenger Extensions and Checkbox Plugin require ==
110
- ## == a page to specify a domain whitelist. ==
111
- def set_whitelist_domains!
112
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
113
- raise Bobot::InvalidParameter.new(:domains) unless Bobot.config.domains.present?
114
- Bobot::Profile.set(
115
- body: { whitelisted_domains: Bobot.config.domains },
116
- query: { access_token: page_access_token },
117
- )
118
- end
119
-
120
- def unset_whitelist_domains!
121
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
122
- Bobot::Profile.unset(
123
- body: { fields: ["whitelisted_domains"] },
124
- query: { access_token: page_access_token },
125
- )
126
- end
127
-
128
- ## == You can define the action to trigger when new humans click on ==
129
- ## == the Get Started button. Before doing it you should check to select the ==
130
- ## == messaging_postbacks field when setting up your webhook. ==
131
- def set_get_started_button!
132
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
133
- raise Bobot::InvalidParameter.new(:get_started_payload) unless get_started_payload.present?
134
- Bobot::Profile.set(
135
- body: { get_started: { payload: get_started_payload } },
136
- query: { access_token: page_access_token },
137
- )
138
- end
139
-
140
- def unset_get_started_button!
141
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
142
- Bobot::Profile.unset(
143
- body: { fields: %w[persistent_menu get_started] },
144
- query: { access_token: page_access_token },
145
- )
146
- end
147
-
148
- ## == You can show a persistent menu to humans. ==
149
- ## == If you want to have a persistent menu, you have to set get_started ==
150
- ## == button before. ==
151
- def set_persistent_menu!
152
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
153
- persistent_menus = []
154
- # Default text
155
- if language.nil?
156
- persistent_menu = I18n.t("bobot.#{slug}.config.persistent_menu", locale: I18n.default_locale, default: nil)
157
- if persistent_menu.present?
158
- persistent_menus << {
159
- locale: 'default',
160
- composer_input_disabled: persistent_menu[:composer_input_disabled],
161
- call_to_actions: persistent_menu[:call_to_actions],
162
- }
163
- end
164
- # Each languages
165
- I18n.available_locales.each do |locale|
166
- persistent_menu = I18n.t("bobot.#{slug}.config.persistent_menu", locale: locale, default: nil)
167
- facebook_locales = I18n.t("bobot.#{slug}.config.facebook_locales", locale: locale, default: nil)
168
- next unless persistent_menu.present?
169
- facebook_locales.to_a.each do |locale_long|
170
- persistent_menus << {
171
- locale: locale_long,
172
- composer_input_disabled: persistent_menu[:composer_input_disabled],
173
- call_to_actions: persistent_menu[:call_to_actions],
174
- }
175
- end
176
- end
177
- else
178
- persistent_menu = I18n.t("bobot.#{slug}.config.persistent_menu", locale: language, default: nil)
179
- if persistent_menu.present?
180
- persistent_menus << {
181
- locale: 'default',
182
- composer_input_disabled: persistent_menu[:composer_input_disabled],
183
- call_to_actions: persistent_menu[:call_to_actions],
184
- }
185
- end
186
- end
187
- if persistent_menus.present?
188
- Bobot::Profile.set(
189
- body: { persistent_menu: persistent_menus },
190
- query: { access_token: page_access_token },
191
- )
192
- end
193
- end
194
-
195
- def unset_persistent_menu!
196
- raise Bobot::InvalidParameter.new(:access_token) unless page_access_token.present?
197
- Bobot::Profile.unset(
198
- body: { fields: ["persistent_menu"] },
199
- query: { access_token: page_access_token },
200
- )
201
- end
202
- end
203
16
  end
204
- end
17
+ end