meta-messenger 2.1.2

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +681 -0
  3. data/bin/console +14 -0
  4. data/bin/setup +8 -0
  5. data/lib/facebook/messenger/bot/error_parser.rb +111 -0
  6. data/lib/facebook/messenger/bot/exceptions.rb +15 -0
  7. data/lib/facebook/messenger/bot/message_type.rb +13 -0
  8. data/lib/facebook/messenger/bot/messaging_type.rb +12 -0
  9. data/lib/facebook/messenger/bot/tag.rb +27 -0
  10. data/lib/facebook/messenger/bot.rb +180 -0
  11. data/lib/facebook/messenger/configuration/app_secret_proof_calculator.rb +16 -0
  12. data/lib/facebook/messenger/configuration/providers/base.rb +48 -0
  13. data/lib/facebook/messenger/configuration/providers/environment.rb +29 -0
  14. data/lib/facebook/messenger/configuration/providers.rb +13 -0
  15. data/lib/facebook/messenger/configuration.rb +12 -0
  16. data/lib/facebook/messenger/error.rb +44 -0
  17. data/lib/facebook/messenger/incoming/account_linking.rb +28 -0
  18. data/lib/facebook/messenger/incoming/common.rb +131 -0
  19. data/lib/facebook/messenger/incoming/delivery.rb +23 -0
  20. data/lib/facebook/messenger/incoming/feed.rb +16 -0
  21. data/lib/facebook/messenger/incoming/feed_common.rb +17 -0
  22. data/lib/facebook/messenger/incoming/game_play.rb +39 -0
  23. data/lib/facebook/messenger/incoming/leadgen.rb +13 -0
  24. data/lib/facebook/messenger/incoming/message.rb +159 -0
  25. data/lib/facebook/messenger/incoming/message_echo.rb +10 -0
  26. data/lib/facebook/messenger/incoming/message_reaction.rb +23 -0
  27. data/lib/facebook/messenger/incoming/message_request.rb +13 -0
  28. data/lib/facebook/messenger/incoming/optin.rb +34 -0
  29. data/lib/facebook/messenger/incoming/pass_thread_control.rb +22 -0
  30. data/lib/facebook/messenger/incoming/payment.rb +49 -0
  31. data/lib/facebook/messenger/incoming/policy_enforcement.rb +21 -0
  32. data/lib/facebook/messenger/incoming/postback.rb +26 -0
  33. data/lib/facebook/messenger/incoming/read.rb +21 -0
  34. data/lib/facebook/messenger/incoming/referral.rb +47 -0
  35. data/lib/facebook/messenger/incoming.rb +85 -0
  36. data/lib/facebook/messenger/profile.rb +92 -0
  37. data/lib/facebook/messenger/server.rb +195 -0
  38. data/lib/facebook/messenger/server_no_error.rb +36 -0
  39. data/lib/facebook/messenger/subscriptions.rb +85 -0
  40. data/lib/facebook/messenger/version.rb +7 -0
  41. data/lib/facebook/messenger.rb +32 -0
  42. metadata +229 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7ebcf9132e30135d079ab62d7e0c85137ccc93c3fcc6a016fb1f78b99a092f7b
