bot_mob 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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