bobot 2.6.2 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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