sinbotra 0.1.6 → 0.2.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.
@@ -0,0 +1,72 @@
1
+ module Sinbotra::Messenger
2
+ class Platform
3
+ attr_reader :recipient_id
4
+ attr_reader :client
5
+
6
+ def self.get_started(postback_id)
7
+ client = MessengerClient::Client.new(ENV.fetch("FACEBOOK_PAGE_TOKEN"))
8
+ client.get_started(postback_id)
9
+ end
10
+
11
+ def initialize(recipient_id)
12
+ @recipient_id = recipient_id
13
+ @client = MessengerClient::Client.new(ENV.fetch("FACEBOOK_PAGE_TOKEN"))
14
+ end
15
+
16
+ def say(text)
17
+ client.text(
18
+ recipient_id: recipient_id,
19
+ text: text
20
+ )
21
+ end
22
+ alias_method :text, :say
23
+
24
+ def quick_replies(text, replies)
25
+ client.qr(
26
+ recipient_id: recipient_id,
27
+ text: text,
28
+ replies: replies
29
+ )
30
+ end
31
+
32
+ def typing(seconds=0, &blk)
33
+ client.typing(recipient_id: recipient_id, seconds: seconds, &blk)
34
+ end
35
+
36
+ def qr(text, postback=nil)
37
+ MessengerClient::QuickReply.new(text, postback)
38
+ end
39
+
40
+ def payload_button(text, payload)
41
+ MessengerClient::PayloadButton.new(text, payload)
42
+ end
43
+
44
+ def url_button(text, url)
45
+ MessengerClient::URLButton.new(text, url)
46
+ end
47
+
48
+ def generic_template(title, subtitle, image, link, buttons)
49
+ end
50
+
51
+ def text_with_buttons(text, buttons)
52
+ client.button_template(
53
+ recipient_id: recipient_id,
54
+ text: text,
55
+ buttons: buttons
56
+ )
57
+ end
58
+
59
+ def video(url)
60
+ client.video(recipient_id: recipient_id, url: url)
61
+ end
62
+
63
+ def image(url)
64
+ client.image(recipient_id: recipient_id, url: url)
65
+ end
66
+
67
+ def location
68
+ client.location(recipient_id: recipient_id)
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,46 @@
1
+ module Sinbotra::Messenger
2
+ class UserPresenter
3
+ attr_reader :current_user
4
+
5
+ def initialize(sender)
6
+ @current_user = Sinbotra::Bot::UserRepo.find_or_create(sender.id)
7
+ end
8
+
9
+ def id; current_user.id; end
10
+
11
+ def conversation
12
+ @current_user.conversation
13
+ end
14
+
15
+ def in_conversation?
16
+ !current_conversation.nil?
17
+ end
18
+
19
+ def start_conversation(convo_id)
20
+ update_conversation(convo_id, 0)
21
+ end
22
+
23
+ def next_step!
24
+ convo = current_user.conversation
25
+ id, step = convo.id, convo.step + 1
26
+ update_conversation(id, step)
27
+ end
28
+
29
+ def update_conversation(convo_id, step)
30
+ Sinbotra::Bot::UserRepo.update_conversation(
31
+ user: current_user,
32
+ convo_id: convo_id,
33
+ step: step
34
+ )
35
+ end
36
+
37
+ def end_conversation!
38
+ current_user.end_conversation
39
+ Sinbotra::Bot::UserRepo.update(current_user)
40
+ end
41
+
42
+ def current_conversation
43
+ current_user.conversation
44
+ end
45
+ end
46
+ end
@@ -2,25 +2,22 @@ require "sinbotra"
2
2
  require "sinatra"
3
3
  require "json"
4
4
 
5
+ require "sinbotra/messenger/middleware/facebook_signature"
6
+ require "sinbotra/messenger/middleware/parse_message"
7
+
5
8
  module Sinbotra
9
+
6
10
  class Server < Sinatra::Base
