sinbotra 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b234ddd78053ba74b469a2f9286d152c6f51f43
4
- data.tar.gz: 362c8ab94b8d5a24eb2a7209e03e05aa149ee326
3
+ metadata.gz: 978fcbcaca2a3766c1a7b655713a30cdff701d70
4
+ data.tar.gz: 58df78433260eb468f01632274f292c947955f62
5
5
  SHA512:
6
- metadata.gz: 201c51423d49bd568f511aacc4659496f51e465b34a80a05252e4ca3df64c67931742d05c753f6da96b17b6653e986e6dd37e84cf6efcecd0fc64150b7fa5eed
7
- data.tar.gz: 8bccd900765a669141fd285fd83275488a4d11825c585a68e5e335564168e4c627966f90be852c2d5a45466285deb134d83500aad61afbebfe5a456f11f54af4
6
+ metadata.gz: 1e5aaeaf20e279e5c4f47063ba10267cb0ed98955608c1af215a0f16768eba80a25f200793887eb8b0f1b0fc6a6a07ab391d1f2e0e16bfa7c703ab7014cce767
7
+ data.tar.gz: 3a5202cc1d82c29e0258b570644b27d7a9d1d3e20451d50cac4b020c985ecd3c6b32be9ab382f817feab2cf5c805aedd01abe4362e0e49cbd29cb9e9e9718a18
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
 
11
11
  .env*
12
12
  .DS_Store
13
+ tags
data/README.md CHANGED
@@ -39,3 +39,129 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERN
39
39
 
40
40
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
41
 
42
+ ## New API!
43
+
44
+ You take an incoming message
45
+
46
+ Create a user out of the sender and a bot out of the user
47
+
48
+ You tell the bot to handle the message
49
+
50
+ It checks if the user is in a conversation
51
+
52
+ If YES then handle the current conversation step. On step end, increase conversation step or end convo
53
+
54
+ If NO then cycle through handlers
55
+
56
+ If still nothing, answer with default if it exists
57
+
58
+ If still not, use default I don't understandj
59
+
60
+
61
+ ```ruby
62
+ class BurpeeBot < Sinbotra::Bot
63
+ listeners onboard: /hi|hello/,
64
+ start_workout: /start|begin/
65
+
66
+ default_listener :default
67
+
68
+ conversations onboarding: OnboardingConversation,
69
+ start_workout: StartWorkoutConversation
70
+
71
+ actions start_workout: StartWorkoutAction
72
+
73
+ def onboard(msg, user)
74
+ bot.say(user, "Hello #{user.name}")
75
+ start_conversation(:onboarding)
76
+ end
77
+
78
+ def start_workout(msg, user)
79
+ end
80
+
81
+ def default(msg, user)
82
+ end
83
+ end
84
+
85
+ BurpeeBot.perform_action(:start_workout, args)
86
+
87
+ class StartWorkoutAction
88
+ def call(bot, args={})
89
+ athletes = Athlete::Repo.find_fresh
90
+ athletes.each do |athlete|
91
+ bot.send_text(athlete.fb_id, "Time to work out!!!")
92
+ end
93
+ end
94
+ end
95
+
96
+ class Sinbotra::Conversation
97
+ class << self
98
+ attr_reader :step_names
99
+ def steps(*all_steps)
100
+ @step_names = all_steps
101
+ end
102
+ end
103
+
104
+ attr_reader :current_user
105
+ attr_reader :step_num
106
+
107
+ def initialize(user, bot)
108
+ @current_user = user
109
+ @step_num = 0
110
+ @active = true
111
+ @steps = self.class.step_names
112
+ end
113
+
114
+ def next_step!
115
+ @step_num += 1
116
+ if @step_num == @steps.size
117
+ end_conversation!
118
+ end
119
+ end
120
+
121
+ def say(txt)
122
+ STDOUT.puts(txt)
123
+ end
124
+
125
+ def end_conversation!
126
+ @step_num = -1
127
+ @active = false
128
+ end
129
+
130
+ def active?; @active; end
131
+ end
132
+
133
+ class OnboardingConversation
134
+ include Sinbotra::Conversation
135
+
136
+ steps :ask_name, :ask_age, :ask_fitness_level, :all_good
137
+
138
+ def ask_name(msg)
139
+ say("Hello, what is your name?")
140
+ # I have access to current_user
141
+ next_step!
142
+ end
143
+
144
+ def ask_age(msg, bot)
145
+ say("Great, your name is #{msg.text}")
146
+ say("How old are you?")
147
+ next_step!
148
+ end
149
+
150
+ def ask_fitness_level(msg, bot)
151
+ say("Great, your #{msg.text} years old.")
152
+ say("What's your fitness level?")
153
+ next_step!
154
+ end
155
+
156
+ def all_good(msg, bot)
157
+ say("Great, your fitness level is #{msg.text}.")
158
+ say("We have everything we need!")
159
+ end_conversation!
160
+ end
161
+ end
162
+
163
+ class Bot::User
164
+ attr_reader :id, :current_converation
165
+ end
166
+
167
+ ```
File without changes
@@ -6,7 +6,13 @@ require "sinbotra/config"
6
6
 
