slack-ruby-client 0.4.0 → 0.5.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/.rubocop_todo.yml +23 -6
  4. data/.travis.yml +3 -0
  5. data/CHANGELOG.md +11 -0
  6. data/Gemfile +2 -0
  7. data/README.md +89 -1
  8. data/RELEASING.md +3 -1
  9. data/UPGRADING.md +26 -0
  10. data/bin/commands.rb +20 -0
  11. data/bin/commands/api.rb +14 -0
  12. data/bin/commands/auth.rb +12 -0
  13. data/bin/commands/channels.rb +140 -0
  14. data/bin/commands/chat.rb +47 -0
  15. data/bin/commands/emoji.rb +12 -0
  16. data/bin/commands/files.rb +61 -0
  17. data/bin/commands/groups.rb +158 -0
  18. data/bin/commands/im.rb +53 -0
  19. data/bin/commands/mpim.rb +53 -0
  20. data/bin/commands/oauth.rb +16 -0
  21. data/bin/commands/pins.rb +37 -0
  22. data/bin/commands/reactions.rb +53 -0
  23. data/bin/commands/rtm.rb +15 -0
  24. data/bin/commands/search.rb +40 -0
  25. data/bin/commands/stars.rb +37 -0
  26. data/bin/commands/team.rb +32 -0
  27. data/bin/commands/usergroups.rb +73 -0
  28. data/bin/commands/users.rb +48 -0
  29. data/bin/slack +50 -0
  30. data/examples/hi_real_time/Gemfile +2 -0
  31. data/examples/hi_real_time_and_web/Gemfile +2 -0
  32. data/examples/hi_real_time_async/Gemfile +5 -0
  33. data/examples/hi_real_time_async/hi.rb +29 -0
  34. data/lib/slack-ruby-client.rb +2 -2
  35. data/lib/slack/real_time/client.rb +72 -30
  36. data/lib/slack/real_time/concurrency.rb +8 -0
  37. data/lib/slack/real_time/concurrency/celluloid.rb +92 -0
  38. data/lib/slack/real_time/concurrency/eventmachine.rb +39 -0
  39. data/lib/slack/real_time/config.rb +23 -1
  40. data/lib/slack/real_time/socket.rb +50 -12
  41. data/lib/slack/version.rb +1 -1
  42. data/lib/slack/web/api/endpoints.rb +2 -0
  43. data/lib/slack/web/api/endpoints/groups.rb +1 -1
  44. data/lib/slack/web/api/endpoints/team.rb +1 -1
  45. data/lib/slack/web/api/endpoints/usergroups.rb +113 -0
  46. data/lib/slack/web/api/error.rb +6 -0
  47. data/lib/slack/web/api/schema/group.json +14 -0
  48. data/lib/slack/web/api/tasks/generate.rake +19 -3
  49. data/lib/slack/web/api/templates/command.erb +34 -0
  50. data/lib/slack/web/api/templates/commands.erb +5 -0
  51. data/lib/slack/web/config.rb +2 -0
  52. data/lib/slack/web/faraday/connection.rb +3 -2
  53. data/lib/slack/web/faraday/response/raise_error.rb +2 -1
  54. data/slack-ruby-client.gemspec +4 -2
  55. data/spec/fixtures/slack/web/429_error.yml +83 -0
  56. data/spec/fixtures/slack/web/rtm_start.yml +1 -1
  57. data/spec/fixtures/slack/web/users_list.yml +72 -0
  58. data/spec/integration/integration_spec.rb +88 -0
  59. data/spec/slack/real_time/client_spec.rb +8 -5
  60. data/spec/slack/real_time/concurrency/celluloid_spec.rb +58 -0
  61. data/spec/slack/real_time/concurrency/eventmachine_spec.rb +49 -0
  62. data/spec/slack/slack_spec.rb +52 -0
  63. data/spec/slack/web/api/endpoints/auth_spec.rb +6 -1
  64. data/spec/slack/web/api/endpoints/users_spec.rb +13 -0
  65. data/spec/slack/web/api/error_spec.rb +14 -0
  66. data/spec/slack/web/client_spec.rb +16 -0
  67. data/spec/support/real_time/concurrency/mock.rb +31 -0
  68. data/spec/support/real_time/connected_client.rb +5 -2
  69. metadata +55 -8
  70. data/spec/slack/real_time/socket_spec.rb +0 -46