11
+ use Sinbotra::Messenger::Middleware::FacebookSignature
12
+ use Sinbotra::Messenger::Middleware::ParseMessage
13
+
7
14
  configure do
8
- raise ArgumentError, "You need to set a FACEBOOK_PAGE_TOKEN environmental variable to run the server!" unless ENV["FACEBOOK_PAGE_TOKEN"]
9
- raise ArgumentError, "You need to set a FACEBOOK_APP_SECRET environmental variable to run the server!" unless ENV["FACEBOOK_APP_SECRET"]
10
15
  raise ArgumentError, "You need to set a FACEBOOK_CHALLENGE environmental variable to run the server!" unless ENV["FACEBOOK_CHALLENGE"]
16
+ set :facebook_handler, Sinbotra::MessageHandler.new(:facebook)
17
+ Sinbotra::Bot::UserRepo.connect
11
18
  end
12
19
 
13
20
  helpers do
14
- def verify_facebook_signature!(payload_body)
15
- digest = OpenSSL::HMAC.hexdigest(
16
- OpenSSL::Digest.new("sha1"),
17
- ENV["FACEBOOK_APP_SECRET"],
18
- payload_body
19
- )
20
- signature = "sha1=" + digest
21
- return halt 403, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env["HTTP_X_HUB_SIGNATURE"])
22
- end
23
-
24
21
  def match_facebook_challenge(params)
25
22
  matching = params["hub.mode"] == "subscribe" &&
26
23
  params["hub.verify_token"] == ENV.fetch("FACEBOOK_CHALLENGE")
@@ -37,16 +34,15 @@ module Sinbotra
37
34
  end
38
35
 
39
36
  post "/facebook/webhook" do
40
- payload = request.body.read
41
- verify_facebook_signature!(payload)
42
37
  begin
43
- msg = JSON.load(payload)
44
- Sinbotra::Bot.handle(:facebook, msg)
38
+ msgs = request.env["facebook.messages"]
39
+ settings.facebook_handler.receive_messages(msgs)
40
+ status 200
45
41
  "OK"
46
42
  rescue Exception => e
47
43
  STDERR.puts e.message
48
44
  STDERR.puts e.backtrace
49
- status 500
45
+ status 200
50
46
  "ERROR"
51
47
  end
52
48
  end
@@ -1,3 +1,3 @@
1
1
  module Sinbotra
2
- VERSION = "0.1.6"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "messenger_client"
35
35
  spec.add_dependency "sucker_punch"
36
36
  spec.add_dependency "typhoeus"
37
+ spec.add_dependency "redis"
37
38
 
38
39
  spec.add_development_dependency "puma"
39
40
  spec.add_development_dependency "bundler", "~> 1.13"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinbotra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Cruz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-24 00:00:00.000000000 Z
11
+ date: 2017-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: redis
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: puma
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -153,21 +167,25 @@ files:
153
167
  - bin/console
154
168
  - bin/setup
155
169
  - config.example.ru
156
- - examples/pushup_bot.rb
157
- - examples/simple_conversations.rb
170
+ - examples/.keep
158
171
  - lib/sinbotra.rb
159
172
  - lib/sinbotra/bot.rb
160
173
  - lib/sinbotra/bot/conversation.rb
161
- - lib/sinbotra/bot/conversation_repo.rb
162
- - lib/sinbotra/bot/default_message_history.rb
174
+ - lib/sinbotra/bot/redis_store.rb
163
175
  - lib/sinbotra/bot/user.rb
164
176
  - lib/sinbotra/bot/user_repo.rb
177
+ - lib/sinbotra/bot/user_store.rb
165
178
  - lib/sinbotra/config.rb
179
+ - lib/sinbotra/message_handler.rb
180
+ - lib/sinbotra/message_store.rb
166
181
  - lib/sinbotra/messenger.rb
167
182
  - lib/sinbotra/messenger/bot.rb
168
- - lib/sinbotra/messenger/handler.rb
169
183
  - lib/sinbotra/messenger/message.rb
