bot 0.0.1 → 0.0.16

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/bot/bot_controller.rb +23 -0
  3. data/lib/bot.rb +7 -1
  4. data/lib/bot/adapter.rb +8 -0
  5. data/lib/bot/adapters/base.rb +15 -0
  6. data/lib/bot/adapters/kik.rb +26 -0
  7. data/lib/bot/adapters/test.rb +19 -0
  8. data/lib/bot/configuration.rb +21 -0
  9. data/lib/bot/engine.rb +4 -0
  10. data/lib/bot/handler.rb +46 -0
  11. data/lib/bot/responder.rb +96 -0
  12. data/lib/bot/responder_chain.rb +15 -0
  13. data/lib/bot/route_extensions.rb +7 -0
  14. data/lib/bot/rspec.rb +23 -0
  15. data/lib/bot/rspec/fixnum_helper.rb +13 -0
  16. data/lib/bot/rspec/matchers.rb +41 -0
  17. data/lib/bot/rspec/syntax.rb +39 -0
  18. data/lib/bot/version.rb +1 -1
  19. data/lib/generators/bot/install/install_generator.rb +24 -0
  20. data/lib/generators/bot/install/templates/application_handler.rb +7 -0
  21. data/lib/generators/bot/install/templates/application_responder.rb +11 -0
  22. data/lib/generators/bot/install/templates/initializer.rb +3 -0
  23. data/lib/generators/bot/responder/responder_generator.rb +25 -0
  24. data/lib/generators/rspec/install_generator.rb +14 -0
  25. data/lib/generators/rspec/responder_generator.rb +14 -0
  26. data/lib/generators/rspec/templates/application_handler_spec.rb +5 -0
  27. data/lib/generators/rspec/templates/responder_spec.rb +17 -0
  28. data/test/dummy/app/bot/application_handler.rb +5 -0
  29. data/test/dummy/app/bot/application_responder.rb +2 -0
  30. data/test/dummy/app/bot/responders/multi.rb +11 -0
  31. data/test/dummy/app/bot/responders/scan.rb +11 -0
  32. data/test/dummy/app/bot/responders/test.rb +11 -0
  33. data/test/dummy/app/bot/responders/text.rb +9 -0
  34. data/test/dummy/config/environments/development.rb +2 -0
  35. data/test/dummy/config/initializers/bot.rb +3 -0
  36. data/test/dummy/config/routes.rb +1 -2
  37. data/test/dummy/db/development.sqlite3 +0 -0
  38. data/test/dummy/db/schema.rb +16 -0
  39. data/test/dummy/db/test.sqlite3 +0 -0
  40. data/test/dummy/log/development.log +4 -0
  41. data/test/dummy/log/test.log +2345 -0
  42. data/test/integration/navigation_test.rb +24 -1
  43. data/test/test_helper.rb +2 -0
  44. metadata +90 -8
  45. data/app/controllers/bot/kik_controller.rb +0 -8
  46. data/config/routes.rb +0 -3
  47. data/lib/bot/message_handler.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 73903dfd956f72bd8b5027f5fc0f426167781940
4
- data.tar.gz: 88d59d632dc0ec52bd5a1ebc5b4d1e8a1ed812e8
3
+ metadata.gz: 07f61d84f72deea7fe40bb6f1780276a64cd7460
4
+ data.tar.gz: 1c24da37fa185a9003c4b8bd8bd4fcc5c6667cca
5
5
  SHA512:
