mos-eisley 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b537a2e7627e6922e3796c2bd8d28d2f6fab2f8f
4
- data.tar.gz: c6e97af433d6d21d85d2d5b2e50af1d27f2691f9
3
+ metadata.gz: 6a35fd6879c702d39965d72c48add4ee90b47fcf
4
+ data.tar.gz: 4238234e33680a356e40d89f4fb6c2f53daa42d9
5
5
  SHA512:
6
- metadata.gz: 61dc0130ad335a21bbf8d1da0eedd965fc02aa238e08c4d7d968eccc63cc7c0bafca1f841d0791bef05a53c1524262a8afe03d02b02cbd18251592b6a74a2af1
7
- data.tar.gz: 38ba1113a4f5b646a5406599c96acdf6006a0eefe2d6c601707a10b76f5ecc6bf55044258dd4557068177d4678b3c9df76f6a0fe44af07ba13cc76e7e7a7b7a3
6
+ metadata.gz: ce68accb9a686f22f76afd09dc9b5cad10b2336e685c0b4eae604497f6a97b021b2cb77db8a60e058659dc9c0bc2d12ebf2e2afb4083c5efb1d9366cb54db836
7
+ data.tar.gz: c4b6901abd0f0cd07961660ab76d5f52c29cf1691f24bc58d8d003cf9c8cde6612441ce1314caa6fe3908057758f252388a592477cf2ff1e930b3c09ce9ae673
data/README.md CHANGED
@@ -1,35 +1,41 @@
1
1
  # mos-eisley
2
2
 
3
- ```markdown
4
3
  [![Gem Version](https://badge.fury.io/rb/mos-eisley.svg)](http://badge.fury.io/rb/mos-eisley) [![Code Climate](https://codeclimate.com/github/kenjij/mos-eisley/badges/gpa.svg)](https://codeclimate.com/github/kenjij/mos-eisley) [![security](https://hakiri.io/github/kenjij/mos-eisley/master.svg)](https://hakiri.io/github/kenjij/mos-eisley/master)
5
- ```
6
4
 