@@ -0,0 +1,8 @@
1
+ module Slack
2
+ module RealTime
3
+ module Concurrency
4
+ autoload :Eventmachine, 'slack/real_time/concurrency/eventmachine'
5
+ autoload :Celluloid, 'slack/real_time/concurrency/celluloid'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,92 @@
1
+ require 'websocket/driver'
2
+ require 'socket'
3
+ require 'forwardable'
4
+ require 'celluloid/io'
5
+
6
+ module Slack
7
+ module RealTime
8
+ module Concurrency
9
+ module Celluloid
10
+ class Socket < Slack::RealTime::Socket
11
+ include ::Celluloid::IO
12
+ include ::Celluloid::Logger
13
+
14
+ BLOCK_SIZE = 4096
15
+
16
+ extend ::Forwardable
17
+ def_delegator :socket, :write
18
+ def_delegators :driver, :text, :binary, :close
19
+
20
+ attr_reader :socket
21
+
22
+ def initialize(*args)
23
+ super
24
+ @driver = build_driver
25
+ end
26
+
27
+ # @yieldparam [WebSocket::Driver] driver
28
+ def connect!
29
+ super
30
+
31
+ driver.start
32
+ future.run_loop
33
+ end
34
+
35
+ def run_loop
36
+ loop { read } if socket
37
+ rescue EOFError
38
+ # connection closed
39
+ end
40
+
41
+ def read
42
+ buffer = socket.readpartial(BLOCK_SIZE)
43
+ driver.parse buffer
44
+ end
45
+
46
+ def start_async
47
+ future = yield self if block_given?
48
+
49
+ Actor.new(future)
50
+ end
51
+
52
+ protected
53
+
54
+ class Actor
55
+ attr_reader :future
56
+
57
+ def initialize(future)
58
+ @future = future
59
+ end
60
+
61
+ def join
62
+ @future.value
63
+ end
64
+ end
65
+
66
+ def connected?
67
+ !@connected.nil?
68
+ end
69
+
70
+ def build_socket
71
+ socket = TCPSocket.new(addr, port)
72
+ socket = SSLSocket.new(socket, build_ssl_context) if secure?
73
+ socket
74
+ end
75
+
76
+ def build_ssl_context
77
+ OpenSSL::SSL::SSLContext.new(:TLSv1_2_client)
78
+ end
79
+
80
+ def build_driver
81
+ ::WebSocket::Driver.client(self)
82
+ end
83
+
84
+ def connect
85
+ @socket = build_socket
86
+ @connected = @socket.connect
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,39 @@
1
+ require 'faye/websocket'
2
+ require 'eventmachine'
3
+
4
+ module Slack
5
+ module RealTime
6
+ module Concurrency
7
+ module Eventmachine
8
+ class Socket < Slack::RealTime::Socket
9
+ def start_async
10
+ thread = ensure_reactor_running
11
+
12
+ yield self if block_given?
13
+
14
+ thread
15
+ end
16
+
17
+ def send_data(message)
18
+ driver.send(message)
19
+ end
20
+
21
+ protected
22
+
23
+ # @return [Thread]
24
+ def ensure_reactor_running
25
+ return if EventMachine.reactor_running?
26
+
27
+ reactor = Thread.new { EventMachine.run }
28
+ Thread.pass until EventMachine.reactor_running?
29
+ reactor
30
+ end
31
+
32
+ def connect
33
+ @driver = ::Faye::WebSocket::Client.new(url, nil, options)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,12 +1,15 @@
1
1
  module Slack
2
2
  module RealTime
3
3
  module Config
4
+ class NoConcurrencyError < StandardError; end
5
+
4
6
  extend self
5
7
 
6
8
  ATTRIBUTES = [
7
9
  :token,
8
10
  :websocket_ping,
9
- :websocket_proxy
11
+ :websocket_proxy,
12
+ :concurrency
10
13
  ]
11
14
 
12
15
  attr_accessor(*Config::ATTRIBUTES)
@@ -15,6 +18,25 @@ module Slack
15
18
  self.websocket_ping = 30
16
19
  self.websocket_proxy = nil
17
20
  self.token = nil
21
+ self.concurrency = method(:detect_concurrency)
22
+ end
23
+
24
+ def concurrency
25
+ (val = @concurrency).respond_to?(:call) ? val.call : val
26
+ end
27
+
28
+ private
29
+
30
+ def detect_concurrency
31
+ [:Eventmachine, :Celluloid].each do |concurrency|
32
+ begin
33
+ return Slack::RealTime::Concurrency.const_get(concurrency)
34
+ rescue LoadError
35
+ false # could not be loaded, missing dependencies
36
+ end
37
+ end
38
+
39
+ fail NoConcurrencyError, 'Missing concurrency. Add faye-websocket or celluloid-io to your Gemfile.'
18
40
  end
19
41
  end
20
42
 
@@ -3,40 +3,78 @@ module Slack
3
3
  class Socket
4
4
  attr_accessor :url
5
5
  attr_accessor :options
6
+ attr_reader :driver
6
7
 
7
8
  def initialize(url, options = {})
8
9
  @url = url
9
10
  @options = options
11
+ @driver = nil
10
12
  end
