bot_mob 0.2.2 → 0.3.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 +4 -4
- data/.env +1 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +16 -0
- data/.travis.yml +1 -1
- data/Gemfile +0 -2
- data/LICENSE.txt +21 -0
- data/README.md +5 -4
- data/bin/console +8 -4
- data/bin/mob +4 -0
- data/bot_mob.gemspec +8 -14
- data/examples/fizz_buzz_bot.rb +30 -0
- data/examples/hello_bot.rb +14 -0
- data/examples/server.rb +7 -0
- data/lib/bot_mob.rb +46 -32
- data/lib/bot_mob/bot.rb +146 -29
- data/lib/bot_mob/command.rb +38 -0
- data/lib/bot_mob/connection.rb +18 -50
- data/lib/bot_mob/core_ext/hash.rb +148 -0
- data/lib/bot_mob/core_ext/string.rb +8 -0
- data/lib/bot_mob/environment.rb +25 -0
- data/lib/bot_mob/inbound_message.rb +13 -32
- data/lib/bot_mob/networks/roaming.rb +12 -0
- data/lib/bot_mob/networks/roaming/connection.rb +34 -0
- data/lib/bot_mob/networks/roaming/outbound_message.rb +15 -0
- data/lib/bot_mob/networks/slack.rb +11 -0
- data/lib/bot_mob/networks/slack/connection.rb +37 -0
- data/lib/bot_mob/networks/slack/outbound_message.rb +18 -0
- data/lib/bot_mob/outbound_message.rb +23 -10
- data/lib/bot_mob/public/console.js +10 -0
- data/lib/bot_mob/roster.rb +8 -80
- data/lib/bot_mob/server.rb +47 -0
- data/lib/bot_mob/version.rb +1 -1
- data/lib/bot_mob/views/authorize.erb +1 -0
- data/lib/bot_mob/views/console.erb +6 -0
- data/lib/bot_mob/views/index.erb +1 -0
- data/lib/bot_mob/views/layout.erb +11 -0
- metadata +50 -110
- data/lib/bot_mob/ambassador.rb +0 -34
- data/lib/bot_mob/application.rb +0 -43
- data/lib/bot_mob/authority.rb +0 -41
- data/lib/bot_mob/development/ambassador.rb +0 -21
- data/lib/bot_mob/development/connection.rb +0 -34
- data/lib/bot_mob/development/inbound_message.rb +0 -17
- data/lib/bot_mob/errors.rb +0 -12
- data/lib/bot_mob/install.rb +0 -16
- data/lib/bot_mob/nil_connection.rb +0 -29
- data/lib/bot_mob/slack.rb +0 -10
- data/lib/bot_mob/slack/ambassador.rb +0 -58
- data/lib/bot_mob/slack/connection.rb +0 -87
- data/lib/bot_mob/slack/inbound_message.rb +0 -52
- data/lib/bot_mob/wire.rb +0 -69
data/lib/bot_mob/ambassador.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
class Ambassador
|
3
|
-
# ## BotMob::Ambassador.setup
|
4
|
-
#
|
5
|
-
# Ambassador setup delegates the ambassadorship
|
6
|
-
# to the correct network.
|
7
|
-
def self.setup(network, args = {})
|
8
|
-
invalid_network_error(network) unless BotMob.valid_network?(network)
|
9
|
-
|
10
|
-
ambassador = network_delegate(network)
|
11
|
-
invalid_delegate_error(network) unless ambassador.respond_to?(:setup)
|
12
|
-
|
13
|
-
ambassador.setup(args)
|
14
|
-
end
|
15
|
-
|
16
|
-
def data
|
17
|
-
{}
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def self.network_delegate(network)
|
23
|
-
BotMob.networks[network].const_get('Ambassador')
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.invalid_network_error(network)
|
27
|
-
raise BotMob::InvalidNetworkError, "The #{network.inspect} network has not been mounted"
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.invalid_delegate_error(network)
|
31
|
-
raise BotMob::InvalidAmbassadorError, "The #{network.inspect} ambassador must implement the `setup` method"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/bot_mob/application.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module BotMob
|
4
|
-
# ## BotMob::Application
|
5
|
-
#
|
6
|
-
# This is the base application class for BotMob.
|
7
|
-
class Application
|
8
|
-
extend Forwardable
|
9
|
-
|
10
|
-
def_delegators :roster, :register
|
11
|
-
|
12
|
-
def start!
|
13
|
-
BotMob.logger.info("=> BotMob #{BotMob::VERSION} application starting")
|
14
|
-
|
15
|
-
roster.load
|
16
|
-
wire.listen { |auth| roster.connect(auth) }
|
17
|
-
|
18
|
-
loop do
|
19
|
-
sleep(10)
|
20
|
-
# Avoid polling at all for the time being
|
21
|
-
# roster.load if roster.available? && !wire.available?
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# ## Roster
|
28
|
-
#
|
29
|
-
# The `roster` is the object that keeps track of the registration
|
30
|
-
# and participation of any various bots active on your mob
|
31
|
-
def roster
|
32
|
-
@roster ||= BotMob::Roster.new
|
33
|
-
end
|
34
|
-
|
35
|
-
# ## Wire
|
36
|
-
#
|
37
|
-
# The `wire` is the object that allows for other processes to
|
38
|
-
# tell your bot mob application to take some action.
|
39
|
-
def wire
|
40
|
-
@wire ||= BotMob::Wire.new
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/lib/bot_mob/authority.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
# ## BotMob::Authority
|
3
|
-
#
|
4
|
-
# The Authority class will process an access args
|
5
|
-
# and retrieve an access token from the specified
|
6
|
-
# network
|
7
|
-
#
|
8
|
-
# Example Invocation:
|
9
|
-
#
|
10
|
-
# BotMob::Authority.new(:slack, code: params[:code])
|
11
|
-
class Authority
|
12
|
-
attr_reader :network
|
13
|
-
|
14
|
-
def initialize(network, args = {})
|
15
|
-
@network = network
|
16
|
-
@args = args
|
17
|
-
end
|
18
|
-
|
19
|
-
def process(bot_class)
|
20
|
-
return unless ambassador.success?
|
21
|
-
|
22
|
-
# wire.publish(network: :slack, external_id: 'foo', bot_class: 'MockBot', data: { token: ENV['IOBOT_TOKEN'] } )
|
23
|
-
wire.publish({
|
24
|
-
network: network,
|
25
|
-
external_id: ambassador.external_id,
|
26
|
-
bot_class: bot_class.to_s,
|
27
|
-
data: ambassador.data || {}
|
28
|
-
})
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def ambassador
|
34
|
-
@ambassador ||= BotMob::Ambassador.setup(network, @args)
|
35
|
-
end
|
36
|
-
|
37
|
-
def wire
|
38
|
-
@wire ||= BotMob::Wire.new
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
module Development
|
3
|
-
class Connection < BotMob::Connection
|
4
|
-
|
5
|
-
def initialize(bot, options = {})
|
6
|
-
@bot = bot
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.setup(bot, options = {})
|
10
|
-
new(bot, options)
|
11
|
-
end
|
12
|
-
|
13
|
-
def deliver(outbound_message, options = {})
|
14
|
-
BotMob.logger.info " Dev Connection Sent: #{outbound_message.text.inspect}"
|
15
|
-
end
|
16
|
-
|
17
|
-
def connect
|
18
|
-
BotMob.logger.info 'Connecting... '
|
19
|
-
end
|
20
|
-
|
21
|
-
def reconnect
|
22
|
-
BotMob.logger.info 'Reconnecting... '
|
23
|
-
end
|
24
|
-
|
25
|
-
def refresh
|
26
|
-
BotMob.logger.info 'Refreshing... '
|
27
|
-
end
|
28
|
-
|
29
|
-
def client
|
30
|
-
self
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
module Development
|
3
|
-
class InboundMessage < BotMob::InboundMessage
|
4
|
-
def initialize(params)
|
5
|
-
@text = params[:text]
|
6
|
-
@network = params[:network]
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.parse(**params)
|
10
|
-
new({
|
11
|
-
network: params[:network],
|
12
|
-
text: params[:text]
|
13
|
-
})
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/bot_mob/errors.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
class InvalidAmbassadorError < StandardError; end
|
3
|
-
class InvalidConnectionError < StandardError; end
|
4
|
-
class InvalidInboundMessageError < StandardError; end
|
5
|
-
|
6
|
-
class InvalidNetworkError < StandardError; end
|
7
|
-
class InvalidClientIdError < StandardError; end
|
8
|
-
class InvalidClientSecretError < StandardError; end
|
9
|
-
class InvalidCodeError < StandardError; end
|
10
|
-
class ConnectionNotEstablished < StandardError; end
|
11
|
-
class UnregisteredBotError < StandardError; end
|
12
|
-
end
|
data/lib/bot_mob/install.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
class Install < ActiveRecord::Base
|
3
|
-
self.table_name = "bot_mob_installs"
|
4
|
-
|
5
|
-
def self.registered_bot(auth)
|
6
|
-
where(bot_class: auth['bot_class'], external_id: auth['external_id'])
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.create_with_auth(auth)
|
10
|
-
registered_bot(auth).first_or_initialize.tap do |i|
|
11
|
-
i.data = auth['data']
|
12
|
-
i.save!
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
class NilConnection
|
3
|
-
|
4
|
-
def initialize(bot)
|
5
|
-
@bot = bot
|
6
|
-
end
|
7
|
-
|
8
|
-
def deliver(outbound_message, options = {})
|
9
|
-
BotMob.logger.info '! Missing connection attempting to deliver... '
|
10
|
-
BotMob.logger.info " Message sent: #{outbound_message.text.inspect}"
|
11
|
-
end
|
12
|
-
|
13
|
-
def connect
|
14
|
-
BotMob.logger.info '! Missing connection attempting to connect... '
|
15
|
-
end
|
16
|
-
|
17
|
-
def reconnect
|
18
|
-
BotMob.logger.info '! Missing connection attempting to reconnect... '
|
19
|
-
end
|
20
|
-
|
21
|
-
def refresh
|
22
|
-
BotMob.logger.info '! Missing connection attempting to refresh... '
|
23
|
-
end
|
24
|
-
|
25
|
-
def client
|
26
|
-
self
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/lib/bot_mob/slack.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
module Slack
|
3
|
-
class Ambassador < BotMob::Ambassador
|
4
|
-
def initialize(code)
|
5
|
-
@code = code
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.setup(args)
|
9
|
-
new(args[:code])
|
10
|
-
end
|
11
|
-
|
12
|
-
def token
|
13
|
-
oauth_response.bot_access_token
|
14
|
-
end
|
15
|
-
|
16
|
-
def external_id
|
17
|
-
oauth_response.bot_user_id
|
18
|
-
end
|
19
|
-
|
20
|
-
def data
|
21
|
-
{ token: token }
|
22
|
-
end
|
23
|
-
|
24
|
-
def success?
|
25
|
-
!!(external_id && token)
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def oauth_response
|
31
|
-
return @oauth_response if defined?(@oauth_response)
|
32
|
-
|
33
|
-
response = client.oauth_access({
|
34
|
-
code: @code,
|
35
|
-
client_id: ENV['SLACK_CLIENT_ID'],
|
36
|
-
client_secret: ENV['SLACK_CLIENT_SECRET']
|
37
|
-
})
|
38
|
-
|
39
|
-
@oauth_response = response.bot
|
40
|
-
rescue ::Slack::Web::Api::Error => e
|
41
|
-
case e.message
|
42
|
-
when 'invalid_client_id'
|
43
|
-
raise BotMob::InvalidClientIdError, 'Invalid SLACK_CLIENT_ID environment variable'
|
44
|
-
when 'bad_client_secret'
|
45
|
-
raise BotMob::InvalidClientSecretError, 'Invalid SLACK_CLIENT_SECRET environment variable'
|
46
|
-
when 'invalid_code'
|
47
|
-
raise BotMob::InvalidCodeError, 'Invalid access code'
|
48
|
-
else
|
49
|
-
raise e
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def client
|
54
|
-
@client ||= ::Slack::Web::Client.new
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
module Slack
|
3
|
-
class Connection < BotMob::Connection
|
4
|
-
attr_reader :bot, :token
|
5
|
-
|
6
|
-
def initialize(bot, options = {})
|
7
|
-
@bot = bot
|
8
|
-
@token = options['token']
|
9
|
-
token.nil? ? set_state(:inactive) : setup
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.setup(bot, options = {})
|
13
|
-
new(bot, options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def deliver(outbound_message, options = {})
|
17
|
-
BotMob.logger.info(" Sending to Slack: \"#{options.inspect}\"\n")
|
18
|
-
BotMob.logger.info(" Text: \"#{outbound_message.text.inspect}\"\n")
|
19
|
-
client.web_client.chat_postMessage(outbound_message.to_h)
|
20
|
-
end
|
21
|
-
|
22
|
-
def connect
|
23
|
-
return unless active?
|
24
|
-
set_state(:connecting)
|
25
|
-
establish
|
26
|
-
end
|
27
|
-
|
28
|
-
def reconnect
|
29
|
-
return unless active?
|
30
|
-
set_state(:reconnecting)
|
31
|
-
renew_client
|
32
|
-
setup
|
33
|
-
establish
|
34
|
-
end
|
35
|
-
|
36
|
-
def refresh
|
37
|
-
return unless active?
|
38
|
-
|
39
|
-
case true
|
40
|
-
when waiting?
|
41
|
-
connect
|
42
|
-
when disconnected?
|
43
|
-
reconnect
|
44
|
-
else
|
45
|
-
BotMob.logger.info "State[#{bot.class.to_s}##{bot.key}] - No Change"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def welcome_message
|
50
|
-
"Successfully connected, '#{client.team.name}' team at https://#{client.team.domain}.slack.com."
|
51
|
-
end
|
52
|
-
|
53
|
-
def client
|
54
|
-
@client ||= renew_client
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def establish
|
60
|
-
client.start_async
|
61
|
-
rescue ::Slack::Web::Api::Error => e
|
62
|
-
BotMob.logger.info e.message
|
63
|
-
set_state(:waiting)
|
64
|
-
end
|
65
|
-
|
66
|
-
def setup
|
67
|
-
set_state(:waiting)
|
68
|
-
client.on(:hello) { set_state(:connected) }
|
69
|
-
client.on(:close) { set_state(:disconnecting) }
|
70
|
-
client.on(:closed) { set_state(:closed) }
|
71
|
-
|
72
|
-
client.on :message do |data|
|
73
|
-
message = BotMob::InboundMessage.parse({
|
74
|
-
network: :slack,
|
75
|
-
format: :websocket
|
76
|
-
}.merge(data))
|
77
|
-
|
78
|
-
bot.receive message
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def renew_client
|
83
|
-
@client = ::Slack::RealTime::Client.new(token: @token)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
module BotMob
|
2
|
-
module Slack
|
3
|
-
class InboundMessage < BotMob::InboundMessage
|
4
|
-
attr_reader :token, :team_id, :team_domain, :params, :format,
|
5
|
-
:channel_id, :channel_name, :external_id, :user_name,
|
6
|
-
:command, :bot_id, :response_url, :ts
|
7
|
-
|
8
|
-
def initialize(params)
|
9
|
-
@format = params[:format] || :websocket
|
10
|
-
@network = params[:network]
|
11
|
-
@params = params
|
12
|
-
send("parse_#{format}".to_sym, @params)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.parse(params)
|
16
|
-
new(params)
|
17
|
-
end
|
18
|
-
|
19
|
-
def human?
|
20
|
-
bot_id.nil?
|
21
|
-
end
|
22
|
-
|
23
|
-
def command?
|
24
|
-
!command.nil?
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def parse_websocket(data)
|
30
|
-
@text = data['text']
|
31
|
-
@channel_id = data['channel_id'] || data['channel']
|
32
|
-
@bot_id = data['bot_id']
|
33
|
-
@team = data['team']
|
34
|
-
@ts = data['ts']
|
35
|
-
@external_id = data['user_id']
|
36
|
-
end
|
37
|
-
|
38
|
-
def parse_webhook(data)
|
39
|
-
@token = data['token']
|
40
|
-
@team_id = data['team_id']
|
41
|
-
@team_domain = data['team_domain']
|
42
|
-
@channel_id = data['channel_id']
|
43
|
-
@channel_name = data['channel_name']
|
44
|
-
@external_id = data['user_id']
|
45
|
-
@user_name = data['user_name']
|
46
|
-
@command = data['command']
|
47
|
-
@text = data['text']
|
48
|
-
@response_url = data['response_url']
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|