7
- Ruby server for running a Slackbot.
5
+ A Ruby based [Slack app](https://api.slack.com/slack-apps) server. It provides API endpoints to Slack as well as functions to access Slack API and manages events with handlers you create.
8
6
 
9
- ## Requirements
7
+ ## Environment
10
8
 
9
+ - UNIX-like systems
11
10
  - [Ruby](https://www.ruby-lang.org/) >= 2.1
12
-
13
- Powered by [Sinatra](http://www.sinatrarb.com/).
11
+ - [Sinatra](http://www.sinatrarb.com/) ~> 2.0 – for inbound API server
12
+ - [em-http-request](https://github.com/igrigorik/em-http-request) ~> 1.1 – for outbound API access
14
13
 
15
14
  ## Getting Started
16
15
 
17
- ### Install
16
+ Install the gem.
18
17
 
19
- ```
20
- $ gem install mos-eisley
21
- ```
18
+ gem install mos-eisley
19
+
20
+ Create a configuration file and register some handlers. Handlers are your code that gets executed when events are received from Slack. See below for [more details](#).
22
21
 
23
- ### Configure
22
+ Run Mos Eisley.
24
23
 
25
- Create a configuration file following the example below.
24
+ mos-eisley -c config.rb start
25
+
26
+ ## Setup
27
+
28
+ ### Configuration File
29
+
30
+ This is a standard Ruby file and anything can go in it. It'll be executed at the very beginning of app launch, before the HTTP server is started. Here is an example.
26
31
 
27
32
  ```ruby
28
33
  # Configure application logging
29
34
  MosEisley.logger = Logger.new(STDOUT)
30
35
  MosEisley.logger.level = Logger::DEBUG
31
36
 
32
- MosEisley::Config.setup do |c|
37
+ # Main configuration block (MosEisley namespace can be abbrv. to ME)
38
+ ME::Config.setup do |c|
33
39
  # User custom data
34
40
  c.user = {my_data1: 'Something', my_data2: 'Somethingelse'}
35
41
 
@@ -43,14 +49,28 @@ MosEisley::Config.setup do |c|
43
49
  ]
44
50
 
45
51
  # Slack info
46
- c.verification_tokens = [
47
- 'vErIf1c4t0k3n5'
48
- ]
52
+ c.verification_token = 'vErIf1c4t0k3n5'
49
53
  c.bot_access_token = 'xoxb-1234567890-b0t4cCe5sToK3N'
50
54
  end
51
55
  ```
52
56
 
53
- ### Use
57
+ ### Handlers
58
+
59
+ Define handlers, also a Ruby file, and they'll be executed as incoming Slack events are processed. You can define as many handlers as you want. You'll store the file(s) in the directory you've identified in the configuration file above.
60
+
61
+ ```ruby
62
+ ME::Handler.add(:event, 'debug') do |e, h|
63
+ e.event.each { |k, v| puts "#{k}: #{v}" }
64
+ h.stop unless e.for_me?
65
+ end
66
+
67
+ ```
68
+
69
+ ### Slack
70
+
71
+
72
+
73
+ ## Usage
54
74
 
55
75
  To see help:
56
76
 
@@ -6,36 +6,36 @@ module MosEisley
6
6
 
7
7
  class HTTPClient
8
8
 
9
- def self.post_form(url:, params:, &block)
9
+ def self.post_form(url:, params: nil, head: nil, &block)
10
10
  if EM.reactor_running?
11
- MosEisley.logger.debug('POSTing form')
12
- MosEisley::HTTPClient.request(url: url, body: params, &block)
11
+ MosEisley.logger.debug("POSTing form to #{url}")
12
+ MosEisley::HTTPClient.request(url: url, head: head, body: params, &block)
13
13
  else
14
14
  MosEisley.logger.debug('Starting reactor...')
15
15
  EM.run {
16
- MosEisley.logger.debug('POSTing form')
17
- MosEisley::HTTPClient.request(url: url, body: params, stop: true, &block)
16
+ MosEisley.logger.debug("POSTing form to #{url}")
17
+ MosEisley::HTTPClient.request(url: url, head: head, body: params, stop: true, &block)
18
18
  }
19
19
  end
20
20
  end
21
21
 
22
- def self.post_json(url:, params: nil, body: nil, &block)
23
- head = {'Content-Type' => 'application/json'}
22
+ def self.post_json(url:, params: nil, body: nil, head: {}, &block)
23
+ head.merge!({'Content-Type' => 'application/json'})
24
24
  body = S3PO.json_with_object(params) if params
25
25
  if EM.reactor_running?
26
- MosEisley.logger.debug('POSTing JSON')
27
- MosEisley::HTTPClient.request(url: url, body: body, head: head, &block)
26
+ MosEisley.logger.debug("POSTing JSON to: #{url}")
27
+ MosEisley::HTTPClient.request(url: url, head: head, body: body, &block)
28
28
  else
29
29
  MosEisley.logger.debug('Starting reactor...')
30
30
  EM.run {
31
- MosEisley.logger.debug('POSTing JSON')
32
- MosEisley::HTTPClient.request(url: url, body: body, head: head, stop: true, &block)
31
+ MosEisley.logger.debug("POSTing JSON to #{url}")
32
+ MosEisley::HTTPClient.request(url: url, head: head, body: body, stop: true, &block)
33
33
  }
34
34
  end
35
35
  end
36
36
 
37
37
  def self.request(url:, head: nil, body:, stop: false, &block)
38
- http = EM::HttpRequest.new(url).post(body: body)
38
+ http = EM::HttpRequest.new(url).post(body: body, head: head)
39
39
  http.errback {
40
40
  MosEisley.logger.error('HTTP error')
41
41
  if stop
@@ -5,32 +5,33 @@ module MosEisley
5
5
  BaseURL = 'https://slack.com/api/'
6
6
 
7
7
  def self.auth_test
8
- MosEisley.logger.debug('auth_test')
9
8
  m = 'auth.test'
10
9
  url = BaseURL + m
11
- params = {token: MosEisley.config.bot_access_token}
12
- MosEisley.logger.debug("#{url}\n#{params}")
13
- HTTPClient.post_form(url: url, params: params) do |h|
10
+ head = {authorization: "Bearer #{MosEisley.config.bot_access_token}"}
11
+ HTTPClient.post_form(url: url, head: head) do |h|
14
12
  MosEisley.config.meta.merge!(S3PO.parse_json(h.response))
15
- MosEisley.logger.debug("meta data updated:\n#{MosEisley.config.meta}")
13
+ MosEisley.logger.info('Meta data updated by auth.test call.')
14
+ MosEisley.logger.debug("Meta data:\n#{MosEisley.config.meta}")
16
15
  end
17
16
  end
18
17
 
19
18
  def self.post_message(msg)
20
19
  m = 'chat.postMessage'
21
20
  url = BaseURL + m
22
- msg[:token] = MosEisley.config.bot_access_token
23
- HTTPClient.post_form(url: url, params: msg) do |h|
24
- MosEisley.logger.debug("chat.postMessage POSTed: #{h.response}")
21
+ head = {authorization: "Bearer #{MosEisley.config.bot_access_token}"}
22
+ HTTPClient.post_json(url: url, params: msg, head: head) do |h|
23
+ MosEisley.logger.info('POSTed chat.postMessage')
24
+ MosEisley.logger.debug("chat.postMessage echo:\n#{h.response}")
25
25
  end
26
26
  end
27
27
 
28
28
  def self.post_ephemeral(msg)
29
29
  m = 'chat.postEphemeral'
30
30
  url = BaseURL + m
31
- msg[:token] = MosEisley.config.bot_access_token
32
- HTTPClient.post_form(url: url, params: msg) do |h|
33
- MosEisley.logger.debug("chat.postEphemeral POSTed: #{h.response}")
31
+ head = {authorization: "Bearer #{MosEisley.config.bot_access_token}"}
32
+ HTTPClient.post_json(url: url, params: msg, head: head) do |h|
33
+ MosEisley.logger.info('POSTed chat.postEphemeral')
34
+ MosEisley.logger.debug("chat.postEphemeral echo:\n#{h.response}")
34
35
  end
35
36
  end
36
37
 
@@ -20,7 +20,7 @@ module MosEisley
20
20
  end
21
21
 
22
22
  def valid_token?(token)
23
- Config.shared.verification_tokens.include?(token)
23
+ token == Config.shared.verification_token
24
24
  end
25
25
 
26
26
  # Convert object into JSON, optionally pretty-format
@@ -24,11 +24,11 @@ module MosEisley
24
24
  # Interactive message buttons
25
25
  # Receive POST form with payload containing JSON string; use "token" to verify
26
26
  post '/action' do
27
- logger.info('Incoming request received.')
27
+ logger.info('Incoming request received at /action.')
28
28
  logger.debug("Body size: #{request.content_length} bytes")
29
29
  event = parse_json(params[:payload])
30
30
  halt 400 if event.nil?
31
- logger.debug("#{event}")
31
+ logger.debug("Parsed JSON data:\n#{event}")
32
32
  unless valid_token?(event[:token])
33
33
  logger.debug("Invalid Slack Events token: #{event[:token]}")
34
34
  halt 401
@@ -46,6 +46,7 @@ module MosEisley
46
46
  # Respond within 3 sec directly; raw text or formatted
47
47
  # OR, use response_url
48
48
  post '/command' do
49
+ logger.info('Incoming request received at /command.')
49
50
  cmd = parse_command(params)
50
51
  unless valid_token?(cmd[:token])
51
52
  logger.debug("Invalid Slack Events token: #{cmd[:token]}")
@@ -62,12 +63,12 @@ module MosEisley
62
63
  # Event API
63
64
  # Receive POST JSON; use "token" to verify
64
65
  post '/event' do
65
- logger.info('Incoming request received.')
66
+ logger.info('Incoming request received at /event.')
66
67
  logger.debug("Body size: #{request.content_length} bytes")
67
68
  request.body.rewind
68
69
  event = parse_json(request.body.read)
69
70
  halt 400 if event.nil?
70
- logger.debug("#{event}")
71
+ logger.debug("Parsed JSON data:\n#{event}")
71
72
 
72
73
  unless valid_token?(event[:token])
73
74
  logger.debug("Invalid Slack Events token: #{event[:token]}")
@@ -83,8 +84,6 @@ module MosEisley
83
84
  else
84
85
  resp[:text] = "Unknown event type: #{event[:type]}"
85
86
  end
86
- logger.debug("#{resp}")
87
- logger.debug("#{ME.config.meta}")
88
87
  json_with_object(resp)
89
88
  end
90
89
 
@@ -34,7 +34,7 @@ module MosEisley
34
34
 
35
35
  attr_reader :meta
36
36
 
37
- attr_accessor :verification_tokens
37
+ attr_accessor :verification_token
38
38
  attr_accessor :bot_access_token
39
39
 
40
40
  def initialize
@@ -44,7 +44,7 @@ module MosEisley
44
44
 
45
45
  @meta = {}
46
46
 
47
- @verification_tokens = []
47
+ @verification_token = nil
48
48
  @bot_access_token = ''
49
49
  end
50
50
 
@@ -21,14 +21,16 @@ module MosEisley
21
21
  }
22
22
  end
23
23
 
24
- # Call as often as necessary to add handlers; each call creates a MosEisley::Handler object
25
- def self.add(type, &block)
24
+ # Call as often as necessary to add handlers with blocks; each call creates a MosEisley::Handler object
25
+ # @param type [Symbol] :action | :command | :event
26
+ # @param name [String]
27
+ def self.add(type, name = nil, &block)
26
28
  @handlers ||= {
27
29
  action: [],
28
30
  command: [],
29
31
  event: []
30
32
  }
31
- @handlers[type] << Handler.new(type, &block)
33
+ @handlers[type] << Handler.new(type, name, &block)
32
34
  MosEisley.logger.debug("Added #{type} handler: #{@handlers[type].last}")
33
35
  end
34
36
 
@@ -41,47 +43,73 @@ module MosEisley
41
43
  # @param event [Hash] from Slack Events API JSON data
42
44
  def self.run(type, event)
43
45
  logger = MosEisley.logger
44
- logger.info("Running #{type} handlers...")
45
46
  responses = []
46
- @handlers[type].each { |h| responses << h.run(event) }
47
+ @handlers[type].each do |h|
48
+ responses << h.run(event)
49
+ if h.stopped?
50
+ logger.debug('Handler stop was requested.')
51
+ break
52
+ end
53
+ end
47
54
  logger.info("Done running #{type} handlers.")
48
55
  responses = [] if type == :event
49
56
  merged_res = {}
50
- responses.each do |r|
51
- next unless r.class == Hash
52
- [:response_type, :replace_original].each { |k| merged_res[k] = r[k] if r.has_key?(k) }
53
- if r[:text]
54
- if merged_res[:text]
55
- merged_res[:text] += "\n#{r[:text]}"
56
- else
57
- merged_res[:text] = r[:text]
58
- end
59
- end
60
- if r[:attachments]
61
- merged_res[:attachments] ||= []
62
- merged_res[:attachments] += r[:attachments]
63
- end
64
- end
57
+ # Only take the last response
58
+ r = responses.last
59
+ merged_res = r if r.class == Hash
60
+ # ## Accumulative Response routine ##
61
+ # responses.each do |r|
62
+ # next unless r.class == Hash
63
+ # [:response_type, :replace_original].each { |k| merged_res[k] = r[k] if r.has_key?(k) }
64
+ # if r[:text]
65
+ # if merged_res[:text]
66
+ # merged_res[:text] += "\n#{r[:text]}"
67
+ # else
68
+ # merged_res[:text] = r[:text]
69
+ # end
70
+ # end
71
+ # if r[:attachments]
72
+ # merged_res[:attachments] ||= []
73
+ # merged_res[:attachments] += r[:attachments]
74
+ # end
75
+ # end
65
76
  return nil if merged_res.empty?
66
77
  return merged_res
67
78
  end
68
79
 
69
- def initialize(type, &block)
70
- @type = type
80
+ attr_reader :type, :name
81
+
82
+ def initialize(t, n = nil, &block)
83
+ @type = t
84
+ @name = n
71
85
  @block = block
86
+ @stopped = false
72
87
  end
73
88
 
74
89
  def run(event)
75
90
  logger = MosEisley.logger
76
91
  logger.warn("No block to execute for #{@type} handler: #{self}") unless @block
77
92
  logger.debug("Running #{@type} handler: #{self}")
78
- @block.call(event)
93
+ @stopped = false
94
+ @block.call(event, self)
79
95
  rescue => e
80
96
  logger.error(e.message)
81
97
  logger.error(e.backtrace.join("\n"))
82
98
  {text: "Woops, encountered an error."}
83
99
  end
84
100
 
101
+ def stop
102
+ @stopped = true
103
+ end
104
+
105
+ def stopped?
106
+ @stopped
107
+ end
108
+
109
+ def to_s
110
+ "#<#{self.class}:#{self.object_id.to_s(16)}(#{name})>"
111
+ end
112
+
85
113
  end
86
114
 
87
115
  end
@@ -1,5 +1,5 @@
1
1
  module MosEisley
2
2
 
3
- Version = '0.0.3'
3
+ Version = '0.1.0'
4
4
 
5
5
  end
data/lib/s3po/message.rb CHANGED
@@ -57,6 +57,10 @@ module MosEisley
57
57
  event[:user]
58
58
  end
59
59
 
60
+ def user=(u)
61
+ event[:user] = u
62
+ end
63
+
60
64
  def ts
61
65
  event[:ts]
62
66
  end
data/lib/s3po/s3po.rb CHANGED
@@ -52,7 +52,7 @@ module MosEisley
52
52
  end
53
53
 
54
54
  def self.create_event(e, type = nil)
55
- type = e[:type] if e[:type]
55
+ type ||= e[:type] if e[:type]
56
56
  case type
57
57
  when 'message'
58
58
  return Message.new(e)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mos-eisley
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken J.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-22 00:00:00.000000000 Z
11
+ date: 2017-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kajiki