slack-ruby-client-bhe 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +4 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +6 -0
  6. data/.rubocop_todo.yml +78 -0
  7. data/.travis.yml +26 -0
  8. data/CHANGELOG.md +79 -0
  9. data/CONTRIBUTING.md +157 -0
  10. data/Gemfile +5 -0
  11. data/LICENSE.md +22 -0
  12. data/README.md +385 -0
  13. data/RELEASING.md +69 -0
  14. data/Rakefile +19 -0
  15. data/UPGRADING.md +26 -0
  16. data/bin/commands.rb +21 -0
  17. data/bin/commands/api.rb +14 -0
  18. data/bin/commands/auth.rb +12 -0
  19. data/bin/commands/channels.rb +149 -0
  20. data/bin/commands/chat.rb +47 -0
  21. data/bin/commands/dnd.rb +47 -0
  22. data/bin/commands/emoji.rb +12 -0
  23. data/bin/commands/files.rb +72 -0
  24. data/bin/commands/groups.rb +167 -0
  25. data/bin/commands/im.rb +53 -0
  26. data/bin/commands/mpim.rb +53 -0
  27. data/bin/commands/oauth.rb +16 -0
  28. data/bin/commands/pins.rb +37 -0
  29. data/bin/commands/reactions.rb +53 -0
  30. data/bin/commands/rtm.rb +15 -0
  31. data/bin/commands/search.rb +40 -0
  32. data/bin/commands/stars.rb +37 -0
  33. data/bin/commands/team.rb +32 -0
  34. data/bin/commands/usergroups.rb +73 -0
  35. data/bin/commands/users.rb +57 -0
  36. data/bin/slack +50 -0
  37. data/examples/hi_real_time/Gemfile +5 -0
  38. data/examples/hi_real_time/hi.gif +0 -0
  39. data/examples/hi_real_time/hi.rb +32 -0
  40. data/examples/hi_real_time_and_web/Gemfile +5 -0
  41. data/examples/hi_real_time_and_web/hi.gif +0 -0
  42. data/examples/hi_real_time_and_web/hi.rb +24 -0
  43. data/examples/hi_real_time_async/Gemfile +5 -0
  44. data/examples/hi_real_time_async/hi.rb +29 -0
  45. data/examples/hi_web/Gemfile +3 -0
  46. data/examples/hi_web/hi.gif +0 -0
  47. data/examples/hi_web/hi.rb +12 -0
  48. data/examples/new_ticket/Gemfile +3 -0
  49. data/examples/new_ticket/new_ticket.rb +25 -0
  50. data/lib/slack-ruby-client.rb +29 -0
  51. data/lib/slack.rb +1 -0
  52. data/lib/slack/config.rb +21 -0
  53. data/lib/slack/messages/formatting.rb +30 -0
  54. data/lib/slack/real_time/api/message.rb +20 -0
  55. data/lib/slack/real_time/api/message_id.rb +14 -0
  56. data/lib/slack/real_time/api/ping.rb +16 -0
  57. data/lib/slack/real_time/api/typing.rb +17 -0
  58. data/lib/slack/real_time/client.rb +152 -0
  59. data/lib/slack/real_time/concurrency.rb +8 -0
  60. data/lib/slack/real_time/concurrency/celluloid.rb +93 -0
  61. data/lib/slack/real_time/concurrency/eventmachine.rb +39 -0
  62. data/lib/slack/real_time/config.rb +55 -0
  63. data/lib/slack/real_time/socket.rb +81 -0
  64. data/lib/slack/version.rb +3 -0
  65. data/lib/slack/web/api/endpoints.rb +53 -0
  66. data/lib/slack/web/api/endpoints/api.rb +24 -0
  67. data/lib/slack/web/api/endpoints/auth.rb +20 -0
  68. data/lib/slack/web/api/endpoints/channels.rb +220 -0
  69. data/lib/slack/web/api/endpoints/chat.rb +91 -0
  70. data/lib/slack/web/api/endpoints/dnd.rb +64 -0
  71. data/lib/slack/web/api/endpoints/emoji.rb +20 -0
  72. data/lib/slack/web/api/endpoints/files.rb +108 -0
  73. data/lib/slack/web/api/endpoints/groups.rb +247 -0
  74. data/lib/slack/web/api/endpoints/im.rb +85 -0
  75. data/lib/slack/web/api/endpoints/mpim.rb +84 -0
  76. data/lib/slack/web/api/endpoints/oauth.rb +32 -0
  77. data/lib/slack/web/api/endpoints/pins.rb +64 -0
  78. data/lib/slack/web/api/endpoints/presence.rb +23 -0
  79. data/lib/slack/web/api/endpoints/reactions.rb +89 -0
  80. data/lib/slack/web/api/endpoints/rtm.rb +27 -0
  81. data/lib/slack/web/api/endpoints/search.rb +65 -0
  82. data/lib/slack/web/api/endpoints/stars.rb +61 -0
  83. data/lib/slack/web/api/endpoints/team.rb +47 -0
  84. data/lib/slack/web/api/endpoints/usergroups.rb +113 -0
  85. data/lib/slack/web/api/endpoints/users.rb +73 -0
  86. data/lib/slack/web/api/error.rb +14 -0
  87. data/lib/slack/web/api/mixins.rb +3 -0
  88. data/lib/slack/web/api/mixins/channels.id.json +20 -0
  89. data/lib/slack/web/api/mixins/channels.id.rb +26 -0
  90. data/lib/slack/web/api/mixins/groups.id.json +20 -0
  91. data/lib/slack/web/api/mixins/groups.id.rb +26 -0
  92. data/lib/slack/web/api/mixins/users.id.json +20 -0
  93. data/lib/slack/web/api/mixins/users.id.rb +26 -0
  94. data/lib/slack/web/api/patches/chat.1.text-attachments-required.patch +13 -0
  95. data/lib/slack/web/api/patches/chat.2.attachments-json.patch +17 -0
  96. data/lib/slack/web/api/schema/group.json +14 -0
  97. data/lib/slack/web/api/schema/method.json +45 -0
  98. data/lib/slack/web/api/tasks/generate.rake +61 -0
  99. data/lib/slack/web/api/templates/command.erb +34 -0
  100. data/lib/slack/web/api/templates/commands.erb +5 -0
  101. data/lib/slack/web/api/templates/endpoints.erb +21 -0
  102. data/lib/slack/web/api/templates/method.erb +49 -0
  103. data/lib/slack/web/client.rb +28 -0
  104. data/lib/slack/web/config.rb +41 -0
  105. data/lib/slack/web/faraday/connection.rb +29 -0
  106. data/lib/slack/web/faraday/request.rb +39 -0
  107. data/lib/slack/web/faraday/response/raise_error.rb +15 -0
  108. data/lib/slack_ruby_client.rb +1 -0
  109. data/screenshots/register-bot.png +0 -0
  110. data/slack-ruby-client.gemspec +31 -0
  111. data/slack.png +0 -0
  112. data/spec/fixtures/slack/web/429_error.yml +83 -0
  113. data/spec/fixtures/slack/web/auth_test_error.yml +48 -0
  114. data/spec/fixtures/slack/web/auth_test_success.yml +57 -0
  115. data/spec/fixtures/slack/web/channels_info.yml +46 -0
  116. data/spec/fixtures/slack/web/groups_info.yml +43 -0
  117. data/spec/fixtures/slack/web/rtm_start.yml +73 -0
  118. data/spec/fixtures/slack/web/users_info.yml +130 -0
  119. data/spec/fixtures/slack/web/users_list.yml +72 -0
  120. data/spec/integration/integration_spec.rb +107 -0
  121. data/spec/slack/config_spec.rb +14 -0
  122. data/spec/slack/messages/formatting_spec.rb +43 -0
  123. data/spec/slack/real_time/api/message_spec.rb +15 -0
  124. data/spec/slack/real_time/api/ping_spec.rb +15 -0
  125. data/spec/slack/real_time/api/typing_spec.rb +15 -0
  126. data/spec/slack/real_time/client_spec.rb +198 -0
  127. data/spec/slack/real_time/concurrency/celluloid_spec.rb +58 -0
  128. data/spec/slack/real_time/concurrency/eventmachine_spec.rb +49 -0
  129. data/spec/slack/slack_spec.rb +58 -0
  130. data/spec/slack/version_spec.rb +7 -0
  131. data/spec/slack/web/api/endpoints/auth_spec.rb +20 -0
  132. data/spec/slack/web/api/endpoints/channels_spec.rb +11 -0
  133. data/spec/slack/web/api/endpoints/chat_spec.rb +33 -0
  134. data/spec/slack/web/api/endpoints/groups_spec.rb +11 -0
  135. data/spec/slack/web/api/endpoints/users_spec.rb +17 -0
  136. data/spec/slack/web/api/error_spec.rb +14 -0
  137. data/spec/slack/web/api/mixins/channels_spec.rb +31 -0
  138. data/spec/slack/web/api/mixins/groups_spec.rb +31 -0
  139. data/spec/slack/web/api/mixins/users_spec.rb +31 -0
  140. data/spec/slack/web/client_spec.rb +134 -0
  141. data/spec/spec_helper.rb +14 -0
  142. data/spec/support/queue_with_timeout.rb +34 -0
  143. data/spec/support/real_time/concurrency/mock.rb +31 -0
  144. data/spec/support/real_time/connected_client.rb +16 -0
  145. data/spec/support/token.rb +10 -0
  146. data/spec/support/vcr.rb +8 -0
  147. metadata +392 -0