7
7
  # Require sinbotra framework
8
8
  require "sinbotra/version"
9
- require "sinbotra/bot"
9
+ require "sinbotra/message_store"
10
+ require "sinbotra/message_handler"
11
+ require "sinbotra/bot/redis_store"
12
+ require "sinbotra/bot/user"
13
+ require "sinbotra/bot/user_store"
14
+ require "sinbotra/bot/user_repo"
15
+ require "sinbotra/bot/conversation"
10
16
  require "sinbotra/messenger"
11
17
 
12
18
  module Sinbotra
@@ -1,7 +1,8 @@
1
- require "sinbotra/bot/conversation"
2
- require "sinbotra/bot/conversation_repo"
1
+ require "sinbotra/bot/redis_store"
3
2
  require "sinbotra/bot/user"
3
+ require "sinbotra/bot/user_store"
4
4
  require "sinbotra/bot/user_repo"
5
+ require "sinbotra/bot/conversation"
5
6
 
6
7
  require "sucker_punch"
7
8
 
@@ -25,7 +26,9 @@ module Sinbotra
25
26
  def receive(provider, msg)
26
27
  handler = @handlers[provider.to_sym]
27
28
  Sinbotra::Config.logger.debug("MESSAGE DEBUG:\n" + msg.inspect) if ENV["DEBUG"]
28
- handler.handle_message(msg)
29
+ user = Sinbotra::User.from_message(msg)
30
+ bot = handler.new(user)
31
+ bot.handle_message(msg)
29
32
  end
30
33
 
31
34
  def handle_action(action_name, provider, handler, opts={})
@@ -35,10 +38,9 @@ module Sinbotra
35
38
 
36
39
  include SuckerPunch::Job
37
40
 
38
- def perform(provider, msg)
39
- # THis needs to be done elsewhere
40
- msg["entry"].first["messaging"].each do |m|
41
- Sinbotra::Bot.receive(provider, m)
41
+ def perform(provider, msgs)
42
+ msgs.each do |msg|
43
+ Sinbotra::Bot.receive(provider, msg)
42
44
  end
43
45
  end
44
46
  end
@@ -1,86 +1,85 @@
1
1
  class Sinbotra::Bot
2
2
  class Conversation
3
- Step = Struct.new(:id, :callback)
3
+ class << self
4
+ attr_reader :_steps
4
5
 
5
- attr_reader :id
6
- attr_reader :steps
6
+ private
7
7
 
8
- def initialize(id, store={})
9
- @id = id
10
- @steps = []
11
- @store = store
12
- update_current_step(0)
13
- store!(:is_active, false)
8
+ def steps(*_steps)
9
+ @_steps = _steps
10
+ end
14
11
  end
15
12
 
16
- def start
17
- store!(:is_active, true)
13
+ def initialize(bot, platform)
14
+ @bot = bot
15
+ @current_user = bot.current_user
16
+ @message = bot.message
17
+ @platform = platform
18
18
  end
19
19
 
20
- def active?
21
- recall(:is_active)
20
+ def start
21
+ run_step!(current_user.conversation.step, message)
22
22
  end
23
23
 
24
- def current_step
25
- recall(:_current_step)
24
+ def continue_dialogue
25
+ run_step!(current_user.conversation.step, message)
26
26
  end
27
27
 
28
- def step(id, &blk)
29
- @steps << Step.new(id, blk)
30
- end
28
+ private
31
29
 
32
- def store!(k, v)
33
- @store[k] = v
34
- end
30
+ attr_reader :current_user
31
+ attr_reader :message
32
+ attr_reader :platform
35
33
 
36
- def recall(k)
37
- @store[k]
34
+ def start_conversation(convo_id)
35
+ @bot.start_conversation(convo_id)
38
36
  end
39
37
 
40
- def say(text)
41
- puts text
38
+ def has_conversation?(intent)
39
+ @bot.has_conversation(intent)
42
40
  end
43
41
 
44
- def skip_to_next_step!
45
- puts "NOOP skip_to_next_step!"
42
+ def get_intent(msg)
43
+ @bot.get_intent(msg)
46
44
  end
47
45
 
48
- def next_step!
49
- new_step = current_step + 1
50
- update_current_step(new_step)
51
- done! if completed_last_step?
52
- end
46
+ def steps; self.class._steps; end
53
47
 
54
- def next_step_now!(bot)
55
- next_step!
56
- perform_current_step(bot)
57
- end
48
+ def step(n) steps[n]; end
58
49
 
59
- def repeat_step!
60
- puts "Repeating conversation step. Nothing to do here."
50
+ def step_count; steps.size; end
51
+
52
+ def run_step!(step_number, message)
53
+ send(step(step_number), message)
61
54
  end
62
55
 
63
- def done!
64
- update_current_step(0)
65
- store!(:is_active, false)
56
+ def skip_to_next_step!
57
+ next_step!
66
58
  end
67
59
 
