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.
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