11
13
 
12
- def send_data(data)
13
- @ws.send(data) if @ws
14
+ def send_data(message)
15
+ case message
16
+ when Numeric then driver.text(message.to_s)
17
+ when String then driver.text(message)
18
+ when Array then driver.binary(message)
19
+ else false
20
+ end
14
21
  end
15
22
 
16
- def connect!(&_block)
23
+ def connect!
17
24
  return if connected?
18
25
 
19
- @ws = Faye::WebSocket::Client.new(url, nil, options)
20
-
21
- @ws.on :close do |event|
22
- close(event)
23
- end
26
+ connect
24
27
 
25
- yield @ws if block_given?
28
+ yield driver if block_given?
26
29
  end
27
30
 
28
31
  def disconnect!
29
- @ws.close if @ws
32
+ driver.close
30
33
  end
31
34
 
32
35
  def connected?
33
- !@ws.nil?
36
+ !driver.nil?
37
+ end
38
+
39
+ def start_sync(&block)
40
+ thread = start_async(&block)
41
+ thread.join if thread
42
+ rescue Interrupt
43
+ thread.exit if thread
44
+ end
45
+
46
+ # @return [#join]
47
+ def start_async
48
+ fail NotImplementedError, "Expected #{self.class} to implement #{__method__}."
34
49
  end
35
50
 
36
51
  protected
37
52
 
53
+ def addr
54
+ URI(url).host
55
+ end
56
+
57
+ def secure?
58
+ port == URI::HTTPS::DEFAULT_PORT
59
+ end
60
+
61
+ def port
62
+ case (uri = URI(url)).scheme
63
+ when 'wss'.freeze, 'https'.freeze
64
+ URI::HTTPS::DEFAULT_PORT
65
+ when 'ws', 'http'.freeze
66
+ URI::HTTP::DEFAULT_PORT
67
+ else
68
+ uri.port
69
+ end
70
+ end
71
+
72
+ def connect
73
+ fail NotImplementedError, "Expected #{self.class} to implement #{__method__}."
74
+ end
75
+
38
76
  def close(_event)
39
- @ws = nil
77
+ @driver = nil
40
78
  end
41
79
  end
42
80
  end
@@ -1,3 +1,3 @@
1
1
  module Slack
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -16,6 +16,7 @@ require 'slack/web/api/endpoints/rtm'
16
16
  require 'slack/web/api/endpoints/search'
17
17
  require 'slack/web/api/endpoints/stars'
18
18
  require 'slack/web/api/endpoints/team'
19
+ require 'slack/web/api/endpoints/usergroups'
19
20
  require 'slack/web/api/endpoints/users'
20
21
 
21
22
  module Slack
@@ -38,6 +39,7 @@ module Slack
38
39
  include Search
39
40
  include Stars
40
41
  include Team
42
+ include Usergroups
41
43
  include Users
42
44
  end
43
45
  end
@@ -21,7 +21,7 @@ module Slack
21
21
  # This method closes a private group.
22
22
  #
23
23
  # @option options [group] :channel
24
- # Group to open.
24
+ # Group to close.
25
25
  # @see https://api.slack.com/methods/groups.close
26
26
  # @see https://github.com/dblock/slack-api-ref/blob/master/methods/groups.close.json
27
27
  def groups_close(options = {})
@@ -31,7 +31,7 @@ module Slack
31
31
  # @option options [Object] :app_id
32
32
  # Filter logs to this API application. Defaults to all logs.
33
33
  # @option options [user] :user
34
- # Filter logs generated by this users actions. Defaults to all logs.
34
+ # Filter logs generated by this user's actions. Defaults to all logs.
35
35
  # @option options [Object] :change_type
36
36
  # Filter logs with this change type. Defaults to all logs.
37
37
  # @see https://api.slack.com/methods/team.integrationLogs