170
- - lib/sinbotra/messenger/user.rb
184
+ - lib/sinbotra/messenger/message_presenter.rb
185
+ - lib/sinbotra/messenger/middleware/facebook_signature.rb
186
+ - lib/sinbotra/messenger/middleware/parse_message.rb
187
+ - lib/sinbotra/messenger/platform.rb
188
+ - lib/sinbotra/messenger/user_presenter.rb
171
189
  - lib/sinbotra/server.rb
172
190
  - lib/sinbotra/version.rb
173
191
  - sinbotra.gemspec
@@ -191,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
209
  version: '0'
192
210
  requirements: []
193
211
  rubyforge_project:
194
- rubygems_version: 2.6.8
212
+ rubygems_version: 2.5.2
195
213
  signing_key:
196
214
  specification_version: 4
197
215
  summary: A bot framework.
@@ -1,47 +0,0 @@
1
- class PushupBot
2
- include Sinbotra::Messenger::Handler
3
-
4
- enable_get_started!
5
-
6
- get_started do
7
- start_conversation(:onboarding)
8
- end
9
-
10
- hear(/hi|hello|yo/i) do |bot, _msg|
11
- bot.say "Hello there!"
12
- bot.start_conversation(:onboarding, msg)
13
- end
14
-
15
- hear do |bot, msg|
16
- intent = get_intent(msg.text)
17
- if conversations.include?(intent)
18
- bot.start_conversation(intent, msg)
19
- else
20
- bot.say next_dont_understand_phrase
21
- end
22
- end
23
-
24
- conversation :onboarding do |convo|
25
- convo.step :ask_name do |_msg|
26
- convo.say "Hi there!"
27
- convo.say "What's your name?"
28
- convo.next_step!
29
- end
30
-
31
- convo.step :ask_height do |msg|
32
- convo.store!(:name, msg.text)
33
- convo.say "Great #{convo.recall[:name]}. How tall are you?"
34
- convo.next_step!
35
- end
36
-
37
- convo.step :done do |msg|
38
- convo.store!(:height, msg.text)
39
- convo.say "Thank you for the info"
40
- typing 1
41
- convo.say "See you around!"
42
- convo.done!
43
- end
44
- end
45
- end
46
-
47
- Sinbotra::Bot.add_handler(:facebook, PushupBot)
@@ -1,38 +0,0 @@
1
- class SimpleConversation
2
- include Sinbotra::Conversation
3
-
4
- hear /hi|hello/ do |_|
5
- start_conversation(:onboarding)
6
- end
7
-
8
- hear do |msg|
9
- intent = get_intent(msg.text)
10
- if conversations.include?(intent)
11
- start_conversation(intent)
12
- else
13
- say next_dont_understand_phrase
14
- end
15
- end
16
-
17
- conversation :onboarding do
18
- step :ask_name do |convo|
19
- say "Hi there!"
20
- say "What's your name?"
21
- convo.next_step!
22
- end
23
-
24
- step :ask_height do |convo|
25
- convo.store(:name, convo.last_message.text)
26
- say "Great. How tall are you?"
27
- convo.next_step!
28
- end
29
-
30
- step :done do |convo|
31
- convo.store(:height, convo.last_message.text)
32
- say "Thank you for the info"
33
- typing 1
34
- say "See you around!"
35
- convo.done!
36
- end
37
- end
38
- end
@@ -1,17 +0,0 @@
1
- class Sinbotra::Bot
2
- class ConversationRepo
3
- class << self
4
- def connect
5
- @conversations ||= {}
6
- end
7
-
8
- def find(id)
9
- @conversations[id]
10
- end
11
-
12
- def create(id, convo)
13
- @conversations[id] = convo
14
- end
15
- end
16
- end
17
- end
@@ -1,15 +0,0 @@
1
- class Sinbotra::Bot
2
- class DefaultMessageHistory
3
- attr_reader :store
4
-
5
- def initialize(store=[])
6
- @store = store
7
- end
8
-
9
- def add_message(msg)
10
- @store << msg
11
- end
12
-
13
- def all() @store end
14
- end
15
- end
@@ -1,126 +0,0 @@
1
- module Sinbotra::Messenger
2
- module Handler
3
- def self.included(base)
4
- ensure_repos_connected
5
- base.extend ClassMethods
6
- end
7
-
8
- def self.ensure_repos_connected
9
- Sinbotra::Bot::ConversationRepo.connect
10
- Sinbotra::Bot::UserRepo.connect
11
- end
12
-
13
- module ClassMethods
14
- GET_STARTED_ID = "GET_STARTED"
15
-
16
- def handle_message(msg)
17
- user = Sinbotra::Messenger::User.load(msg)
18
- bot = Sinbotra::Messenger::Bot.new(user)
19
- # Next step in conversation
20
- # or match the `#hear`
21
- # or get default fallback
22
- if user.in_conversation?
23
- convo = user.current_conversation
24
- convo.perform_current_step(bot)
25
- elsif intent = match_intent(bot)
26
- intent.callback.(bot) unless intent.nil?
27
- else
28
- intent = default_intent
29
- intent.callback.(bot) unless intent.nil?
30
- end
31
- end
32
-
33
- def handle_action(action_name, opts={})
34
- a = @actions[action_name]
35
- if a.nil?
36
- raise ArgumentError, "No action found with the key #{action_name}"
37
- end
38
- a.(opts)
39
- end
40
-
41
- def enable_get_started!
42
- @client = MessengerClient::Client.new(ENV.fetch("FACEBOOK_PAGE_TOKEN"))
43
- @client.get_started(GET_STARTED_ID)
44
- end
45
-
46
- def get_started(&blk)
47
- postback(GET_STARTED_ID, &blk)
48
- end
49
-
50
- def conversation(id, &blk)
51
- c = Sinbotra::Bot::Conversation.new(id)
52
- Sinbotra::Bot::ConversationRepo.create(id, c)
53
- yield c
54
- end
55
-
56
- Listener = Struct.new(:matcher, :callback) do
57
- def default?; false end
58
- end
59
- DefaultListener = Struct.new(:callback) do
60
- def default?; true end
61
- end
62
- Postback = Struct.new(:matcher, :callback)
63
-
64
- def postback(matcher, &blk)
65
- # TODO: THis feels kinda wrong
66
- @postbacks ||= []
67
- @postbacks << Postback.new(matcher, blk)
68
- end
69
-
70
- def hear(matcher=nil, &blk)
71
- # TODO: THis feels kinda wrong
72
- @listeners ||= []
73
- if matcher.nil?
74
- return @listeners << DefaultListener.new(blk)
75
- end
76
- @listeners << Listener.new(matcher, blk)
77
- end
78
-
79
- def action(name, &blk)
80
- @actions ||= {}
81
- @actions[name] = blk
82
- end
83
-
84
- def get_intent(text)
85
- puts "NOOP get_intent"
86
- nil
87
- end
88
-
89
- def yes?(str)
90
- str =~ yes_matcher
91
- end
92
-
93
- def typing(delay=1)
94
- puts "NOOP show typing for #{delay} seconds"
95
- end
96
-
97
- def next_dont_understand_phrase
98
- puts "NOOP next_dont_understand_phrase"
99
- end
100
-
101
- private
102
-
103
- def yes_matcher
104
- /^(yes|yeah|ya|ok)/i
105
- end
106
-
107
- def default_intent
108
- @listeners.find(&:default?)
109
- end
110
-
111
- def match_intent(bot)
112
- msg = bot.current_message
113
- # look for postback
114
- postback = msg.postback
115
- if !postback.nil?
116
- found_pb = @postbacks.find { |pb| postback.match(pb.matcher) }
117
- return found_pb unless found_pb.nil?
118
- end
119
- # if you couldn't find a postback, look for text
120
- txt = msg.text
121
- @listeners.find { |l| !l.default? && txt.match(l.matcher) }
122
- end
123
- end
124
- end
125
- end
126
-