slack-ruby-client 0.4.0 → 0.5.0

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