68
- def perform_current_step(bot)
69
- current_action.(bot)
60
+ def has_steps?
61
+ current_user.conversation.step < step_count - 1
70
62
  end
71
63
 
72
- private
64
+ def next_step!
65
+ has_steps? ? current_user.next_step! : done!
66
+ end
73
67
 
74
- def completed_last_step?
75
- current_step == @steps.size
68
+ def next_step_now!
69
+ if has_steps?
70
+ current_user.next_step!
71
+ run_step!(current_user.conversation.step, message)
72
+ else
73
+ raise ArgumentError, "We're out of steps, shouldn't be skipping: #{self}"
74
+ end
76
75
  end
77
76
 
78
- def update_current_step(n)
79
- store!(:_current_step, n)
77
+ def repeat_step!
78
+ # do nothing, leave state the same
80
79
  end
81
80
 
82
- def current_action
83
- @steps[current_step].callback
81
+ def done!
82
+ current_user.end_conversation!
84
83
  end
85
84
  end
86
85
  end
@@ -0,0 +1,20 @@
1
+ class Sinbotra::Bot
2
+ class RedisStore
3
+ def get(id)
4
+ res = $redis.get(make_key(id))
5
+ Marshal.load(res) unless res.nil?
6
+ end
7
+ alias_method :[], :get
8
+
9
+ def set(id, obj)
10
+ raw = Marshal.dump(obj)
11
+ $redis.set(make_key(id), raw)
12
+ end
13
+ alias_method :[]=, :set
14
+
15
+ def make_key(id)
16
+ raise NotImplementedError, "make_key needs to be implemented by children"
17
+ end
18
+ end
19
+ end
20
+
@@ -1,59 +1,22 @@
1
1
  require "securerandom"
2
- require "sinbotra/bot/default_message_history"
3
2
 
4
3
  class Sinbotra::Bot
5
4
  class User
6
- attr_reader :id
7
- attr_reader :session_id
8
- attr_reader :current_message
9
- attr_reader :conversations
10
- attr_reader :history
11
-
12
- def initialize(id, history=Sinbotra::Bot::DefaultMessageHistory.new)
13
- @id = id
14
- @history = history
15
- @session_id = nil
16
- @conversations = []
17
- start_session
18
- end
19
-
20
- def handle_message(msg)
21
- @current_message = msg
22
- history.add_message(msg)
23
- end
24
-
25
- # Sessions
5
+ Conversation = Struct.new(:id, :step)
26
6
 
27
- def logged_in?() !@session_id.nil? end
28
-
29
- # remove user while not removing history
30
- def destroy
31
- end_session
32
- end
33
-
34
- # Conversations
35
-
36
- def start_conversation(convo)
37
- conversations.unshift(convo)
38
- convo.start
39
- end
40
-
41
- def in_conversation?
42
- !current_conversation.nil?
43
- end
7
+ attr_reader :id
8
+ attr_reader :conversation
44
9
 
45
- def current_conversation
46
- conversations.find { |c| c.active? }
10
+ def initialize(id)
11
+ @id = id
47
12
  end
48
13
 
49
- private
50
-
51
- def start_session
52
- @session_id = SecureRandom.uuid
14
+ def end_conversation
15
+ @conversation = nil
53
16
  end
54
17
 
55
- def end_session
56
- @session_id = nil
18
+ def update_conversation(id, step)
19
+ @conversation = Conversation.new(id, step)
57
20
  end
58
21
  end
59
22
  end
@@ -1,22 +1,48 @@
1
1
  class Sinbotra::Bot
2
2
  class UserRepo
3
3
  class << self
4
- def connect
5
- @users ||= {}
4
+ def connect(store=UserStore.new)
5
+ @users ||= store
6
6
  end
7
7
 
8
- def find_or_create(user_class, id)
8
+ def find_or_create(id)
9
9
  user = @users[id]
10
10
  if user.nil?
11
- user = user_class.new(id)
12
- @users[id] = user
11
+ user = create(id)
13
12
  end
14
13
  user
15
14
  end
16
15
 
16
+ def create(id)
17
+ user = Sinbotra::Bot::User.new(id)
18
+ update(user)
19
+ user
20
+ end
21
+
22
+ def next_step!(user)
23
+ step = user.conversation.step + 1
24
+ update_conversation(user, user.conversation.id, step)
25
+ end
26
+
27
+ def start_conversation(user_id, convo_id)
28
+ user = find_or_create(user_id)
29
+ user = update_conversation(user: user, convo_id: convo_id, step: 0)
30
+ user
31
+ end
32
+
33
+ def update_conversation(user:, convo_id:, step:)
34
+ user.update_conversation(convo_id, step)
35
+ update(user)
36
+ user
37
+ end
38
+
39
+ def update(user)
40
+ @users[user.id] = user
41
+ end
42
+
17
43
  def messages(id)
18
- user = @users[id]
19
- user.history.all
44
+ # Get messages from message store
45
+ $logger.debug("WIP user messages retrieval")
20
46
  end
21
47
  end
22
48
  end