@@ -0,0 +1,113 @@
1
+ # This file was auto-generated by lib/slack/web/api/tasks/generate.rake
2
+
3
+ module Slack
4
+ module Web
5
+ module Api
6
+ module Endpoints
7
+ module Usergroups
8
+ #
9
+ # This method is used to create a user group.
10
+ #
11
+ # @option options [Object] :name
12
+ # A name for the user group. Must be unique among user groups.
13
+ # @option options [Object] :handle
14
+ # A mention handle. Must be unique among channels, users and user groups.
15
+ # @option options [Object] :description
16
+ # A short description of the user group.
17
+ # @option options [Object] :channels
18
+ # A comma separated string of encoded channel IDs for which the user group uses as a default.
19
+ # @option options [Object] :include_count
20
+ # Include the number of users in each user group.
21
+ # @see https://api.slack.com/methods/usergroups.create
22
+ # @see https://github.com/dblock/slack-api-ref/blob/master/methods/usergroups.create.json
23
+ def usergroups_create(options = {})
24
+ throw ArgumentError.new('Required arguments :name missing') if options[:name].nil?
25
+ post('usergroups.create', options)
26
+ end
27
+
28
+ #
29
+ # This method disables an existing user group.
30
+ #
31
+ # @option options [Object] :usergroup
32
+ # The encoded ID of the user group to disable.
33
+ # @option options [Object] :include_count
34
+ # Include the number of users in the user group.
35
+ # @see https://api.slack.com/methods/usergroups.disable
36
+ # @see https://github.com/dblock/slack-api-ref/blob/master/methods/usergroups.disable.json
37
+ def usergroups_disable(options = {})
38
+ throw ArgumentError.new('Required arguments :usergroup missing') if options[:usergroup].nil?
39
+ post('usergroups.disable', options)
40
+ end
41
+
42
+ #
43
+ # This method enables a user group which was previously disabled.
44
+ #
45
+ # @option options [Object] :usergroup
46
+ # The encoded ID of the user group to enable.
47
+ # @option options [Object] :include_count
48
+ # Include the number of users in the user group.
49
+ # @see https://api.slack.com/methods/usergroups.enable
50
+ # @see https://github.com/dblock/slack-api-ref/blob/master/methods/usergroups.enable.json
51
+ def usergroups_enable(options = {})
52
+ throw ArgumentError.new('Required arguments :usergroup missing') if options[:usergroup].nil?
53
+ post('usergroups.enable', options)
54
+ end
55
+
56
+ #
57
+ # This method returns a list of all user groups in the team. This can optionally include disabled user groups.
58
+ #
59
+ # @option options [Object] :include_disabled
60
+ # Include disabled user groups.
61
+ # @option options [Object] :include_count
62
+ # Include the number of users in each user group.
63
+ # @option options [Object] :include_users
64
+ # Include the list of users for each user group.
65
+ # @see https://api.slack.com/methods/usergroups.list
66
+ # @see https://github.com/dblock/slack-api-ref/blob/master/methods/usergroups.list.json
67
+ def usergroups_list(options = {})
68
+ post('usergroups.list', options)
69
+ end
70
+
71
+ #
72
+ # This method updates the properties of an existing user group.
73
+ #
74
+ # @option options [Object] :usergroup
75
+ # The encoded ID of the user group to update.
76
+ # @option options [Object] :name
77
+ # A name for the user group. Must be unique among user groups.
78
+ # @option options [Object] :handle
79
+ # A mention handle. Must be unique among channels, users and user groups.
80
+ # @option options [Object] :description
81
+ # A short description of the user group.
82
+ # @option options [Object] :channels
83
+ # A comma separated string of encoded channel IDs for which the user group uses as a default.
84
+ # @option options [Object] :include_count
85
+ # Include the number of users in the user group.
86
+ # @see https://api.slack.com/methods/usergroups.update
87
+ # @see https://github.com/dblock/slack-api-ref/blob/master/methods/usergroups.update.json
88
+ def usergroups_update(options = {})
89
+ throw ArgumentError.new('Required arguments :usergroup missing') if options[:usergroup].nil?
90
+ post('usergroups.update', options)
91
+ end
92
+
93
+ #
94
+ # This method updates the list of users that belong to a user group. This method replaces all users in a user group with the list of users provided in the users parameter.
95
+ #
96
+ # @option options [Object] :usergroup
97
+ # The encoded ID of the user group to update.
98
+ # @option options [Object] :users
99
+ # A comma separated string of encoded user IDs that represent the entire list of users for the user group.
100
+ # @option options [Object] :include_count
101
+ # Include the number of users in the user group.
102
+ # @see https://api.slack.com/methods/usergroups.users
103
+ # @see https://github.com/dblock/slack-api-ref/blob/master/methods/usergroups.users.json
104
+ def usergroups_users(options = {})
105
+ throw ArgumentError.new('Required arguments :usergroup missing') if options[:usergroup].nil?
106
+ throw ArgumentError.new('Required arguments :users missing') if options[:users].nil?
107
+ post('usergroups.users', options)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -2,6 +2,12 @@ module Slack
2
2
  module Web
3
3
  module Api
4
4
  class Error < ::Faraday::Error
5
+ attr_reader :response
6
+
7
+ def initialize(message, response)
8
+ @response = response
9
+ super message
10
+ end
5
11
  end
6
12
  end
7
13
  end
@@ -0,0 +1,14 @@
1
+ {
2
+ "type": "object",
3
+ "required": [
4
+ "name"
5
+ ],
6
+ "properties": {
7
+ "name": {
8
+ "type": "string"
9
+ },
10
+ "desc": {
11
+ "type": "string"
12
+ }
13
+ }
14
+ }