messenger-bot-ruby 0.1.0
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 +7 -0
- data/.gitignore +36 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +71 -0
- data/LICENSE +21 -0
- data/README.md +152 -0
- data/Rakefile +2 -0
- data/bin/console +6 -0
- data/bin/setup +5 -0
- data/config.ru +3 -0
- data/examples/bot.rb +53 -0
- data/lib/messenger/bot.rb +10 -0
- data/lib/messenger/bot/api.rb +87 -0
- data/lib/messenger/bot/client.rb +66 -0
- data/lib/messenger/bot/exceptions.rb +2 -0
- data/lib/messenger/bot/exceptions/base.rb +7 -0
- data/lib/messenger/bot/exceptions/response_error.rb +32 -0
- data/lib/messenger/bot/null_logger.rb +11 -0
- data/lib/messenger/bot/types.rb +20 -0
- data/lib/messenger/bot/types/address.rb +14 -0
- data/lib/messenger/bot/types/adjustment.rb +10 -0
- data/lib/messenger/bot/types/attachment.rb +10 -0
- data/lib/messenger/bot/types/base.rb +9 -0
- data/lib/messenger/bot/types/button.rb +12 -0
- data/lib/messenger/bot/types/button_template.rb +12 -0
- data/lib/messenger/bot/types/callback.rb +10 -0
- data/lib/messenger/bot/types/delivery.rb +11 -0
- data/lib/messenger/bot/types/element.rb +11 -0
- data/lib/messenger/bot/types/entry.rb +11 -0
- data/lib/messenger/bot/types/generic_template.rb +28 -0
- data/lib/messenger/bot/types/image.rb +12 -0
- data/lib/messenger/bot/types/image_attachment.rb +10 -0
- data/lib/messenger/bot/types/message.rb +14 -0
- data/lib/messenger/bot/types/messaging.rb +14 -0
- data/lib/messenger/bot/types/payload.rb +9 -0
- data/lib/messenger/bot/types/postback.rb +9 -0
- data/lib/messenger/bot/types/receipt_element.rb +14 -0
- data/lib/messenger/bot/types/receipt_template.rb +19 -0
- data/lib/messenger/bot/types/recipient.rb +12 -0
- data/lib/messenger/bot/types/sender.rb +12 -0
- data/lib/messenger/bot/types/summary.rb +12 -0
- data/lib/messenger/bot/types/template_attachment.rb +10 -0
- data/lib/messenger/bot/version.rb +5 -0
- data/messenger-bot-ruby.gemspec +29 -0
- metadata +202 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 5e58554d6934e450aa15901619fbda6209f4daa4
|
|
4
|
+
data.tar.gz: debc4eaa53d54eae3afaaaa30a601b9a03bebf56
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 134cac2692eb0ec98fe15e189a9b1704b2910e39ee94d7827a98ce2d3b2fd203c4bb047dae3a4f3b3119190e68cb2a0514a20519037782bccb14cb08a66a63fe
|
|
7
|
+
data.tar.gz: a939e9cb006ce518aec1d7721559bae14984ec2d8b26f0c7d307aab3c88c8a4f17f0c5886aa4e1fa84a821e6c64398dac58b581774d57cf32d8dd4abe5bbfeee
|
data/.gitignore
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
/.config
|
|
4
|
+
/coverage/
|
|
5
|
+
/InstalledFiles
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/spec/examples.txt
|
|
9
|
+
/test/tmp/
|
|
10
|
+
/test/version_tmp/
|
|
11
|
+
/tmp/
|
|
12
|
+
|
|
13
|
+
## Specific to RubyMotion:
|
|
14
|
+
.dat*
|
|
15
|
+
.repl_history
|
|
16
|
+
build/
|
|
17
|
+
|
|
18
|
+
## Documentation cache and generated files:
|
|
19
|
+
/.yardoc/
|
|
20
|
+
/_yardoc/
|
|
21
|
+
/doc/
|
|
22
|
+
/rdoc/
|
|
23
|
+
|
|
24
|
+
## Environment normalization:
|
|
25
|
+
/.bundle/
|
|
26
|
+
/vendor/bundle
|
|
27
|
+
/lib/bundler/man/
|
|
28
|
+
|
|
29
|
+
# for a library or gem, you might want to ignore these files since the code is
|
|
30
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
31
|
+
# Gemfile.lock
|
|
32
|
+
# .ruby-version
|
|
33
|
+
# .ruby-gemset
|
|
34
|
+
|
|
35
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
36
|
+
.rvmrc
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
messenger-bot-ruby (0.1.0)
|
|
5
|
+
httmultiparty (~> 0.3.16)
|
|
6
|
+
persistent_httparty (~> 0.1.2)
|
|
7
|
+
virtus (~> 1.0.5)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
axiom-types (0.1.1)
|
|
13
|
+
descendants_tracker (~> 0.0.4)
|
|
14
|
+
ice_nine (~> 0.11.0)
|
|
15
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
16
|
+
coderay (1.1.1)
|
|
17
|
+
coercible (1.0.0)
|
|
18
|
+
descendants_tracker (~> 0.0.1)
|
|
19
|
+
cuba (3.6.0)
|
|
20
|
+
rack (~> 1.6.0)
|
|
21
|
+
descendants_tracker (0.0.4)
|
|
22
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
23
|
+
equalizer (0.0.11)
|
|
24
|
+
gene_pool (1.4.1)
|
|
25
|
+
thread_safe
|
|
26
|
+
httmultiparty (0.3.16)
|
|
27
|
+
httparty (>= 0.7.3)
|
|
28
|
+
mimemagic
|
|
29
|
+
multipart-post
|
|
30
|
+
httparty (0.13.7)
|
|
31
|
+
json (~> 1.8)
|
|
32
|
+
multi_xml (>= 0.5.2)
|
|
33
|
+
ice_nine (0.11.2)
|
|
34
|
+
json (1.8.3)
|
|
35
|
+
method_source (0.8.2)
|
|
36
|
+
mimemagic (0.3.1)
|
|
37
|
+
multi_json (1.11.2)
|
|
38
|
+
multi_xml (0.5.5)
|
|
39
|
+
multipart-post (2.0.0)
|
|
40
|
+
persistent_http (1.0.6)
|
|
41
|
+
gene_pool (>= 1.3)
|
|
42
|
+
persistent_httparty (0.1.2)
|
|
43
|
+
httparty (~> 0.9)
|
|
44
|
+
persistent_http (< 2)
|
|
45
|
+
pry (0.10.3)
|
|
46
|
+
coderay (~> 1.1.0)
|
|
47
|
+
method_source (~> 0.8.1)
|
|
48
|
+
slop (~> 3.4)
|
|
49
|
+
rack (1.6.4)
|
|
50
|
+
rake (10.5.0)
|
|
51
|
+
slop (3.6.0)
|
|
52
|
+
thread_safe (0.3.5)
|
|
53
|
+
virtus (1.0.5)
|
|
54
|
+
axiom-types (~> 0.1)
|
|
55
|
+
coercible (~> 1.0)
|
|
56
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
|
57
|
+
equalizer (~> 0.0, >= 0.0.9)
|
|
58
|
+
|
|
59
|
+
PLATFORMS
|
|
60
|
+
ruby
|
|
61
|
+
|
|
62
|
+
DEPENDENCIES
|
|
63
|
+
bundler (~> 1.9)
|
|
64
|
+
cuba (~> 3.6.0)
|
|
65
|
+
messenger-bot-ruby!
|
|
66
|
+
multi_json
|
|
67
|
+
pry (~> 0.10.3)
|
|
68
|
+
rake (~> 10.0)
|
|
69
|
+
|
|
70
|
+
BUNDLED WITH
|
|
71
|
+
1.11.2
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016 Matteo Gavagnin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# messenger-bot-ruby
|
|
2
|
+
|
|
3
|
+
Ruby wrapper for [Messenger's Platform Bot API](https://developers.facebook.com/docs/messenger-platform).
|
|
4
|
+
|
|
5
|
+
[](http://badge.fury.io/rb/messenger-bot-ruby)
|
|
6
|
+
|
|
7
|
+
This library is heavily based on the great [telegram-bot-ruby](https://github.com/atipugin/telegram-bot-ruby) gem written by [Alexander Tipugin](mailto:atipugin@gmail.com) and its main goal is let you easily integrate your (existing?) Telegram Bot with Messenger.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
Messenger platform uses the webhook pattern to send you updates on events, requiring a TLS encrypted connection.
|
|
12
|
+
|
|
13
|
+
### Development: ngrok
|
|
14
|
+
|
|
15
|
+
To be able to use the bot on your development machine I heavily suggest you to purchase a [ngrok Pro subscription](https://ngrok.com/product#pricing) (billed monthly or yearly). I struggled with [localtunnel](https://localtunnel.me) that's free, but at the moment has a bug (it crashes losing the tunnel and the domain) and you have to reconfigure the webhook on Facebook every few minutes.
|
|
16
|
+
|
|
17
|
+
Remember to [authorize your machine](https://dashboard.ngrok.com/auth) after you purchase the subscription.
|
|
18
|
+
|
|
19
|
+
Whenever you need to develop, start a tunnel following the ngrok instructions, but something like this should suffice:
|
|
20
|
+
|
|
21
|
+
```shell
|
|
22
|
+
ngrok http 9292
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
where `http` is the protocol (if authenticated will establish also the encrypted `https` tunnel), `9292` is the default Cuba port.
|
|
26
|
+
|
|
27
|
+
```shell
|
|
28
|
+
Forwarding http://bf6ab2c6.ngrok.io -> localhost:9292
|
|
29
|
+
Forwarding https://bf6ab2c6.ngrok.io -> localhost:9292
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The interesting information is the `https://bf6bb3c6.ngrok.io` forwarded address, you need to use it on Facebook to configure the webhook appending the `/messenger` path.
|
|
33
|
+
|
|
34
|
+
So your webhook should point to something like
|
|
35
|
+
|
|
36
|
+
https://bf6ab2c6.ngrok.io/messenger
|
|
37
|
+
|
|
38
|
+
Please follow the [official guide](https://developers.facebook.com/docs/messenger-platform) and once completed you can proceed.
|
|
39
|
+
|
|
40
|
+
### Production: Let's Encrypt
|
|
41
|
+
|
|
42
|
+
On the production machine I cannot praise enough [Let's Encrypt](http://letsencrypt.org): free and secure TLS certificates for everyone (I'm using it on many production projects and it rocks!).
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
Clone this repository and start a terminal session inside the folder and execute the following commands to fetch the necessary dependancies and start the server.
|
|
47
|
+
|
|
48
|
+
```shell
|
|
49
|
+
bundle
|
|
50
|
+
bundle exec rackup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `rackup` command will start a Cuba server using the sample file in `examples/bot.rb`.
|
|
54
|
+
There you should replace the `YOUR_MESSENGER_PAGE_TOKEN_HERE` string with the page token provided by Facebook (follow the [official guide](https://developers.facebook.com/docs/messenger-platform) to know more).
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
Add following line to your Gemfile:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
gem 'messenger-bot-ruby'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
And then execute:
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
$ bundle
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or install it system-wide:
|
|
71
|
+
|
|
72
|
+
```shell
|
|
73
|
+
$ gem install messenger-bot-ruby
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
Take a look at the `examples/bot.rb` file reported below.
|
|
79
|
+
I'm planning to add detailed informations in the next few days.
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
require 'bundler/setup'
|
|
83
|
+
require 'cuba'
|
|
84
|
+
require 'multi_json'
|
|
85
|
+
require 'messenger/bot'
|
|
86
|
+
|
|
87
|
+
token = "YOUR_MESSENGER_PAGE_TOKEN_HERE"
|
|
88
|
+
bot = Messenger::Bot::Client.new(token)
|
|
89
|
+
|
|
90
|
+
# deserialize JSON data from request body
|
|
91
|
+
def json_load(request_body)
|
|
92
|
+
request_body.rewind
|
|
93
|
+
body = request_body.read
|
|
94
|
+
MultiJson.load body
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Cuba.define do
|
|
98
|
+
on get do
|
|
99
|
+
on "messenger" do
|
|
100
|
+
on param("hub.mode"), param("hub.challenge"), param("hub.verify_token") do |mode, challenge, token|
|
|
101
|
+
if mode == "subscribe"
|
|
102
|
+
# TODO: check the verify_token value that you inserted on the facebook developer page
|
|
103
|
+
res.write challenge
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
on root do
|
|
108
|
+
res.write "Hello world!"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
on post do
|
|
113
|
+
puts "Got a post call #{res.to_json}"
|
|
114
|
+
on "messenger" do
|
|
115
|
+
data = json_load req.body
|
|
116
|
+
# puts "#{data}"
|
|
117
|
+
res.write "" #200
|
|
118
|
+
callback = Messenger::Bot::Types::Callback.new(data)
|
|
119
|
+
for e in callback.entry
|
|
120
|
+
for mess in e.messaging
|
|
121
|
+
if mess.message
|
|
122
|
+
if mess.sender.id # We have someone to send the message to!
|
|
123
|
+
options = {bot: bot, messaging: mess, token: token}
|
|
124
|
+
bot.api.messages(recipient: {id: mess.sender.id}, message: { text: "Hello from a Bot!"})
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
puts "Received Postback or delivery"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Aknowledgments
|
|
138
|
+
|
|
139
|
+
Idea and initial implementation by [Matteo Gavagnin](https://twitter.com/macteo)
|
|
140
|
+
Thanks again to [Alexander Tipugin](mailto:atipugin@gmail.com) for inspiration and the [original gem](https://github.com/atipugin/telegram-bot-ruby) architecture.
|
|
141
|
+
|
|
142
|
+
## Contributing
|
|
143
|
+
|
|
144
|
+
1. Fork it
|
|
145
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
146
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
147
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
148
|
+
5. Create new Pull Request
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
Released under MIT license.
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/config.ru
ADDED
data/examples/bot.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
require 'cuba'
|
|
4
|
+
require 'multi_json'
|
|
5
|
+
require 'messenger/bot'
|
|
6
|
+
|
|
7
|
+
token = "YOUR_MESSENGER_PAGE_TOKEN_HERE"
|
|
8
|
+
bot = Messenger::Bot::Client.new(token)
|
|
9
|
+
|
|
10
|
+
# deserialize JSON data from request body
|
|
11
|
+
def json_load(request_body)
|
|
12
|
+
request_body.rewind
|
|
13
|
+
body = request_body.read
|
|
14
|
+
MultiJson.load body
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Cuba.define do
|
|
18
|
+
on get do
|
|
19
|
+
on "messenger" do
|
|
20
|
+
on param("hub.mode"), param("hub.challenge"), param("hub.verify_token") do |mode, challenge, token|
|
|
21
|
+
if mode == "subscribe"
|
|
22
|
+
# TODO: check the verify_token value that you inserted on the facebook developer page
|
|
23
|
+
res.write challenge
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
on root do
|
|
28
|
+
res.write "Hello world!"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
on post do
|
|
33
|
+
puts "Got a post call #{res.to_json}"
|
|
34
|
+
on "messenger" do
|
|
35
|
+
data = json_load req.body
|
|
36
|
+
# puts "#{data}"
|
|
37
|
+
res.write "" #200
|
|
38
|
+
callback = Messenger::Bot::Types::Callback.new(data)
|
|
39
|
+
for e in callback.entry
|
|
40
|
+
for mess in e.messaging
|
|
41
|
+
if mess.message
|
|
42
|
+
if mess.sender.id # We have someone to send the message to!
|
|
43
|
+
options = {bot: bot, messaging: mess, token: token}
|
|
44
|
+
bot.api.messages(recipient: {id: mess.sender.id}, message: { text: "Hello from a Bot!"})
|
|
45
|
+
end
|
|
46
|
+
else
|
|
47
|
+
puts "Received Postback or delivery"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'httmultiparty'
|
|
2
|
+
require 'persistent_httparty'
|
|
3
|
+
require 'virtus'
|
|
4
|
+
|
|
5
|
+
require 'messenger/bot/types'
|
|
6
|
+
require 'messenger/bot/exceptions'
|
|
7
|
+
require 'messenger/bot/api'
|
|
8
|
+
require 'messenger/bot/null_logger'
|
|
9
|
+
require 'messenger/bot/client'
|
|
10
|
+
require 'messenger/bot/version'
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
class Api
|
|
4
|
+
include HTTMultiParty
|
|
5
|
+
ENDPOINTS = %w(
|
|
6
|
+
messages
|
|
7
|
+
).freeze
|
|
8
|
+
POOL_SIZE = ENV.fetch('MESSENGER_BOT_POOL_SIZE', 1).to_i.freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :token
|
|
11
|
+
|
|
12
|
+
base_uri 'https://graph.facebook.com/v2.6/'
|
|
13
|
+
persistent_connection_adapter pool_size: POOL_SIZE,
|
|
14
|
+
keep_alive: 30,
|
|
15
|
+
force_retry: true
|
|
16
|
+
|
|
17
|
+
def initialize(token)
|
|
18
|
+
@token = token
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_missing(method_name, *args, &block)
|
|
22
|
+
endpoint = method_name.to_s
|
|
23
|
+
endpoint = camelize(endpoint) if endpoint.include?('_')
|
|
24
|
+
|
|
25
|
+
ENDPOINTS.include?(endpoint) ? call(endpoint, *args) : super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def respond_to_missing?(*args)
|
|
29
|
+
method_name = args[0].to_s
|
|
30
|
+
method_name = camelize(method_name) if method_name.include?('_')
|
|
31
|
+
|
|
32
|
+
ENDPOINTS.include?(method_name) || super
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def user_profile(user_id)
|
|
36
|
+
response = self.class.get("#{user_id}?fields=first_name,last_name,profile_pic&access_token=#{token}")
|
|
37
|
+
if response.code == 200
|
|
38
|
+
response.to_hash
|
|
39
|
+
else
|
|
40
|
+
fail Exceptions::ResponseError.new(response),
|
|
41
|
+
'Messenger API has returned the error.'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def call(endpoint, raw_params = {})
|
|
46
|
+
params = build_params(raw_params)
|
|
47
|
+
response = self.class.post("/me/#{endpoint}?access_token=#{token}", query: params)
|
|
48
|
+
if response.code == 200
|
|
49
|
+
response.to_hash
|
|
50
|
+
else
|
|
51
|
+
fail Exceptions::ResponseError.new(response),
|
|
52
|
+
'Messenger API has returned the error.'
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_params(h)
|
|
59
|
+
h.each_with_object({}) do |(key, value), params|
|
|
60
|
+
params[key] = sanitize_value(value)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sanitize_value(value)
|
|
65
|
+
jsonify(jsonify_with_file_preservation(value))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def jsonify_with_file_preservation(value)
|
|
69
|
+
if !value.is_a?(File)
|
|
70
|
+
return value.to_h.to_json
|
|
71
|
+
end
|
|
72
|
+
return value
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def jsonify(value)
|
|
76
|
+
return value unless value.is_a?(Array)
|
|
77
|
+
value.map { |i| i.to_h.select { |_, v| v } }.to_json
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def camelize(method_name)
|
|
81
|
+
words = method_name.split('_')
|
|
82
|
+
words.drop(1).map(&:capitalize!)
|
|
83
|
+
words.join
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
class Client
|
|
4
|
+
TIMEOUT_EXCEPTIONS = [Timeout::Error]
|
|
5
|
+
TIMEOUT_EXCEPTIONS << Net::ReadTimeout if Net.const_defined?(:ReadTimeout)
|
|
6
|
+
|
|
7
|
+
attr_reader :api, :offset, :timeout
|
|
8
|
+
attr_accessor :logger
|
|
9
|
+
|
|
10
|
+
def self.run(*args, &block)
|
|
11
|
+
new(*args).run(&block)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(token, h = {})
|
|
15
|
+
options = default_options.merge(h)
|
|
16
|
+
@api = Api.new(token)
|
|
17
|
+
@offset = options[:offset]
|
|
18
|
+
@timeout = options[:timeout]
|
|
19
|
+
@logger = options[:logger]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run
|
|
23
|
+
yield self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def listen(&block)
|
|
27
|
+
logger.info('Starting bot')
|
|
28
|
+
running = true
|
|
29
|
+
Signal.trap('INT') { running = false }
|
|
30
|
+
fetch_updates(&block) while running
|
|
31
|
+
exit
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def fetch_updates
|
|
35
|
+
response = api.getUpdates(offset: offset, timeout: timeout)
|
|
36
|
+
return unless response['ok']
|
|
37
|
+
|
|
38
|
+
response['result'].each do |data|
|
|
39
|
+
update = Types::Update.new(data)
|
|
40
|
+
@offset = update.update_id.next
|
|
41
|
+
message = extract_message(update)
|
|
42
|
+
log_incoming_message(message)
|
|
43
|
+
yield message
|
|
44
|
+
end
|
|
45
|
+
rescue *TIMEOUT_EXCEPTIONS
|
|
46
|
+
retry
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def default_options
|
|
52
|
+
{ offset: 0, timeout: 20, logger: NullLogger.new }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extract_message(update)
|
|
56
|
+
update.inline_query || update.chosen_inline_result || update.message
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def log_incoming_message(message)
|
|
60
|
+
logger.info(
|
|
61
|
+
format('Incoming message: text="%s" uid=%i', message, message.from.id)
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Exceptions
|
|
4
|
+
class ResponseError < Base
|
|
5
|
+
attr_reader :response
|
|
6
|
+
|
|
7
|
+
def initialize(response)
|
|
8
|
+
@response = response
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
super +
|
|
13
|
+
format(' (%s)', data.map { |k, v| %(#{k}: "#{v}") }.join(', '))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def error_code
|
|
17
|
+
data[:error_code] || data['error_code']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def data
|
|
23
|
+
@data ||= begin
|
|
24
|
+
JSON.parse(response.body)
|
|
25
|
+
rescue JSON::ParserError
|
|
26
|
+
{ error_code: response.code, uri: response.request.last_uri.to_s }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'messenger/bot/types/base'
|
|
2
|
+
require 'messenger/bot/types/payload'
|
|
3
|
+
require 'messenger/bot/types/element'
|
|
4
|
+
require 'messenger/bot/types/button'
|
|
5
|
+
require 'messenger/bot/types/attachment'
|
|
6
|
+
require 'messenger/bot/types/sender'
|
|
7
|
+
require 'messenger/bot/types/recipient'
|
|
8
|
+
require 'messenger/bot/types/delivery'
|
|
9
|
+
require 'messenger/bot/types/postback'
|
|
10
|
+
require 'messenger/bot/types/message'
|
|
11
|
+
require 'messenger/bot/types/messaging'
|
|
12
|
+
require 'messenger/bot/types/entry'
|
|
13
|
+
require 'messenger/bot/types/callback'
|
|
14
|
+
require 'messenger/bot/types/button_template'
|
|
15
|
+
require 'messenger/bot/types/generic_template'
|
|
16
|
+
require 'messenger/bot/types/receipt_element'
|
|
17
|
+
require 'messenger/bot/types/address'
|
|
18
|
+
require 'messenger/bot/types/summary'
|
|
19
|
+
require 'messenger/bot/types/adjustment'
|
|
20
|
+
require 'messenger/bot/types/receipt_template'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Types
|
|
4
|
+
class Address < Base
|
|
5
|
+
attribute :street_1, String
|
|
6
|
+
attribute :street_2, String
|
|
7
|
+
attribute :city, String
|
|
8
|
+
attribute :postal_code, String
|
|
9
|
+
attribute :state, String
|
|
10
|
+
attribute :country, String
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Types
|
|
4
|
+
class Button < Base
|
|
5
|
+
attribute :type, String # web_url or postback
|
|
6
|
+
attribute :title, String
|
|
7
|
+
attribute :url, String # Required for web_url buttons
|
|
8
|
+
attribute :payload, String # Required for postback buttons
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Types
|
|
4
|
+
class GenericTemplate < Base
|
|
5
|
+
attribute :template_type, String # generic
|
|
6
|
+
attribute :elements, Array[Element]
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
=begin
|
|
13
|
+
|
|
14
|
+
# Generic Template Guidelines
|
|
15
|
+
|
|
16
|
+
## Limits
|
|
17
|
+
|
|
18
|
+
- Title: 45 characters
|
|
19
|
+
- Subtitle: 80 characters
|
|
20
|
+
- Call-to-action title: 20 characters
|
|
21
|
+
- Call-to-action items: 3 buttons
|
|
22
|
+
- Bubbles per message (horizontal scroll): 10 elements
|
|
23
|
+
|
|
24
|
+
## Image Dimensions
|
|
25
|
+
|
|
26
|
+
Image ratio is 1.91:1
|
|
27
|
+
|
|
28
|
+
=end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Types
|
|
4
|
+
class Messaging < Base
|
|
5
|
+
attribute :sender, Sender
|
|
6
|
+
attribute :recipient, Recipient
|
|
7
|
+
attribute :timestamp, Integer
|
|
8
|
+
attribute :message, Message
|
|
9
|
+
attribute :delivery, Delivery
|
|
10
|
+
attribute :postback, Postback
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Types
|
|
4
|
+
class ReceiptElement < Base
|
|
5
|
+
attribute :title, String
|
|
6
|
+
attribute :subtitle, String
|
|
7
|
+
attribute :quantity, Float
|
|
8
|
+
attribute :price, String
|
|
9
|
+
attribute :currency, String
|
|
10
|
+
attribute :image_url, String
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Messenger
|
|
2
|
+
module Bot
|
|
3
|
+
module Types
|
|
4
|
+
class ReceiptTemplate < Base
|
|
5
|
+
attribute :template_type, String # receipt
|
|
6
|
+
attribute :recipient_name, String
|
|
7
|
+
attribute :order_number, String
|
|
8
|
+
attribute :currency, String
|
|
9
|
+
attribute :payment_method, String
|
|
10
|
+
attribute :timestamp, String
|
|
11
|
+
attribute :order_url, String
|
|
12
|
+
attribute :elements, Array[ReceiptElement]
|
|
13
|
+
attribute :address, Address
|
|
14
|
+
attribute :summary, Summary
|
|
15
|
+
attribute :adjustments, Array[Adjustment]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
|
|
4
|
+
require 'messenger/bot/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'messenger-bot-ruby'
|
|
8
|
+
spec.version = Messenger::Bot::VERSION
|
|
9
|
+
spec.authors = ['Matteo Gavagnin', 'Alexander Tipugin']
|
|
10
|
+
spec.email = ['m@macteo.it', 'atipugin@gmail.com']
|
|
11
|
+
spec.licenses = ['MIT']
|
|
12
|
+
spec.summary = "Ruby wrapper for Messenger's Bot API"
|
|
13
|
+
spec.homepage = 'https://github.com/macteo/messenger-bot-ruby'
|
|
14
|
+
|
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
16
|
+
spec.bindir = 'exe'
|
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
18
|
+
spec.require_paths = ['lib']
|
|
19
|
+
|
|
20
|
+
spec.add_dependency 'httmultiparty', '~> 0.3'
|
|
21
|
+
spec.add_dependency 'persistent_httparty', '~> 0.1'
|
|
22
|
+
spec.add_dependency 'virtus', '~> 1.0'
|
|
23
|
+
|
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
26
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
|
27
|
+
spec.add_development_dependency 'cuba', '~> 3.6'
|
|
28
|
+
spec.add_development_dependency 'multi_json', '~> 1.11'
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: messenger-bot-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matteo Gavagnin
|
|
8
|
+
- Alexander Tipugin
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: exe
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2016-04-13 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: httmultiparty
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '0.3'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '0.3'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: persistent_httparty
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '0.1'
|
|
35
|
+
type: :runtime
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0.1'
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: virtus
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - "~>"
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '1.0'
|
|
49
|
+
type: :runtime
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - "~>"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '1.0'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: bundler
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - "~>"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '1.9'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '1.9'
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: rake
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - "~>"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '10.0'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - "~>"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '10.0'
|
|
84
|
+
- !ruby/object:Gem::Dependency
|
|
85
|
+
name: pry
|
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - "~>"
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0.10'
|
|
91
|
+
type: :development
|
|
92
|
+
prerelease: false
|
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - "~>"
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0.10'
|
|
98
|
+
- !ruby/object:Gem::Dependency
|
|
99
|
+
name: cuba
|
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - "~>"
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '3.6'
|
|
105
|
+
type: :development
|
|
106
|
+
prerelease: false
|
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - "~>"
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '3.6'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: multi_json
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - "~>"
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '1.11'
|
|
119
|
+
type: :development
|
|
120
|
+
prerelease: false
|
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - "~>"
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '1.11'
|
|
126
|
+
description:
|
|
127
|
+
email:
|
|
128
|
+
- m@macteo.it
|
|
129
|
+
- atipugin@gmail.com
|
|
130
|
+
executables: []
|
|
131
|
+
extensions: []
|
|
132
|
+
extra_rdoc_files: []
|
|
133
|
+
files:
|
|
134
|
+
- ".gitignore"
|
|
135
|
+
- CHANGELOG.md
|
|
136
|
+
- Gemfile
|
|
137
|
+
- Gemfile.lock
|
|
138
|
+
- LICENSE
|
|
139
|
+
- README.md
|
|
140
|
+
- Rakefile
|
|
141
|
+
- bin/console
|
|
142
|
+
- bin/setup
|
|
143
|
+
- config.ru
|
|
144
|
+
- examples/bot.rb
|
|
145
|
+
- lib/messenger/bot.rb
|
|
146
|
+
- lib/messenger/bot/api.rb
|
|
147
|
+
- lib/messenger/bot/client.rb
|
|
148
|
+
- lib/messenger/bot/exceptions.rb
|
|
149
|
+
- lib/messenger/bot/exceptions/base.rb
|
|
150
|
+
- lib/messenger/bot/exceptions/response_error.rb
|
|
151
|
+
- lib/messenger/bot/null_logger.rb
|
|
152
|
+
- lib/messenger/bot/types.rb
|
|
153
|
+
- lib/messenger/bot/types/address.rb
|
|
154
|
+
- lib/messenger/bot/types/adjustment.rb
|
|
155
|
+
- lib/messenger/bot/types/attachment.rb
|
|
156
|
+
- lib/messenger/bot/types/base.rb
|
|
157
|
+
- lib/messenger/bot/types/button.rb
|
|
158
|
+
- lib/messenger/bot/types/button_template.rb
|
|
159
|
+
- lib/messenger/bot/types/callback.rb
|
|
160
|
+
- lib/messenger/bot/types/delivery.rb
|
|
161
|
+
- lib/messenger/bot/types/element.rb
|
|
162
|
+
- lib/messenger/bot/types/entry.rb
|
|
163
|
+
- lib/messenger/bot/types/generic_template.rb
|
|
164
|
+
- lib/messenger/bot/types/image.rb
|
|
165
|
+
- lib/messenger/bot/types/image_attachment.rb
|
|
166
|
+
- lib/messenger/bot/types/message.rb
|
|
167
|
+
- lib/messenger/bot/types/messaging.rb
|
|
168
|
+
- lib/messenger/bot/types/payload.rb
|
|
169
|
+
- lib/messenger/bot/types/postback.rb
|
|
170
|
+
- lib/messenger/bot/types/receipt_element.rb
|
|
171
|
+
- lib/messenger/bot/types/receipt_template.rb
|
|
172
|
+
- lib/messenger/bot/types/recipient.rb
|
|
173
|
+
- lib/messenger/bot/types/sender.rb
|
|
174
|
+
- lib/messenger/bot/types/summary.rb
|
|
175
|
+
- lib/messenger/bot/types/template_attachment.rb
|
|
176
|
+
- lib/messenger/bot/version.rb
|
|
177
|
+
- messenger-bot-ruby.gemspec
|
|
178
|
+
homepage: https://github.com/macteo/messenger-bot-ruby
|
|
179
|
+
licenses:
|
|
180
|
+
- MIT
|
|
181
|
+
metadata: {}
|
|
182
|
+
post_install_message:
|
|
183
|
+
rdoc_options: []
|
|
184
|
+
require_paths:
|
|
185
|
+
- lib
|
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
187
|
+
requirements:
|
|
188
|
+
- - ">="
|
|
189
|
+
- !ruby/object:Gem::Version
|
|
190
|
+
version: '0'
|
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
|
+
requirements:
|
|
193
|
+
- - ">="
|
|
194
|
+
- !ruby/object:Gem::Version
|
|
195
|
+
version: '0'
|
|
196
|
+
requirements: []
|
|
197
|
+
rubyforge_project:
|
|
198
|
+
rubygems_version: 2.5.1
|
|
199
|
+
signing_key:
|
|
200
|
+
specification_version: 4
|
|
201
|
+
summary: Ruby wrapper for Messenger's Bot API
|
|
202
|
+
test_files: []
|