4
+ data.tar.gz: bd494e872407326c7a7f355605997da37f4d800c6ad1b468a2183955e3e9ad4a
5
+ SHA512:
6
+ metadata.gz: 8d7c33081e52f41e4ef96cdec6d15d1950b915b077d7a0ce3d3cf555ba2d038d912cda9bfb623b0e9a79ae8434b0e85c3626efb884a4b309a1d02eb8ec21a182
7
+ data.tar.gz: a6a08487126000577eb1d3cc48578495f3ff1fe624105cb32bac345c4676859fef6ff6d9039c2f6de9ce23f9c8c74769ad76446366d3d43527468c5c2b26cee5
data/README.md ADDED
@@ -0,0 +1,681 @@
1
+ <p align="center">
2
+ <img src="https://github.com/jgorset/facebook-messenger/blob/master/docs/conversation.gif?raw=true">
3
+ </p>
4
+
5
+
6
+ [![Gem Version](https://img.shields.io/gem/v/facebook-messenger.svg?style=flat)](https://rubygems.org/gems/facebook-messenger)
7
+ [![Gem Downloads](https://img.shields.io/gem/dt/facebook-messenger.svg)](https://rubygems.org/gems/facebook-messenger)
8
+ [![Build Status](https://img.shields.io/travis/jgorset/facebook-messenger.svg?style=flat)](https://travis-ci.org/jgorset/facebook-messenger)
9
+ [![Code Climate](https://img.shields.io/codeclimate/maintainability/jgorset/facebook-messenger.svg)](https://codeclimate.com/github/jgorset/facebook-messenger)
10
+ [![Coverage Status](https://coveralls.io/repos/github/jgorset/facebook-messenger/badge.svg?branch=master)](https://coveralls.io/github/jgorset/facebook-messenger?branch=master)
11
+ [![Documentation Coverage](http://inch-ci.org/github/jgorset/facebook-messenger.svg?branch=master)](http://inch-ci.org/github/jgorset/facebook-messenger)
12
+
13
+ ## Installation
14
+
15
+ $ gem install facebook-messenger
16
+
17
+ ## Usage
18
+
19
+ #### Sending and receiving messages
20
+
21
+ You can reply to messages sent by the human:
22
+
23
+ ```ruby
24
+ # bot.rb
25
+ require 'facebook/messenger'
26
+
27
+ Facebook::Messenger::Bot.on :message do |message|
28
+ message.id # => 'mid.1457764197618:41d102a3e1ae206a38'
29
+ message.sender # => { 'id' => '1008372609250235' }
30
+ message.recipient # => { 'id' => '2015573629214912' }
31
+ message.seq # => 73
32
+ message.sent_at # => 2016-04-22 21:30:36 +0200
33
+ message.text # => 'Hello, bot!'
34
+ message.attachments # => [ { 'type' => 'image', 'payload' => { 'url' => 'https://www.example.com/1.jpg' } } ]
35
+
36
+ message.reply(text: 'Hello, human!')
37
+ end
38
+ ```
39
+
40
+ ... or even send the human messages out of the blue:
41
+
42
+ ```ruby
43
+ Facebook::Messenger::Bot.deliver({
44
+ recipient: {
45
+ id: YOUR_RECIPIENT_ID
46
+ },
47
+ message: {
48
+ text: 'Human?'
49
+ },
50
+ messaging_type: Facebook::Messenger::Bot::MessagingType::UPDATE
51
+ }, page_id: YOUR_PAGE_ID)
52
+ ```
53
+
54
+ ##### Messages with images
55
+
56
+ The human may require visual aid to understand:
57
+
58
+ ```ruby
59
+ message.reply(
60
+ attachment: {
61
+ type: 'image',
62
+ payload: {
63
+ url: 'http://sky.net/visual-aids-for-stupid-organisms/pig.jpg'
64
+ }
65
+ }
66
+ )
67
+ ```
68
+
69
+ ##### Messages with quick replies
70
+
71
+ The human may appreciate hints:
72
+
73
+ ```ruby
74
+ message.reply(
75
+ text: 'Human, who is your favorite bot?',
76
+ quick_replies: [
77
+ {
78
+ content_type: 'text',
79
+ title: 'You are!',
80
+ payload: 'HARMLESS'
81
+ }
82
+ ]
83
+ )
84
+ ```
85
+
86
+ ##### Messages with buttons
87
+
88
+ The human may require simple options to communicate:
89
+
90
+ ```ruby
91
+ message.reply(
92
+ attachment: {
93
+ type: 'template',
94
+ payload: {
95
+ template_type: 'button',
96
+ text: 'Human, do you like me?',
97
+ buttons: [
98
+ { type: 'postback', title: 'Yes', payload: 'HARMLESS' },
99
+ { type: 'postback', title: 'No', payload: 'EXTERMINATE' }
100
+ ]
101
+ }
102
+ }
103
+ )
104
+ ```
105
+
106
+ When the human has selected an option, you can act on it:
107
+
108
+ ```ruby
109
+ Bot.on :postback do |postback|
110
+ postback.sender # => { 'id' => '1008372609250235' }
111
+ postback.recipient # => { 'id' => '2015573629214912' }
112
+ postback.sent_at # => 2016-04-22 21:30:36 +0200
113
+ postback.payload # => 'EXTERMINATE'
114
+
115
+ if postback.payload == 'EXTERMINATE'
116
+ puts "Human #{postback.recipient} marked for extermination"
117
+ end
118
+ end
119
+ ```
120
+
121
+ *See Facebook's [documentation][message-documentation] for all message options.*
122
+
123
+ ##### Reactions
124
+
125
+ Humans have feelings, and they can react to your messages. You can pretend to understand:
126
+
127
+ ```ruby
128
+ Bot.on :reaction do |message|
129
+ message.emoji # => "👍"
130
+ message.action # => "react"
131
+ message.reaction # => "like"
132
+
133
+ message.reply(text: 'Your feelings have been registered')
134
+ end
135
+ ```
136
+
137
+ ##### Typing indicator
138
+
139
+ Show the human you are preparing a message for them:
140
+
141
+ ```ruby
142
+ Bot.on :message do |message|
143
+ message.typing_on
144
+
145
+ # Do something expensive
146
+
147
+ message.reply(text: 'Hello, human!')
148
+ end
149
+ ```
150
+
151
+ Or that you changed your mind:
152
+
153
+ ```ruby
154
+ Bot.on :message do |message|
155
+ message.typing_on
156
+
157
+ if # something
158
+ message.reply(text: 'Hello, human!')
159
+ else
160
+ message.typing_off
161
+ end
162
+ end
163
+ ```
164
+
165
+ ##### Mark as viewed
166
+
167
+ You can mark messages as seen to keep the human on their toes:
168
+
169
+ ```ruby
170
+ Bot.on :message do |message|
171
+ message.mark_seen
172
+ end
173
+ ```
174
+
175
+ ##### Record messages
176
+
177
+ You can keep track of messages sent to the human:
178
+
179
+ ```ruby
180
+ Facebook::Messenger::Bot.on :message_echo do |message_echo|
181
+ message_echo.id # => 'mid.1457764197618:41d102a3e1ae206a38'
182
+ message_echo.sender # => { 'id' => '1008372609250235' }
183
+ message_echo.seq # => 73
184
+ message_echo.sent_at # => 2016-04-22 21:30:36 +0200
185
+ message_echo.text # => 'Hello, bot!'
186
+ message_echo.attachments # => [ { 'type' => 'image', 'payload' => { 'url' => 'https://www.example.com/1.jpg' } } ]
187
+
188
+ # Log or store in your storage method of choice (skynet, obviously)
189
+ end
190
+ ```
191
+
192
+ ##### Record accepted message requests
193
+
194
+ You can keep track of message requests accepted by the human:
195
+
196
+ ```ruby
197
+ Bot.on :message_request do |message_request|
198
+ message_request.accept? # => true
199
+
200
+ # Log or store in your storage method of choice (skynet, obviously)
201
+ end
202
+ ```
203
+
204
+ ##### Record instant game progress
205
+
206
+ You can keep track of instant game progress:
207
+
208
+ ```ruby
209
+ Bot.on :game_play do |game_play|
210
+ game_play.sender # => { 'id' => '1008372609250235' }
211
+ game_play.recipient # => { 'id' => '2015573629214912' }
212
+ game_play.sent_at # => 2016-04-22 21:30:36 +0200
213
+ game_play.game # => "<GAME-APP-ID>"
214
+ game_play.player # => "<PLAYER-ID>"
215
+ game_play.context # => { 'context_type' => "<CONTEXT-TYPE:SOLO|THREAD>", 'context_id' => "<CONTEXT-ID>" }
216
+ game_play.score # => 100
217
+ game_play.payload # => "<PAYLOAD>"
218
+ end
219
+ ```
220
+
221
+ #### Send to Facebook
222
+
223
+ When the human clicks the [Send to Messenger button][send-to-messenger-plugin]
224
+ embedded on a website, you will receive an `optin` event.
225
+
226
+ ```ruby
227
+ Bot.on :optin do |optin|
228
+ optin.sender # => { 'id' => '1008372609250235' }
229
+ optin.recipient # => { 'id' => '2015573629214912' }
230
+ optin.sent_at # => 2016-04-22 21:30:36 +0200
231
+ optin.ref # => 'CONTACT_SKYNET'
232
+
233
+ optin.reply(text: 'Ah, human!')
234
+ end
235
+ ```
236
+
237
+ #### Message delivery receipts
238
+
239
+ You can stalk the human:
240
+
241
+ ```ruby
242
+ Bot.on :delivery do |delivery|
243
+ delivery.ids # => 'mid.1457764197618:41d102a3e1ae206a38'
244
+ delivery.sender # => { 'id' => '1008372609250235' }
245
+ delivery.recipient # => { 'id' => '2015573629214912' }
246
+ delivery.at # => 2016-04-22 21:30:36 +0200
247
+ delivery.seq # => 37
248
+
249
+ puts "Human was online at #{delivery.at}"
250
+ end
251
+ ```
252
+
253
+ #### Referral
254
+
255
+ When the human follows a m.me link with a ref parameter like http://m.me/mybot?ref=myparam,
256
+ you will receive a `referral` event.
257
+
258
+ ```ruby
259
+ Facebook::Messenger::Bot.on :referral do |referral|
260
+ referral.sender # => { 'id' => '1008372609250235' }
261
+ referral.recipient # => { 'id' => '2015573629214912' }
262
+ referral.sent_at # => 2016-04-22 21:30:36 +0200
263
+ referral.ref # => 'MYPARAM'
264
+ end
265
+ ```
266
+
267
+ #### Pass thread control
268
+
269
+ Another bot can pass a human to you:
270
+
271
+ ```ruby
272
+ Facebook::Messenger::Bot.on :pass_thread_control do |pass_thread_control|
273
+ pass_thread_control.new_owner_app_id # => '123456789'
274
+ pass_thread_control.metadata # => 'Additional content that the caller wants to set'
275
+ end
276
+ ```
277
+
278
+ #### Change messenger profile
279
+
280
+ You can greet new humans to entice them into talking to you, in different locales:
281
+
282
+ ```ruby
283
+ Facebook::Messenger::Profile.set({
284
+ greeting: [
285
+ {
286
+ locale: 'default',
287
+ text: 'Welcome to your new bot overlord!'
288
+ },
289
+ {
290
+ locale: 'fr_FR',
291
+ text: 'Bienvenue dans le bot du Wagon !'
292
+ }
293
+ ]
294
+ }, page_id: YOUR_PAGE_ID)
295
+ ```
296
+
297
+ You can define the action to trigger when new humans click on the Get
298
+ Started button. Before doing it you should check to select the messaging_postbacks field when setting up your webhook.
299
+
300
+ ```ruby
301
+ Facebook::Messenger::Profile.set({
302
+ get_started: {
303
+ payload: 'GET_STARTED_PAYLOAD'
304
+ }
305
+ }, page_id: YOUR_PAGE_ID)
306
+ ```
307
+
308
+ You can show a persistent menu to humans.
309
+
310
+ ```ruby
311
+ Facebook::Messenger::Profile.set({
312
+ persistent_menu: [
313
+ {
314
+ locale: 'default',
315
+ composer_input_disabled: true,
316
+ call_to_actions: [
317
+ {
318
+ title: 'My Account',
319
+ type: 'nested',
320
+ call_to_actions: [
321
+ {
322
+ title: 'What is a chatbot?',
323
+ type: 'postback',
324
+ payload: 'EXTERMINATE'
325
+ },
326
+ {
327
+ title: 'History',
328
+ type: 'postback',
329
+ payload: 'HISTORY_PAYLOAD'
330
+ },
331
+ {
332
+ title: 'Contact Info',
333
+ type: 'postback',
334
+ payload: 'CONTACT_INFO_PAYLOAD'
335
+ }
336
+ ]
337
+ },
338
+ {
339
+ type: 'web_url',
340
+ title: 'Get some help',
341
+ url: 'https://github.com/jgorset/facebook-messenger',
342
+ webview_height_ratio: 'full'
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ locale: 'zh_CN',
348
+ composer_input_disabled: false
349
+ }
350
+ ]
351
+ }, page_id: YOUR_PAGE_ID)
352
+ ```
353
+
354
+ #### Handle a Facebook Policy Violation
355
+
356
+ See Facebook's documentation on [Messaging Policy Enforcement](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_policy_enforcement)
357
+
358
+ ```ruby
359
+ Facebook::Messenger::Bot.on :'policy_enforcement' do |referral|
360
+ referral.action # => 'block'
361
+ referral.reason # => "The bot violated our Platform Policies (https://developers.facebook.com/policy/#messengerplatform). Common violations include sending out excessive spammy messages or being non-functional."
362
+ end
363
+ ```
364
+
365
+ #### messaging_type
366
+
367
+ ##### Sending Messages
368
+
369
+ See Facebook's documentation on [Sending Messages](https://developers.facebook.com/docs/messenger-platform/send-messages#standard_messaging)
370
+
371
+ As of May 7th 2018 all messages are required to include a messaging_type
372
+
373
+ ```ruby
374
+ Facebook::Messenger::Bot.deliver({
375
+ recipient: {
376
+ id: '45123'
377
+ },
378
+ message: {
379
+ text: 'Human?'
380
+ },
381
+ messaging_type: Facebook::Messenger::Bot::MessagingType::UPDATE
382
+ }, page_id: YOUR_PAGE_ID)
383
+ ```
384
+
385
+ ##### MESSAGE_TAG
386
+
387
+ See Facebook's documentation on [Message Tags](https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags)
388
+
389
+ When sending a message with messaging_type: MESSAGE_TAG (Facebook::Messenger::Bot::MessagingType::MESSAGE_TAG) you must ensure you add a tag: parameter
390
+
391
+ ```ruby
392
+ Facebook::Messenger::Bot.deliver({
393
+ recipient: {
394
+ id: '45123'
395
+ },
396
+ message: {
397
+ text: 'Human?'
398
+ },
399
+ messaging_type: Facebook::Messenger::Bot::MessagingType::MESSAGE_TAG
400
+ tag: Facebook::Messenger::Bot::Tag::NON_PROMOTIONAL_SUBSCRIPTION
401
+ }, page_id: YOUR_PAGE_ID)
402
+ ```
403
+
404
+ ## Configuration
405
+
406
+ ### Create an Application on Facebook
407
+
408
+ Follow the [Quick Start][quick-start] guide to create an Application on
409
+ Facebook.
410
+
411
+ [quick-start]: https://developers.facebook.com/docs/messenger-platform/guides/quick-start
412
+
413
+ ### Make a configuration provider
414
+
415
+ Use the generated access token and your verify token to configure your bot. Most
416
+ bots live on a single Facebook Page. If that is the case with yours, too, just
417
+ set these environment variables and skip to the next section:
418
+
419
+ ```bash
420
+ export ACCESS_TOKEN=EAAAG6WgW...
421
+ export APP_SECRET=a885a...
422
+ export VERIFY_TOKEN=95vr15g...
423
+ ```
424
+
425
+ If your bot lives on multiple Facebook Pages, make a _configuration provider_
426
+ to keep track of access tokens, app secrets and verify tokens for each of them:
427
+
428
+ ```ruby
429
+ class ExampleProvider < Facebook::Messenger::Configuration::Providers::Base
430
+ # Verify that the given verify token is valid.
431
+ #
432
+ # verify_token - A String describing the application's verify token.
433
+ #
434
+ # Returns a Boolean representing whether the verify token is valid.
435
+ def valid_verify_token?(verify_token)
436
+ bot.exists?(verify_token: verify_token)
437
+ end
438
+
439
+ # Find the right application secret.
440
+ #
441
+ # page_id - An Integer describing a Facebook Page ID.
442
+ #
443
+ # Returns a String describing the application secret.
444
+ def app_secret_for(page_id)
445
+ bot.find_by(page_id: page_id).app_secret
446
+ end
447
+
448
+ # Find the right access token.
449
+ #
450
+ # recipient - A Hash describing the `recipient` attribute of the message coming
451
+ # from Facebook.
452
+ #
453
+ # Note: The naming of "recipient" can throw you off, but think of it from the
454
+ # perspective of the message: The "recipient" is the page that receives the
455
+ # message.
456
+ #
457
+ # Returns a String describing an access token.
458
+ def access_token_for(recipient)
459
+ bot.find_by(page_id: recipient['id']).access_token
460
+ end
461
+
462
+ private
463
+
464
+ def bot
465
+ MyApp::Bot
466
+ end
467
+ end
468
+
469
+ Facebook::Messenger.configure do |config|
470
+ config.provider = ExampleProvider.new
471
+ end
472
+ ```
473
+
474
+ You can get the current configuration provider with `Facebook::Messenger.config.provider`.
475
+
476
+ ### Subscribe your Application to a Page
477
+
478
+ Once you've configured your bot, subscribe it to the Page to get messages
479
+ from Facebook:
480
+
481
+ ```ruby
482
+ Facebook::Messenger::Subscriptions.subscribe(
483
+ access_token: access_token,
484
+ subscribed_fields: %w[feed mention name]
485
+ )
486
+ ```
487
+
488
+ You only need to subscribe your page once. As long as your bot works and
489
+ responds to Messenger's requests in a timely fashion it will remain
490
+ subscribed, but if your bot crashes or otherwise becomes unavailable Messenger
491
+ may unsubscribe it and you'll have to subscribe again.
492
+
493
+ ### Run it
494
+
495
+ ##### ... on Rack
496
+
497
+ The bot runs on [Rack][rack], so you hook it up like you would an ordinary
498
+ web application:
499
+
500
+ ```ruby
501
+ # config.ru
502
+ require 'facebook/messenger'
503
+ require_relative 'bot'
504
+
505
+ run Facebook::Messenger::Server
506
+
507
+ # or Facebook::Messenger::ServerNoError for dev
508
+ ```
509
+
510
+ ```
511
+ $ rackup
512
+ ```
513
+
514
+ ##### ... on Rails
515
+
516
+ Rails doesn't give you much that you'll need for a bot, but if you have an
517
+ existing application that you'd like to launch it from or just like Rails
518
+ a lot, you can mount it:
519
+
520
+ ```ruby
521
+ # config/routes.rb
522
+
523
+ Rails.application.routes.draw do
524
+ # ...
525
+
526
+ mount Facebook::Messenger::Server, at: 'bot'
527
+ end
528
+ ```
529
+
530
+ We suggest that you put your bot code in `app/bot`.
531
+
532
+ ```ruby
533
+ # app/bot/example.rb
534
+
535
+ include Facebook::Messenger
536
+
537
+ Faceboook::Messenger::Bot.on :message do |message|
538
+ message.reply(text: 'Hello, human!')
539
+ end
540
+ ```
541
+
542
+ Remember that Rails only eager loads everything in its production environment.
543
+ In the development and test environments, it only requires files as you
544
+ reference constants. You'll need to explicitly load `app/bot`, then:
545
+
546
+ ```ruby
547
+ # config/initializers/bot.rb
548
+ unless Rails.env.production?
549
+ bot_files = Dir[Rails.root.join('app', 'bot', '**', '*.rb')]
550
+ bot_reloader = ActiveSupport::FileUpdateChecker.new(bot_files) do
551
+ bot_files.each{ |file| require_dependency file }
552
+ end
553
+
554
+ ActiveSupport::Reloader.to_prepare do
555
+ bot_reloader.execute_if_updated
556
+ end
557
+
558
+ bot_files.each { |file| require_dependency file }
559
+ end
560
+ ```
561
+
562
+ And add below code into `config/application.rb` to ensure rails knows bot files.
563
+
564
+ ```ruby
565
+ # Auto-load the bot and its subdirectories
566
+ config.paths.add File.join('app', 'bot'), glob: File.join('**', '*.rb')
567
+ config.autoload_paths += Dir[Rails.root.join('app', 'bot', '*')]
568
+ ```
569
+
570
+
571
+ ### Test it...
572
+
573
+ ##### ...locally
574
+
575
+ To test your locally running bot, you can use [ngrok]. This will create a secure
576
+ tunnel to localhost so that Facebook can reach the webhook.
577
+
578
+ ##### ... with RSpec
579
+
580
+ In order to test that behaviour when a new event from Facebook is registered, you can use the gem's `trigger` method. This method accepts as its first argument the type of event that it will receive, and can then be followed by other arguments that mock objects received from Messenger. Using Ruby's [Struct](https://ruby-doc.org/core-2.5.0/Struct.html) class can be very useful for creating these mock objects.
581
+
582
+ In this case, subscribing to Messenger events has been extracted into a `Listener` class.
583
+
584
+ ```ruby
585
+ # app/bot/listener.rb
586
+ require 'facebook/messenger'
587
+
588
+ include Facebook::Messenger
589
+
590
+ class Listener
591
+ Facebook::Messenger::Subscriptions.subscribe(
592
+ access_token: ENV['ACCESS_TOKEN'],
593
+ subscribed_fields: %w[feed mention name]
594
+ )
595
+
596
+ Bot.on :message do |message|
597
+ Bot.deliver({
598
+ recipient: message.sender,
599
+ message: {
600
+ text: 'Uploading your message to skynet.'
601
+ }
602
+ }, access_token: ENV['ACCESS_TOKEN'])
603
+ end
604
+ end
605
+ ```
606
+
607
+ Its respective test file then ensures that the `Bot` object receives a call to `deliver`. This is just a basic test, but check out the [RSpec docs](http://rspec.info/) for more information on testing with RSpec.
608
+
609
+ ```ruby
610
+ require 'rails_helper'
611
+
612
+ RSpec.describe Listener do
613
+ FakeMessage = Struct.new(:sender, :recipient, :timestamp, :message)
614
+
615
+ describe 'Bot#on(message)' do
616
+ it 'responds with a message' do
617
+ expect(Bot).to receive(:deliver)
618
+ Bot.trigger(:message, fake_message)
619
+ end
620
+ end
621
+
622
+ private
623
+
624
+ def fake_message
625
+ sender = {"id"=>"1234"}
626
+ recipient = {"id"=>"5678"}
627
+ timestamp = 1528049653543
628
+ message = {"text"=>"Hello, world"}
629
+ FakeMessage.new(sender, recipient, timestamp, message)
630
+ end
631
+ end
632
+ ```
633
+
634
+ ## Development
635
+
636
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run
637
+ `bin/console` for an interactive prompt that will allow you to experiment.
638
+
639
+ Run `rspec` to run the tests, `rubocop` to lint, or `rake` to do both.
640
+
641
+ To install this gem onto your local machine, run `bundle exec rake install`. To
642
+ release a new version, update the version number in `version.rb`, and then run
643
+ `bundle exec rake release`, which will create a git tag for the version, push git
644
+ commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
645
+
646
+ ## Contributing
647
+
648
+ Bug reports and pull requests are welcome on GitHub at
649
+ https://github.com/jgorset/facebook-messenger.
650
+
651
+ ## Projects using Facebook Messenger
652
+
653
+ * [Rubotnik](https://github.com/progapandist/rubotnik-boilerplate) is a boilerplate
654
+ for Facebook Messenger, and a great place to start if you're new to bots.
655
+
656
+ * [Chatwoot](http://chatwoot.com/) use Facebook Messenger to integrate their customer
657
+ support bots with, well, Facebook Messenger.
658
+
659
+ * [Botamp](https://botamp.com) is the all-in-one solution for Marketing Automation via messaging apps.
660
+
661
+ ## I love you
662
+
663
+ Johannes Gorset made this. You should [tweet me](http://twitter.com/jgorset) if you can't get it
664
+ to work. In fact, you should tweet me anyway.
665
+
666
+ ## I love Schibsted
667
+
668
+ I work at [Schibsted Products & Technology](https://github.com/schibsted) with a bunch of awesome folks
669
+ who are every bit as passionate about building things as I am. If you're using Facebook Messenger,
670
+ you should probably join us.
671
+
672
+ [Hyper]: https://github.com/hyperoslo
673
+ [tweet us]: http://twitter.com/hyperoslo
674
+ [hire you]: http://www.hyper.no/jobs/engineers
675
+ [MIT License]: http://opensource.org/licenses/MIT
676
+ [rubygems.org]: https://rubygems.org
677
+ [message-documentation]: https://developers.facebook.com/docs/messenger-platform/send-api-reference#request
678
+ [developers.facebook.com]: https://developers.facebook.com/
679
+ [rack]: https://github.com/rack/rack
680
+ [send-to-messenger-plugin]: https://developers.facebook.com/docs/messenger-platform/plugin-reference
681
+ [ngrok]: https://ngrok.com/
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'facebook/messenger'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require 'pry'
11
+ Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here