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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -0
  3. data/.gitignore +0 -1
  4. data/.rubocop.yml +16 -0
  5. data/.travis.yml +1 -1
  6. data/Gemfile +0 -2
  7. data/LICENSE.txt +21 -0
  8. data/README.md +5 -4
  9. data/bin/console +8 -4
  10. data/bin/mob +4 -0
  11. data/bot_mob.gemspec +8 -14
  12. data/examples/fizz_buzz_bot.rb +30 -0
  13. data/examples/hello_bot.rb +14 -0
  14. data/examples/server.rb +7 -0
  15. data/lib/bot_mob.rb +46 -32
  16. data/lib/bot_mob/bot.rb +146 -29
  17. data/lib/bot_mob/command.rb +38 -0
  18. data/lib/bot_mob/connection.rb +18 -50
  19. data/lib/bot_mob/core_ext/hash.rb +148 -0
  20. data/lib/bot_mob/core_ext/string.rb +8 -0
  21. data/lib/bot_mob/environment.rb +25 -0
  22. data/lib/bot_mob/inbound_message.rb +13 -32
  23. data/lib/bot_mob/networks/roaming.rb +12 -0
  24. data/lib/bot_mob/networks/roaming/connection.rb +34 -0
  25. data/lib/bot_mob/networks/roaming/outbound_message.rb +15 -0
  26. data/lib/bot_mob/networks/slack.rb +11 -0
  27. data/lib/bot_mob/networks/slack/connection.rb +37 -0
  28. data/lib/bot_mob/networks/slack/outbound_message.rb +18 -0
  29. data/lib/bot_mob/outbound_message.rb +23 -10
  30. data/lib/bot_mob/public/console.js +10 -0
  31. data/lib/bot_mob/roster.rb +8 -80
  32. data/lib/bot_mob/server.rb +47 -0
  33. data/lib/bot_mob/version.rb +1 -1
  34. data/lib/bot_mob/views/authorize.erb +1 -0
  35. data/lib/bot_mob/views/console.erb +6 -0
  36. data/lib/bot_mob/views/index.erb +1 -0
  37. data/lib/bot_mob/views/layout.erb +11 -0
  38. metadata +50 -110
  39. data/lib/bot_mob/ambassador.rb +0 -34
  40. data/lib/bot_mob/application.rb +0 -43
  41. data/lib/bot_mob/authority.rb +0 -41
  42. data/lib/bot_mob/development/ambassador.rb +0 -21
  43. data/lib/bot_mob/development/connection.rb +0 -34
  44. data/lib/bot_mob/development/inbound_message.rb +0 -17
  45. data/lib/bot_mob/errors.rb +0 -12
  46. data/lib/bot_mob/install.rb +0 -16
  47. data/lib/bot_mob/nil_connection.rb +0 -29
  48. data/lib/bot_mob/slack.rb +0 -10
  49. data/lib/bot_mob/slack/ambassador.rb +0 -58
  50. data/lib/bot_mob/slack/connection.rb +0 -87
  51. data/lib/bot_mob/slack/inbound_message.rb +0 -52
  52. data/lib/bot_mob/wire.rb +0 -69
@@ -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
@@ -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
@@ -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,21 +0,0 @@
1
- module BotMob
2
- module Development
3
- class Ambassador < BotMob::Ambassador
4
- def self.setup(args)
5
- new
6
- end
7
-
8
- def success?
9
- true
10
- end
11
-
12
- def external_id
13
- 'dev'
14
- end
15
-
16
- def token
17
- 'na'
18
- end
19
- end
20
- end
21
- 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
@@ -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
@@ -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
@@ -1,10 +0,0 @@
1
- require 'bot_mob'
2
- require 'slack-ruby-client'
3
-
4
- module BotMob
5
- module Slack
6
- autoload :Ambassador, 'bot_mob/slack/ambassador'
7
- autoload :Connection, 'bot_mob/slack/connection'
8
- autoload :InboundMessage, 'bot_mob/slack/inbound_message'
9
- end
10
- end
@@ -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