bot 0.0.1 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/bot/bot_controller.rb +23 -0
- data/lib/bot.rb +7 -1
- data/lib/bot/adapter.rb +8 -0
- data/lib/bot/adapters/base.rb +15 -0
- data/lib/bot/adapters/kik.rb +26 -0
- data/lib/bot/adapters/test.rb +19 -0
- data/lib/bot/configuration.rb +21 -0
- data/lib/bot/engine.rb +4 -0
- data/lib/bot/handler.rb +46 -0
- data/lib/bot/responder.rb +96 -0
- data/lib/bot/responder_chain.rb +15 -0
- data/lib/bot/route_extensions.rb +7 -0
- data/lib/bot/rspec.rb +23 -0
- data/lib/bot/rspec/fixnum_helper.rb +13 -0
- data/lib/bot/rspec/matchers.rb +41 -0
- data/lib/bot/rspec/syntax.rb +39 -0
- data/lib/bot/version.rb +1 -1
- data/lib/generators/bot/install/install_generator.rb +24 -0
- data/lib/generators/bot/install/templates/application_handler.rb +7 -0
- data/lib/generators/bot/install/templates/application_responder.rb +11 -0
- data/lib/generators/bot/install/templates/initializer.rb +3 -0
- data/lib/generators/bot/responder/responder_generator.rb +25 -0
- data/lib/generators/rspec/install_generator.rb +14 -0
- data/lib/generators/rspec/responder_generator.rb +14 -0
- data/lib/generators/rspec/templates/application_handler_spec.rb +5 -0
- data/lib/generators/rspec/templates/responder_spec.rb +17 -0
- data/test/dummy/app/bot/application_handler.rb +5 -0
- data/test/dummy/app/bot/application_responder.rb +2 -0
- data/test/dummy/app/bot/responders/multi.rb +11 -0
- data/test/dummy/app/bot/responders/scan.rb +11 -0
- data/test/dummy/app/bot/responders/test.rb +11 -0
- data/test/dummy/app/bot/responders/text.rb +9 -0
- data/test/dummy/config/environments/development.rb +2 -0
- data/test/dummy/config/initializers/bot.rb +3 -0
- data/test/dummy/config/routes.rb +1 -2
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +16 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +4 -0
- data/test/dummy/log/test.log +2345 -0
- data/test/integration/navigation_test.rb +24 -1
- data/test/test_helper.rb +2 -0
- metadata +90 -8
- data/app/controllers/bot/kik_controller.rb +0 -8
- data/config/routes.rb +0 -3
- data/lib/bot/message_handler.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07f61d84f72deea7fe40bb6f1780276a64cd7460
|
4
|
+
data.tar.gz: 1c24da37fa185a9003c4b8bd8bd4fcc5c6667cca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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
|
data/lib/bot/adapter.rb
ADDED
@@ -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
|
data/lib/bot/engine.rb
CHANGED
data/lib/bot/handler.rb
ADDED
@@ -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
|
data/lib/bot/rspec.rb
ADDED
@@ -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,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
|