@@ -0,0 +1,20 @@
1
+ module Slack
2
+ module RealTime
3
+ module Api
4
+ module Message
5
+ #
6
+ # Sends a message to a channel.
7
+ #
8
+ # @option options [channel] :channel
9
+ # Channel to send message to. Can be a public channel, private group or IM channel. Can be an encoded ID, or a name.
10
+ # @option options [Object] :text
11
+ # Text of the message to send. See below for an explanation of formatting.
12
+ def message(options = {})
13
+ throw ArgumentError.new('Required arguments :channel missing') if options[:channel].nil?
14
+ throw ArgumentError.new('Required arguments :text missing') if options[:text].nil?
15
+ send_json({ type: 'message', id: next_id }.merge(options))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module Slack
2
+ module RealTime
3
+ module Api
4
+ module MessageId
5
+ private
6
+
7
+ def next_id
8
+ @next_id ||= 0
9
+ @next_id += 1
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module Slack
2
+ module RealTime
3
+ module Api
4
+ module Ping
5
+ #
6
+ # Clients should try to quickly detect disconnections, even in idle periods, so that users can easily tell the
7
+ # difference between being disconnected and everyone being quiet. Not all web browsers support the WebSocket
8
+ # ping spec, so the RTM protocol also supports ping/pong messages.
9
+ #
10
+ def ping(options = {})
11
+ send_json({ type: 'ping', id: next_id }.merge(options))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Slack
2
+ module RealTime
3
+ module Api
4
+ module Typing
5
+ #
6
+ # Send a typing indicator to indicate that the user is currently writing a message.
7
+ #
8
+ # @option options [channel] :channel
9
+ # Channel to send message to. Can be a public channel, private group or IM channel. Can be an encoded ID, or a name.
10
+ def typing(options = {})
11
+ throw ArgumentError.new('Required arguments :channel missing') if options[:channel].nil?
12
+ send_json({ type: 'typing', id: next_id }.merge(options))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,152 @@
1
+ module Slack
2
+ module RealTime
3
+ class Client
4
+ class ClientNotStartedError < StandardError; end
5
+ class ClientAlreadyStartedError < StandardError; end
6
+
7
+ include Api::MessageId
8
+ include Api::Ping
9
+ include Api::Message
10
+ include Api::Typing
11
+
12
+ attr_accessor :web_client
13
+ attr_accessor(*Config::ATTRIBUTES)
14
+
15
+ def initialize(options = {})
16
+ @callbacks = Hash.new { |h, k| h[k] = [] }
17
+ Slack::RealTime::Config::ATTRIBUTES.each do |key|
18
+ send("#{key}=", options[key] || Slack::RealTime.config.send(key))
19
+ end
20
+ @token ||= Slack.config.token
21
+ @web_client = Slack::Web::Client.new(token: token)
22
+ end
23
+
24
+ [:url, :team, :self, :users, :channels, :groups, :ims, :bots].each do |attr|
25
+ define_method attr do
26
+ @options[attr.to_s] if @options
27
+ end
28
+ end
29
+
30
+ def on(type, &block)
31
+ type = type.to_s
32
+ callbacks[type] << block
33
+ end
34
+
35
+ # Start RealTime client and block until it disconnects.
36
+ # @yieldparam [Websocket::Driver] driver
37
+ def start!(&block)
38
+ socket = build_socket
39
+ socket.start_sync { run_loop(socket, &block) }
40
+ end
41
+
42
+ # Start RealTime client and return immediately.
43
+ # The RealTime::Client will run in the background.
44
+ # @yieldparam [Websocket::Driver] driver
45
+ def start_async(&block)
46
+ socket = build_socket
47
+ socket.start_async { run_loop(socket, &block) }
48
+ end
49
+
50
+ def stop!
51
+ fail ClientNotStartedError unless started?
52
+ @socket.disconnect! if @socket
53
+ end
54
+
55
+ def started?
56
+ @socket && @socket.connected?
57
+ end
58
+
59
+ class << self
60
+ def configure
61
+ block_given? ? yield(config) : config
62
+ end
63
+
64
+ def config
65
+ Config
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ # @return [Slack::RealTime::Socket]
72
+ def build_socket
73
+ fail ClientAlreadyStartedError if started?
74
+ @options = web_client.rtm_start
75
+
76
+ socket_class.new(@options.fetch('url'), socket_options)
77
+ end
78
+
79
+ def socket_options
80
+ socket_options = {}
81
+ socket_options[:ping] = websocket_ping if websocket_ping
82
+ socket_options[:proxy] = websocket_proxy if websocket_proxy
83
+ socket_options
84
+ end
85
+
86
+ def run_loop(socket)
87
+ @socket = socket
88
+
89
+ @socket.connect! do |driver|
90
+ yield driver if block_given?
91
+
92
+ driver.on :open do |event|
93
+ open(event)
94
+ callback(event, :open)
95
+ end
96
+
97
+ driver.on :message do |event|
98
+ dispatch(event)
99
+ end
100
+
101
+ driver.on :close do |event|
102
+ callback(event, :close)
103
+ close(event)
104
+ end
105
+ end
106
+ end
107
+
108
+ attr_reader :callbacks
109
+ def socket_class
110
+ concurrency::Socket
111
+ end
112
+
113
+ def send_json(data)
114
+ fail ClientNotStartedError unless started?
115
+ @socket.send_data(data.to_json)
116
+ end
117
+
118
+ def open(_event)
119
+ end
120
+
121
+ def close(_event)
122
+ socket = @socket
123
+ @socket = nil
124
+
125
+ [socket, socket_class].each do |s|
126
+ s.close if s.respond_to?(:close)
127
+ end
128
+ end
129
+
130
+ def callback(event, type)
131
+ callbacks = self.callbacks[type.to_s]
132
+ return false unless callbacks
133
+ callbacks.each do |c|
134
+ c.call(event)
135
+ end
136
+ end
137
+
138
+ def dispatch(event)
139
+ return false unless event.data
140
+ data = JSON.parse(event.data)
141
+ type = data['type']
142
+ return false unless type
143
+ callbacks = self.callbacks[type.to_s]
144
+ return false unless callbacks
145
+ callbacks.each do |c|
146
+ c.call(data)
147
+ end
148
+ true
149
+ end
150
+ end
151
+ end
152
+ end
@@ -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,93 @@
1
+ require 'websocket/driver'
2
+ require 'socket'
3
+ require 'forwardable'
4
+ require 'celluloid/current'
5
+ require 'celluloid/io'
6
+
7
+ module Slack
8
+ module RealTime
9
+ module Concurrency
10
+ module Celluloid
11
+ class Socket < Slack::RealTime::Socket
12
+ include ::Celluloid::IO
13
+ include ::Celluloid::Internals::Logger
14
+
15
+ BLOCK_SIZE = 4096
16
+
17
+ extend ::Forwardable
18
+ def_delegator :socket, :write
19
+ def_delegators :driver, :text, :binary, :close
20
+
21
+ attr_reader :socket
22
+
23
+ def initialize(*args)
24
+ super
25
+ @driver = build_driver
26
+ end
27
+
28
+ # @yieldparam [WebSocket::Driver] driver
29
+ def connect!
30
+ super
31
+
32
+ driver.start
33
+ future.run_loop
34
+ end
35
+
36
+ def run_loop
37
+ loop { read } if socket
38
+ rescue EOFError
39
+ # connection closed
40
+ end
41
+
42
+ def read
43
+ buffer = socket.readpartial(BLOCK_SIZE)
44
+ driver.parse buffer
45
+ end
46
+
47
+ def start_async
48
+ future = yield self if block_given?
49
+
50
+ Actor.new(future)
51
+ end
52
+
53
+ def connected?
54
+ !@connected.nil?
55
+ end
56
+
57
+ protected
58
+
59
+ class Actor
60
+ attr_reader :future
61
+
62
+ def initialize(future)
63
+ @future = future
64
+ end
65
+
66
+ def join
67
+ @future.value
68
+ end
69
+ end
70
+
71
+ def build_socket
72
+ socket = TCPSocket.new(addr, port)
73
+ socket = SSLSocket.new(socket, build_ssl_context) if secure?
74
+ socket
75
+ end
76
+
77
+ def build_ssl_context
78
+ OpenSSL::SSL::SSLContext.new(:TLSv1_2_client)
79
+ end
80
+
81
+ def build_driver
82
+ ::WebSocket::Driver.client(self)
83
+ end
84
+
85
+ def connect
86
+ @socket = build_socket
87
+ @connected = @socket.connect
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ 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
@@ -0,0 +1,55 @@
1
+ module Slack
2
+ module RealTime
3
+ module Config
4
+ class NoConcurrencyError < StandardError; end
5
+
6
+ extend self
7
+
8
+ ATTRIBUTES = [
9
+ :token,
10
+ :websocket_ping,
11
+ :websocket_proxy,
12
+ :concurrency
13
+ ]
14
+
15
+ attr_accessor(*Config::ATTRIBUTES)
16
+
17
+ def reset
18
+ self.websocket_ping = 30
19
+ self.websocket_proxy = nil
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, NameError
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.'
40
+ end
41
+ end
42
+
43
+ class << self
44
+ def configure
45
+ block_given? ? yield(Config) : Config
46
+ end
47
+
48
+ def config
49
+ Config
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ Slack::RealTime::Config.reset
@@ -0,0 +1,81 @@
1
+ module Slack
2
+ module RealTime
3
+ class Socket
4
+ attr_accessor :url
5
+ attr_accessor :options
6
+ attr_reader :driver
7
+
8
+ def initialize(url, options = {})
9
+ @url = url
10
+ @options = options
11
+ @driver = nil
12
+ end
13
+
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
21
+ end
22
+
23
+ def connect!
24
+ return if connected?
25
+
26
+ connect
27
+
28
+ yield driver if block_given?
29
+ end
30
+
31
+ def disconnect!
32
+ driver.close
33
+ end
34
+
35
+ def connected?
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__}."
49
+ end
50
+
51
+ def close(_event)
52
+ @driver = nil
53
+ end
54
+
55
+ protected
56
+
57
+ def addr
58
+ URI(url).host
59
+ end
60
+
61
+ def secure?
62
+ port == URI::HTTPS::DEFAULT_PORT
63
+ end
64
+
65
+ def port
66
+ case (uri = URI(url)).scheme
67
+ when 'wss'.freeze, 'https'.freeze
68
+ URI::HTTPS::DEFAULT_PORT
69
+ when 'ws', 'http'.freeze
70
+ URI::HTTP::DEFAULT_PORT
71
+ else
72
+ uri.port
73
+ end
74
+ end
75
+
76
+ def connect
77
+ fail NotImplementedError, "Expected #{self.class} to implement #{__method__}."
78
+ end
79
+ end
80
+ end
81
+ end