6
- metadata.gz: e917449bdf62fe2588fae9af8b625bc7ace0b2965fa27436db6c3f07bed00e3f2ed2f6f6d20476d81af048832b0f600e9d20bf60a58d902791bb0895a0baf7a1
7
- data.tar.gz: 21ca504f96bed73e007506cb00e5399c9522195d358e468513ecab73a8b1afea57bf8edc3946b7003b5c0a20062dcadadae7087f44c1dd6803acb18f74b7cfd2
6
+ metadata.gz: 518171c0bb723d1a2f5f427aae51ea30da0b8cab462ab533042e58e3672ca8af67d83e339af6cf00d9c942759823941cf4f97d800f142af4e452cc519a20a124
7
+ data.tar.gz: a8083b8140a65a80b628cad46e5f16f1c59bf0f0cea3002716d177dff7d2fb93b23a24914359358145518813d4d156874a1acc4aed1e60e0cf23b2cbc7b95f9a
@@ -0,0 +1,23 @@
1
+ class Bot::BotController < ActionController::Base
2
+ def notify
3
+ @responses = bot_handler.handle(messages).compact
4
+
5
+ adapter.send_messages(@responses) if @responses.present?
6
+
7
+ render json: []
8
+ end
9
+
10
+ private
11
+
12
+ def messages
13
+ Array.wrap(params[:messages])
14
+ end
15
+
16
+ def adapter
17
+ Bot.configuration.adapter
18
+ end
19
+
20
+ def bot_handler
21
+ params[:bot]
22
+ end
23
+ end
data/lib/bot.rb CHANGED
@@ -1,5 +1,11 @@
1
1
  require "bot/engine"
2
- require "bot/message_handler"
2
+ require "bot/handler"
3
+ require "bot/responder"
4
+ require "bot/route_extensions"
5
+ require "bot/adapter"
6
+ require "bot/configuration"
7
+ require "bot/responder_chain"
8
+ require "bot/rspec.rb"
3
9
 
4
10
  module Bot
5
11
  end
@@ -0,0 +1,8 @@
1
+ require "bot/adapters/base"
2
+ require "bot/adapters/kik"
3
+ require "bot/adapters/test"
4
+
5
+ module Bot
6
+ module Adapter
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module Bot
2
+ module Adapter
3
+ class Base
4
+ attr_reader :config
5
+
6
+ def initialize(config=nil)
7
+ @config = config
8
+ end
9
+
10
+ def send_messages(messages)
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ require 'rest-client'
2
+
3
+ module Bot
4
+ module Adapter
5
+ class Kik < Base
6
+
7
+ AUTH_URL = "https://auth.kik.com/verification/v1/check"
8
+ MESSAGING_URL = "https://engine.apikik.com/api/v1/message"
9
+
10
+ def send_messages(messages)
11
+ Rails.logger.error "\n\n\nSending:\n"
12
+ Rails.logger.error messages.to_json
13
+ Rails.logger.error "\n\n\n"
14
+
15
+ RestClient::Request.execute({
16
+ method: :post,
17
+ user: config[:bot_user],
18
+ password: config[:bot_token],
19
+ url: MESSAGING_URL,
20
+ payload: { messages: messages }.to_json,
21
+ headers: { content_type: :json }
22
+ })
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module Bot
2
+ module Adapter
3
+ class Test < Base
4
+ @@sent_messages = []
5
+
6
+ def send_messages(messages)
7
+ @@sent_messages += Array.wrap(messages)
8
+ end
9
+
10
+ def self.sent_messages
11
+ @@sent_messages
12
+ end
13
+
14
+ def self.empty_messages
15
+ @@sent_messages = []
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Bot
2
+ @@configuration = nil
3
+
4
+ def self.configure
5
+ @@configuration = Configuration.new
6
+
7
+ if block_given?
8
+ yield configuration
9
+ end
10
+
11
+ configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @@configuration || configure
16
+ end
17
+
18
+ class Configuration
19
+ attr_accessor :adapter
20
+ end
21
+ end
@@ -1,5 +1,9 @@
1
1
  module Bot
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Bot
4
+
5
+ initializer 'bot.routes', after: 'action_dispatch.prepare_dispatcher' do |app|
6
+ ActionDispatch::Routing::Mapper.send :include, ::Bot::RouteExtensions
7
+ end
4
8
  end
5
9
  end
@@ -0,0 +1,46 @@
1
+ module Bot
2
+ class Handler
3
+ @@responder_chain = nil
4
+
5
+ def self.use(responder)
6
+ responder_chain.add(responder)
7
+ end
8
+
9
+ def self.handle(messages)
10
+ new.handle(messages)
11
+ end
12
+
13
+ def handle(messages)
14
+ messages.flat_map { |m| handle_message(m) }
15
+ end
16
+
17
+ def execute_chain(message, responses, user)
18
+ responder_chain.responders.each do |responder|
19
+ responder = responder.new(message, user, responses, self)
20
+ if responder.can_respond_to_type?(message['type']) && responder.can_handle?
21
+ responses << responder.handle
22
+ break
23
+ end
24
+ end
25
+ responses
26
+ end
27
+
28
+ def responder_chain
29
+ @@responder_chain ||= Bot::ResponderChain.new
30
+ end
31
+
32
+ def self.responder_chain
33
+ @@responder_chain ||= Bot::ResponderChain.new
34
+ end
35
+
36
+ protected
37
+
38
+ def handle_message(message)
39
+ user = user_for(message)
40
+ execute_chain(message, [], user).flatten
41
+ end
42
+
43
+ def user_for(message)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,96 @@
1
+ module Bot
2
+ class Responder
3
+ include ActionView::Helpers::TextHelper
4
+ include ActionView::Helpers::DateHelper
5
+
6
+ class_attribute :respond_to_types
7
+
8
+ attr_reader :message, :user, :responses, :handler
9
+
10
+ def self.respond_to(*types)
11
+ new_types = (respond_to_types || []).dup
12
+ types.each { |type| new_types << type.to_s }.uniq
13
+ self.respond_to_types = new_types.freeze
14
+ end
15
+
16
+ def initialize(message, user, responses, handler)
17
+ @message = message
18
+ @user = user
19
+ @responses = responses
20
+ @handler = handler
21
+ end
22
+
23
+ def can_respond_to_type?(type)
24
+ valid_message_types = self.class.respond_to_types || ['text']
25
+ valid_message_types.include?(type)
26
+ end
27
+
28
+ def can_handle?
29
+ !responses.count
30
+ end
31
+
32
+ def handle
33
+ raise NotImplementedError
34
+ end
35
+
36
+ protected
37
+
38
+ def text_response(message_text, suggested_responses=false)
39
+ text_response = {
40
+ 'type' => 'text',
41
+ 'to' => message['from'],
42
+ 'body' => message_text,
43
+ 'typeTime' => 0
44
+ }
45
+ text_response['suggestedResponses'] = suggested_responses if suggested_responses
46
+ text_response
47
+ end
48
+
49
+ def photo_response(photo_url, suggested_responses=false)
50
+ photo_response = {
51
+ 'type' => 'gallery',
52
+ 'to' => message['from'],
53
+ 'picUrl' => photo_url
54
+ }
55
+ photo_response['suggestedResponses'] = suggested_responses if suggested_responses
56
+ photo_response
57
+ end
58
+
59
+ def video_response(video_id, suggested_responses=false)
60
+ video_response = {
61
+ 'type' => 'video',
62
+ 'to' => message['from'],
63
+ 'videoId' => video_id,
64
+ 'muted' => true,
65
+ 'autoplay' => true
66
+ }
67
+ video_response['suggestedResponses'] = suggested_responses if suggested_responses
68
+ video_response
69
+ end
70
+
71
+ def delay(message, delay)
72
+ message['delay'] = delay
73
+ message
74
+ end
75
+
76
+ def match_message(match)
77
+ if match.is_a?(Regexp)
78
+ match.match(message['body'])
79
+ else
80
+ message['body'].upcase == match.upcase
81
+ end
82
+ end
83
+
84
+ def no_responses?
85
+ responses.empty?
86
+ end
87
+
88
+ def reexecute_with(current_response, new_message=false)
89
+ handler.execute_chain(new_message || message, Array.wrap(current_response), user)
90
+ end
91
+
92
+ def scan_data
93
+ JSON.parse(message['data'])
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ module Bot
2
+ class ResponderChain
3
+ def initialize
4
+ @responders = []
5
+ end
6
+
7
+ def add(responder)
8
+ @responders += Array.wrap(responder)
9
+ end
10
+
11
+ def responders
12
+ @responders.map {|r| r.to_s.constantize }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Bot
2
+ module RouteExtensions
3
+ def mount_bot(mount_point='/bot/notify', handler=ApplicationHandler)
4
+ post mount_point, to: 'bot/bot#notify', bot: handler
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ require "bot/rspec/syntax.rb"
2
+ require "bot/rspec/matchers.rb"
3
+ require "bot/rspec/fixnum_helper.rb"
4
+
5
+ module Bot
6
+ module Rspec
7
+ include Syntax
8
+ include Matchers
9
+
10
+ Fixnum.send(:include, FixnumHelper)
11
+
12
+ def self.included(base)
13
+ if base.metadata[:type] == :responder
14
+ base.let(:username) { "user" } unless base.respond_to? :username
15
+ base.let(:user) { create(:user) } unless base.respond_to? :user
16
+ base.let(:message) { incoming_message(user.username) } unless base.respond_to? :message
17
+ base.let(:handler) { class_double("Handler") } unless base.respond_to? :handler
18
+ base.let(:responses) { [] } unless base.respond_to? :responses
19
+ base.let(:responder) { base.described_class.new(message, user, responses, handler) } unless base.respond_to? :responder
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module Bot
2
+ module Rspec
3
+ module FixnumHelper
4
+ def message
5
+ self
6
+ end
7
+
8
+ def messages
9
+ self
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ require 'rspec/expectations'
2
+
3
+ module Bot
4
+ module Rspec
5
+ module Matchers
6
+ RSpec::Matchers.define :be_to do |to_user|
7
+ match do |sent_messages|
8
+ Array.wrap(sent_messages).flatten.any? { |msg| msg["to"] == to_user }
9
+ end
10
+ end
11
+
12
+ RSpec::Matchers.define :say do |body|
13
+ match do |sent_messages|
14
+ Array.wrap(sent_messages).flatten.any? { |msg| msg["body"] == body }
15
+ end
16
+ end
17
+
18
+ RSpec::Matchers.define :match_message do |body|
19
+ match do |sent_messages|
20
+ Array.wrap(sent_messages).flatten.any? { |msg| msg["body"].match(body).present? }
21
+ end
22
+ end
23
+
24
+ RSpec::Matchers.define :send_a_message_like do |expected_message|
25
+ match do |sent_messages|
26
+ Array.wrap(sent_messages).flatten.keep_if { |msg| matches_expected?(msg, expected_message) }.present?
27
+ end
28
+
29
+ def matches_expected?(sent, expected)
30
+ !expected.reject { |k, v| sent[k] == v }.present?
31
+ end
32
+ end
33
+
34
+ RSpec::Matchers.define :send do |expected_count|
35
+ match do |sent_messages|
36
+ Array.wrap(sent_messages).flatten.count == expected_count
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ module Bot
2
+ module Rspec
3
+ module Syntax
4
+ def incoming_message(from, options={})
5
+ {
6
+ "from" => from,
7
+ "body" => "No Message",
8
+ "readReceiptRequested" => true,
9
+ "timestamp" => Time.now.to_i * 1000,
10
+ "type" => "text",
11
+ "id" => "07c6eb77-e01c-439a-80e7-3583dd784de1"
12
+ }.merge(options.stringify_keys)
13
+ end
14
+
15
+ def incoming_scan(from, options={})
16
+ {
17
+ "from" => from,
18
+ "timestamp" => 1457449114952,
19
+ "data" => "{}",
20
+ "mention" => nil,
21
+ "participants" => [from],
22
+ "readReceiptRequested" => false,
23
+ "type" => "scan-data",
24
+ "id" => "e6303158-ca60-4332-a600-236c5de4fedb",
25
+ "chatId" => "d68a7ab10a1526ac9682b764e2784080bb1cb52fc82c8f9c10c7d0534741fc93"
26
+ }.merge(options.stringify_keys)
27
+ end
28
+
29
+ def outgoing_message(to, options={})
30
+ {
31
+ "to" => to,
32
+ "body" => "No Message",
33
+ "type" => "text",
34
+ "typeTime" => 0
35
+ }.merge(options.stringify_keys)
36
+ end
37
+ end
38
+ end
39
+ end