slack-ruby-client 0.12.0 → 0.14.6

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 (301) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +21 -9
  4. data/.rubocop_todo.yml +64 -56
  5. data/.travis.yml +5 -3
  6. data/CHANGELOG.md +90 -32
  7. data/Dangerfile +2 -0
  8. data/Gemfile +10 -4
  9. data/LICENSE.md +1 -1
  10. data/README.md +176 -32
  11. data/Rakefile +2 -1
  12. data/UPGRADING.md +27 -1
  13. data/bin/commands.rb +21 -0
  14. data/bin/commands/admin_apps.rb +27 -0
  15. data/bin/commands/admin_apps_approved.rb +17 -0
  16. data/bin/commands/admin_apps_requests.rb +16 -0
  17. data/bin/commands/admin_apps_restricted.rb +17 -0
  18. data/bin/commands/admin_conversations.rb +17 -0
  19. data/bin/commands/admin_emoji.rb +54 -0
  20. data/bin/commands/admin_inviteRequests.rb +36 -0
  21. data/bin/commands/admin_inviteRequests_approved.rb +16 -0
  22. data/bin/commands/admin_inviteRequests_denied.rb +16 -0
  23. data/bin/commands/admin_teams.rb +27 -0
  24. data/bin/commands/admin_teams_admins.rb +16 -0
  25. data/bin/commands/admin_teams_owners.rb +16 -0
  26. data/bin/commands/admin_teams_settings.rb +64 -0
  27. data/bin/commands/admin_users.rb +97 -0
  28. data/bin/commands/admin_users_session.rb +16 -0
  29. data/bin/commands/api.rb +1 -0
  30. data/bin/commands/apps.rb +15 -0
  31. data/bin/commands/apps_permissions.rb +1 -0
  32. data/bin/commands/apps_permissions_resources.rb +1 -0
  33. data/bin/commands/apps_permissions_scopes.rb +1 -0
  34. data/bin/commands/apps_permissions_users.rb +1 -0
  35. data/bin/commands/auth.rb +1 -0
  36. data/bin/commands/bots.rb +1 -0
  37. data/bin/commands/channels.rb +3 -1
  38. data/bin/commands/chat.rb +48 -9
  39. data/bin/commands/chat_scheduledMessages.rb +18 -0
  40. data/bin/commands/conversations.rb +4 -2
  41. data/bin/commands/dialog.rb +1 -0
  42. data/bin/commands/dnd.rb +4 -3
  43. data/bin/commands/emoji.rb +1 -0
  44. data/bin/commands/files.rb +4 -14
  45. data/bin/commands/files_comments.rb +1 -21
  46. data/bin/commands/files_remote.rb +78 -0
  47. data/bin/commands/groups.rb +2 -1
  48. data/bin/commands/im.rb +2 -1
  49. data/bin/commands/migration.rb +1 -0
  50. data/bin/commands/mpim.rb +2 -1
  51. data/bin/commands/oauth.rb +2 -1
  52. data/bin/commands/oauth_v2.rb +17 -0
  53. data/bin/commands/pins.rb +1 -2
  54. data/bin/commands/reactions.rb +2 -3
  55. data/bin/commands/reminders.rb +1 -0
  56. data/bin/commands/rtm.rb +1 -0
  57. data/bin/commands/search.rb +2 -1
  58. data/bin/commands/stars.rb +1 -0
  59. data/bin/commands/team.rb +2 -0
  60. data/bin/commands/team_profile.rb +1 -0
  61. data/bin/commands/usergroups.rb +2 -1
  62. data/bin/commands/usergroups_users.rb +1 -0
  63. data/bin/commands/users.rb +2 -2
  64. data/bin/commands/users_admin.rb +1 -0
  65. data/bin/commands/users_prefs.rb +1 -0
  66. data/bin/commands/users_profile.rb +1 -0
  67. data/bin/commands/views.rb +48 -0
  68. data/bin/slack +2 -3
  69. data/examples/hi_real_time/Gemfile +1 -0
  70. data/examples/hi_real_time/hi.rb +7 -3
  71. data/examples/hi_real_time_and_web/Gemfile +1 -0
  72. data/examples/hi_real_time_and_web/hi.rb +7 -3
  73. data/examples/hi_real_time_async_async/Gemfile +7 -0
  74. data/examples/hi_real_time_async_async/Procfile +2 -0
  75. data/examples/hi_real_time_async_async/hi.rb +41 -0
  76. data/examples/hi_real_time_async_celluloid/Gemfile +1 -0
  77. data/examples/hi_real_time_async_celluloid/hi.rb +7 -3
  78. data/examples/hi_real_time_async_eventmachine/Gemfile +1 -0
  79. data/examples/hi_real_time_async_eventmachine/hi.rb +7 -3
  80. data/examples/hi_web/Gemfile +1 -0
  81. data/examples/hi_web/hi.rb +1 -0
  82. data/examples/new_ticket/Gemfile +1 -0
  83. data/examples/new_ticket/new_ticket.rb +1 -0
  84. data/lib/slack-ruby-client.rb +8 -2
  85. data/lib/slack.rb +1 -0
  86. data/lib/slack/config.rb +1 -0
  87. data/lib/slack/events/config.rb +32 -0
  88. data/lib/slack/events/request.rb +66 -0
  89. data/lib/slack/logger.rb +6 -5
  90. data/lib/slack/messages/formatting.rb +1 -0
  91. data/lib/slack/messages/message.rb +1 -0
  92. data/lib/slack/real_time/api/message.rb +3 -1
  93. data/lib/slack/real_time/api/message_id.rb +1 -0
  94. data/lib/slack/real_time/api/ping.rb +5 -2
  95. data/lib/slack/real_time/api/typing.rb +3 -1
  96. data/lib/slack/real_time/client.rb +97 -28
  97. data/lib/slack/real_time/concurrency.rb +2 -0
  98. data/lib/slack/real_time/concurrency/async.rb +142 -0
  99. data/lib/slack/real_time/concurrency/celluloid.rb +33 -9
  100. data/lib/slack/real_time/concurrency/eventmachine.rb +32 -7
  101. data/lib/slack/real_time/config.rb +7 -2
  102. data/lib/slack/real_time/models.rb +1 -0
  103. data/lib/slack/real_time/models/base.rb +1 -0
  104. data/lib/slack/real_time/models/bot.rb +1 -0
  105. data/lib/slack/real_time/models/channel.rb +1 -0
  106. data/lib/slack/real_time/models/group.rb +1 -0
  107. data/lib/slack/real_time/models/im.rb +1 -0
  108. data/lib/slack/real_time/models/team.rb +1 -0
  109. data/lib/slack/real_time/models/user.rb +1 -0
  110. data/lib/slack/real_time/socket.rb +42 -13
  111. data/lib/slack/real_time/stores.rb +1 -0
  112. data/lib/slack/real_time/stores/base.rb +1 -0
  113. data/lib/slack/real_time/stores/starter.rb +11 -0
  114. data/lib/slack/real_time/stores/store.rb +28 -25
  115. data/lib/slack/version.rb +2 -1
  116. data/lib/slack/web/api/endpoints.rb +41 -0
  117. data/lib/slack/web/api/endpoints/admin_apps.rb +42 -0
  118. data/lib/slack/web/api/endpoints/admin_apps_approved.rb +35 -0
  119. data/lib/slack/web/api/endpoints/admin_apps_requests.rb +33 -0
  120. data/lib/slack/web/api/endpoints/admin_apps_restricted.rb +35 -0
  121. data/lib/slack/web/api/endpoints/admin_conversations.rb +30 -0
  122. data/lib/slack/web/api/endpoints/admin_emoji.rb +88 -0
  123. data/lib/slack/web/api/endpoints/admin_inviteRequests.rb +61 -0
  124. data/lib/slack/web/api/endpoints/admin_inviteRequests_approved.rb +33 -0
  125. data/lib/slack/web/api/endpoints/admin_inviteRequests_denied.rb +33 -0
  126. data/lib/slack/web/api/endpoints/admin_teams.rb +50 -0
  127. data/lib/slack/web/api/endpoints/admin_teams_admins.rb +34 -0
  128. data/lib/slack/web/api/endpoints/admin_teams_owners.rb +34 -0
  129. data/lib/slack/web/api/endpoints/admin_teams_settings.rb +99 -0
  130. data/lib/slack/web/api/endpoints/admin_users.rb +163 -0
  131. data/lib/slack/web/api/endpoints/admin_users_session.rb +28 -0
  132. data/lib/slack/web/api/endpoints/api.rb +1 -0
  133. data/lib/slack/web/api/endpoints/apps.rb +27 -0
  134. data/lib/slack/web/api/endpoints/apps_permissions.rb +1 -0
  135. data/lib/slack/web/api/endpoints/apps_permissions_resources.rb +1 -0
  136. data/lib/slack/web/api/endpoints/apps_permissions_scopes.rb +1 -0
  137. data/lib/slack/web/api/endpoints/apps_permissions_users.rb +1 -0
  138. data/lib/slack/web/api/endpoints/auth.rb +1 -0
  139. data/lib/slack/web/api/endpoints/bots.rb +1 -0
  140. data/lib/slack/web/api/endpoints/channels.rb +3 -0
  141. data/lib/slack/web/api/endpoints/chat.rb +99 -12
  142. data/lib/slack/web/api/endpoints/chat_scheduledMessages.rb +38 -0
  143. data/lib/slack/web/api/endpoints/conversations.rb +4 -1
  144. data/lib/slack/web/api/endpoints/dialog.rb +1 -0
  145. data/lib/slack/web/api/endpoints/dnd.rb +3 -1
  146. data/lib/slack/web/api/endpoints/emoji.rb +1 -0
  147. data/lib/slack/web/api/endpoints/files.rb +4 -13
  148. data/lib/slack/web/api/endpoints/files_comments.rb +1 -33
  149. data/lib/slack/web/api/endpoints/files_remote.rb +127 -0
  150. data/lib/slack/web/api/endpoints/groups.rb +1 -0
  151. data/lib/slack/web/api/endpoints/im.rb +1 -0
  152. data/lib/slack/web/api/endpoints/migration.rb +1 -0
  153. data/lib/slack/web/api/endpoints/mpim.rb +1 -0
  154. data/lib/slack/web/api/endpoints/oauth.rb +2 -1
  155. data/lib/slack/web/api/endpoints/oauth_v2.rb +30 -0
  156. data/lib/slack/web/api/endpoints/pins.rb +2 -4
  157. data/lib/slack/web/api/endpoints/reactions.rb +5 -6
  158. data/lib/slack/web/api/endpoints/reminders.rb +1 -0
  159. data/lib/slack/web/api/endpoints/rtm.rb +1 -0
  160. data/lib/slack/web/api/endpoints/search.rb +1 -0
  161. data/lib/slack/web/api/endpoints/stars.rb +1 -0
  162. data/lib/slack/web/api/endpoints/team.rb +3 -0
  163. data/lib/slack/web/api/endpoints/team_profile.rb +1 -0
  164. data/lib/slack/web/api/endpoints/usergroups.rb +1 -0
  165. data/lib/slack/web/api/endpoints/usergroups_users.rb +1 -0
  166. data/lib/slack/web/api/endpoints/users.rb +1 -2
  167. data/lib/slack/web/api/endpoints/users_admin.rb +1 -0
  168. data/lib/slack/web/api/endpoints/users_prefs.rb +1 -0
  169. data/lib/slack/web/api/endpoints/users_profile.rb +1 -0
  170. data/lib/slack/web/api/endpoints/views.rb +97 -0
  171. data/lib/slack/web/api/error.rb +1 -0
  172. data/lib/slack/web/api/errors.rb +566 -0
  173. data/lib/slack/web/api/errors/slack_error.rb +14 -1
  174. data/lib/slack/web/api/errors/too_many_requests_error.rb +1 -0
  175. data/lib/slack/web/api/mixins.rb +1 -0
  176. data/lib/slack/web/api/mixins/channels.id.rb +1 -0
  177. data/lib/slack/web/api/mixins/groups.id.rb +1 -0
  178. data/lib/slack/web/api/mixins/ids.id.rb +4 -1
  179. data/lib/slack/web/api/mixins/users.id.rb +1 -0
  180. data/lib/slack/web/api/mixins/users.search.rb +1 -0
  181. data/lib/slack/web/api/patches/chat.5.postEphemeral-text-or-attachments.patch +5 -3
  182. data/lib/slack/web/api/patches/chat.6.block-kit-support.patch +69 -0
  183. data/lib/slack/web/api/patches/views.1.view-json.patch +40 -0
  184. data/lib/slack/web/api/patches/views.1.views-published.patch +16 -0
  185. data/lib/slack/web/api/templates/command.erb +1 -0
  186. data/lib/slack/web/api/templates/commands.erb +1 -0
  187. data/lib/slack/web/api/templates/endpoints.erb +1 -0
  188. data/lib/slack/web/api/templates/errors.erb +20 -0
  189. data/lib/slack/web/api/templates/method.erb +1 -0
  190. data/lib/slack/web/api/templates/method_spec.erb +1 -0
  191. data/lib/slack/web/client.rb +2 -1
  192. data/lib/slack/web/config.rb +1 -0
  193. data/lib/slack/web/faraday/connection.rb +1 -0
  194. data/lib/slack/web/faraday/request.rb +1 -0
  195. data/lib/slack/web/faraday/response/raise_error.rb +10 -6
  196. data/lib/slack/web/pagination/cursor.rb +4 -0
  197. data/lib/slack_ruby_client.rb +1 -0
  198. data/lib/tasks/git.rake +1 -0
  199. data/lib/tasks/real_time.rake +15 -5
  200. data/lib/tasks/update.rake +1 -0
  201. data/lib/tasks/web.rake +28 -7
  202. data/screenshots/create-app.png +0 -0
  203. data/slack-ruby-client.gemspec +6 -2
  204. data/spec/fixtures/slack/web/views_open_error.yml +76 -0
  205. data/spec/integration/integration_spec.rb +116 -48
  206. data/spec/slack/config_spec.rb +2 -0
  207. data/spec/slack/events/config_spec.rb +33 -0
  208. data/spec/slack/events/request_spec.rb +179 -0
  209. data/spec/slack/messages/formatting_spec.rb +25 -13
  210. data/spec/slack/real_time/api/message_spec.rb +6 -1
  211. data/spec/slack/real_time/api/ping_spec.rb +2 -0
  212. data/spec/slack/real_time/api/typing_spec.rb +5 -1
  213. data/spec/slack/real_time/client_spec.rb +212 -31
  214. data/spec/slack/real_time/concurrency/celluloid_spec.rb +15 -5
  215. data/spec/slack/real_time/concurrency/eventmachine_spec.rb +11 -1
  216. data/spec/slack/real_time/concurrency/it_behaves_like_a_realtime_socket.rb +2 -0
  217. data/spec/slack/real_time/event_handlers/bot_spec.rb +2 -1
  218. data/spec/slack/real_time/event_handlers/channel_spec.rb +9 -6
  219. data/spec/slack/real_time/event_handlers/event_handlers_spec.rb +2 -1
  220. data/spec/slack/real_time/event_handlers/group_spec.rb +5 -4
  221. data/spec/slack/real_time/event_handlers/im_spec.rb +4 -3
  222. data/spec/slack/real_time/event_handlers/team_spec.rb +3 -1
  223. data/spec/slack/real_time/event_handlers/user_spec.rb +1 -0
  224. data/spec/slack/real_time/rtm_connect_spec.rb +1 -0
  225. data/spec/slack/real_time/rtm_start_spec.rb +1 -0
  226. data/spec/slack/real_time/store_spec.rb +2 -1
  227. data/spec/slack/slack_spec.rb +37 -5
  228. data/spec/slack/version_spec.rb +2 -1
  229. data/spec/slack/web/api/endpoints/admin_apps_approved_spec.rb +8 -0
  230. data/spec/slack/web/api/endpoints/admin_apps_requests_spec.rb +8 -0
  231. data/spec/slack/web/api/endpoints/admin_apps_restricted_spec.rb +8 -0
  232. data/spec/slack/web/api/endpoints/admin_apps_spec.rb +8 -0
  233. data/spec/slack/web/api/endpoints/admin_conversations_spec.rb +13 -0
  234. data/spec/slack/web/api/endpoints/admin_emoji_spec.rb +37 -0
  235. data/spec/slack/web/api/endpoints/admin_inviteRequests_approved_spec.rb +8 -0
  236. data/spec/slack/web/api/endpoints/admin_inviteRequests_denied_spec.rb +8 -0
  237. data/spec/slack/web/api/endpoints/admin_inviteRequests_spec.rb +18 -0
  238. data/spec/slack/web/api/endpoints/admin_teams_admins_spec.rb +13 -0
  239. data/spec/slack/web/api/endpoints/admin_teams_owners_spec.rb +13 -0
  240. data/spec/slack/web/api/endpoints/admin_teams_settings_spec.rb +53 -0
  241. data/spec/slack/web/api/endpoints/admin_teams_spec.rb +16 -0
  242. data/spec/slack/web/api/endpoints/admin_users_session_spec.rb +13 -0
  243. data/spec/slack/web/api/endpoints/admin_users_spec.rb +75 -0
  244. data/spec/slack/web/api/endpoints/api_spec.rb +1 -0
  245. data/spec/slack/web/api/endpoints/apps_permissions_resources_spec.rb +1 -0
  246. data/spec/slack/web/api/endpoints/apps_permissions_scopes_spec.rb +1 -0
  247. data/spec/slack/web/api/endpoints/apps_permissions_spec.rb +3 -2
  248. data/spec/slack/web/api/endpoints/apps_permissions_users_spec.rb +4 -3
  249. data/spec/slack/web/api/endpoints/apps_spec.rb +16 -0
  250. data/spec/slack/web/api/endpoints/bots_spec.rb +1 -0
  251. data/spec/slack/web/api/endpoints/chat_scheduledMessages_spec.rb +8 -0
  252. data/spec/slack/web/api/endpoints/conversations_spec.rb +2 -1
  253. data/spec/slack/web/api/endpoints/custom_specs/auth_spec.rb +5 -1
  254. data/spec/slack/web/api/endpoints/custom_specs/channels_spec.rb +2 -0
  255. data/spec/slack/web/api/endpoints/custom_specs/chat_spec.rb +112 -36
  256. data/spec/slack/web/api/endpoints/custom_specs/dialog_spec.rb +12 -4
  257. data/spec/slack/web/api/endpoints/custom_specs/groups_spec.rb +2 -0
  258. data/spec/slack/web/api/endpoints/custom_specs/users_spec.rb +6 -1
  259. data/spec/slack/web/api/endpoints/custom_specs/views_spec.rb +95 -0
  260. data/spec/slack/web/api/endpoints/dnd_spec.rb +6 -0
  261. data/spec/slack/web/api/endpoints/emoji_spec.rb +1 -0
  262. data/spec/slack/web/api/endpoints/files_comments_spec.rb +1 -19
  263. data/spec/slack/web/api/endpoints/files_remote_spec.rb +24 -0
  264. data/spec/slack/web/api/endpoints/files_spec.rb +1 -0
  265. data/spec/slack/web/api/endpoints/im_spec.rb +1 -0
  266. data/spec/slack/web/api/endpoints/migration_spec.rb +1 -0
  267. data/spec/slack/web/api/endpoints/mpim_spec.rb +1 -0
  268. data/spec/slack/web/api/endpoints/oauth_spec.rb +1 -0
  269. data/spec/slack/web/api/endpoints/oauth_v2_spec.rb +13 -0
  270. data/spec/slack/web/api/endpoints/pins_spec.rb +5 -1
  271. data/spec/slack/web/api/endpoints/reactions_spec.rb +8 -1
  272. data/spec/slack/web/api/endpoints/reminders_spec.rb +1 -0
  273. data/spec/slack/web/api/endpoints/rtm_spec.rb +1 -0
  274. data/spec/slack/web/api/endpoints/search_spec.rb +1 -0
  275. data/spec/slack/web/api/endpoints/stars_spec.rb +1 -0
  276. data/spec/slack/web/api/endpoints/team_profile_spec.rb +1 -0
  277. data/spec/slack/web/api/endpoints/team_spec.rb +1 -0
  278. data/spec/slack/web/api/endpoints/usergroups_spec.rb +1 -0
  279. data/spec/slack/web/api/endpoints/usergroups_users_spec.rb +1 -0
  280. data/spec/slack/web/api/endpoints/users_admin_spec.rb +1 -0
  281. data/spec/slack/web/api/endpoints/users_prefs_spec.rb +1 -0
  282. data/spec/slack/web/api/endpoints/users_profile_spec.rb +1 -0
  283. data/spec/slack/web/api/endpoints/views_spec.rb +29 -0
  284. data/spec/slack/web/api/error_spec.rb +4 -2
  285. data/spec/slack/web/api/errors/service_unavailable_spec.rb +6 -3
  286. data/spec/slack/web/api/errors/slack_error_spec.rb +26 -2
  287. data/spec/slack/web/api/mixins/channels_spec.rb +17 -7
  288. data/spec/slack/web/api/mixins/groups_spec.rb +17 -7
  289. data/spec/slack/web/api/mixins/users_spec.rb +17 -8
  290. data/spec/slack/web/api/pagination/cursor_spec.rb +40 -10
  291. data/spec/slack/web/client_spec.rb +45 -18
  292. data/spec/slack/web/faraday/response/raise_error_spec.rb +41 -7
  293. data/spec/spec_helper.rb +8 -1
  294. data/spec/support/queue_with_timeout.rb +5 -4
  295. data/spec/support/real_time/concurrency/mock.rb +1 -0
  296. data/spec/support/real_time/connected_client.rb +9 -3
  297. data/spec/support/real_time/event.rb +1 -0
  298. data/spec/support/token.rb +1 -0
  299. data/spec/support/vcr.rb +1 -0
  300. metadata +149 -9
  301. data/screenshots/register-bot.png +0 -0
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Concurrency
5
+ autoload :Async, 'slack/real_time/concurrency/async'
4
6
  autoload :Eventmachine, 'slack/real_time/concurrency/eventmachine'
5
7
  autoload :Celluloid, 'slack/real_time/concurrency/celluloid'
6
8
  end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+ require 'async/websocket'
3
+ require 'async/notification'
4
+ require 'async/clock'
5
+
6
+ module Slack
7
+ module RealTime
8
+ module Concurrency
9
+ module Async
10
+ class Client < ::Async::WebSocket::Client
11
+ extend ::Forwardable
12
+ def_delegators :@driver, :on, :text, :binary, :emit
13
+ end
14
+
15
+ class Socket < Slack::RealTime::Socket
16
+ attr_reader :client
17
+
18
+ def start_sync(client)
19
+ start_reactor(client).wait
20
+ end
21
+
22
+ def start_async(client)
23
+ Thread.new do
24
+ start_reactor(client)
25
+ end
26
+ end
27
+
28
+ def start_reactor(client)
29
+ Async do |task|
30
+ @restart = ::Async::Notification.new
31
+
32
+ if client.run_ping?
33
+ @ping_task = task.async do |subtask|
34
+ subtask.annotate "#{client} keep-alive"
35
+
36
+ # The timer task will naturally exit after the driver is set to nil.
37
+ while @restart
38
+ subtask.sleep client.websocket_ping_timer
39
+ client.run_ping! if @restart
40
+ end
41
+ end
42
+ end
43
+
44
+ while @restart
45
+ @client_task&.stop
46
+
47
+ @client_task = task.async do |subtask|
48
+ begin
49
+ subtask.annotate "#{client} run-loop"
50
+ client.run_loop
51
+ rescue ::Async::Wrapper::Cancelled => e
52
+ # Will get restarted by ping worker.
53
+ rescue StandardError => e
54
+ client.logger.error(subtask.to_s) { e.message }
55
+ end
56
+ end
57
+
58
+ @restart.wait
59
+ end
60
+
61
+ @ping_task&.stop
62
+ end
63
+ end
64
+
65
+ def restart_async(_client, new_url)
66
+ @url = new_url
67
+ @last_message_at = current_time
68
+
69
+ @restart&.signal
70
+ end
71
+
72
+ def current_time
73
+ ::Async::Clock.now
74
+ end
75
+
76
+ def connect!
77
+ super
78
+ run_loop
79
+ end
80
+
81
+ # Kill the restart/ping loop.
82
+ def disconnect!
83
+ super
84
+ ensure
85
+ if (restart = @restart)
86
+ @restart = nil
87
+ restart.signal
88
+ end
89
+ end
90
+
91
+ # Close the socket.
92
+ def close
93
+ super
94
+ ensure
95
+ if @socket
96
+ @socket.close
97
+ @socket = nil
98
+ end
99
+ end
100
+
101
+ def run_loop
102
+ while @driver&.next_event
103
+ # $stderr.puts event.inspect
104
+ end
105
+ end
106
+
107
+ protected
108
+
109
+ def build_ssl_context
110
+ OpenSSL::SSL::SSLContext.new(:TLSv1_2_client).tap do |ctx|
111
+ ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
112
+ end
113
+ end
114
+
115
+ def build_endpoint
116
+ endpoint = ::Async::IO::Endpoint.tcp(addr, port)
117
+ if secure?
118
+ endpoint = ::Async::IO::SSLEndpoint.new(endpoint, ssl_context: build_ssl_context)
119
+ end
120
+ endpoint
121
+ end
122
+
123
+ def connect_socket
124
+ build_endpoint.connect
125
+ end
126
+
127
+ def connect
128
+ @socket = connect_socket
129
+ @driver = Client.new(@socket, url)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ if Gem::Version.new(Async::WebSocket::VERSION) >= Gem::Version.new('0.9.0')
138
+ raise(
139
+ "Incompatible version of async-websocket, #{Async::WebSocket::VERSION}, " \
140
+ "use \"gem 'async-websocket', '~> 0.8.0'\"."
141
+ )
142
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'websocket/driver'
2
3
  require 'socket'
3
4
  require 'forwardable'
@@ -36,30 +37,34 @@ module Slack
36
37
  loop { read } if socket
37
38
  rescue EOFError, Errno::ECONNRESET, Errno::EPIPE => e
38
39
  logger.debug("#{self.class}##{__method__}") { e }
39
- driver.emit(:close, WebSocket::Driver::CloseEvent.new(1001, 'server closed connection')) unless @closing
40
- ensure
41
- begin
42
- current_actor.terminate if current_actor.alive?
43
- rescue StandardError
44
- nil
40
+ unless @closing
41
+ driver.emit(
42
+ :close,
43
+ WebSocket::Driver::CloseEvent.new(1001, 'server closed connection')
44
+ )
45
45
  end
46
46
  end
47
47
 
48
+ def disconnect!
49
+ super
50
+ @ping_timer&.cancel
51
+ end
52
+
48
53
  def close
49
54
  @closing = true
50
- driver.close
51
55
  super
52
56
  end
53
57
 
54
58
  def read
55
59
  buffer = socket.readpartial(BLOCK_SIZE)
56
60
  raise EOFError unless buffer && !buffer.empty?
61
+
57
62
  async.handle_read(buffer)
58
63
  end
59
64
 
60
65
  def handle_read(buffer)
61
66
  logger.debug("#{self.class}##{__method__}") { buffer }
62
- driver.parse buffer
67
+ driver&.parse buffer
63
68
  end
64
69
 
65
70
  def write(data)
@@ -69,15 +74,34 @@ module Slack
69
74
 
70
75
  def start_async(client)
71
76
  @client = client
77
+ Actor.new(future.run_ping_loop)
72
78
  Actor.new(future.run_client_loop)
73
79
  end
74
80
 
75
81
  def run_client_loop
76
82
  @client.run_loop
83
+ rescue StandardError => e
84
+ logger.debug("#{self.class}##{__method__}") { e }
85
+ raise e
86
+ end
87
+
88
+ def run_ping_loop
89
+ return unless @client.run_ping?
90
+
91
+ @ping_timer = every @client.websocket_ping_timer do
92
+ @client.run_ping!
93
+ end
94
+ end
95
+
96
+ def restart_async(client, new_url)
97
+ @last_message_at = current_time
98
+ @url = new_url
99
+ @client = client
100
+ Actor.new(future.run_client_loop)
77
101
  end
78
102
 
79
103
  def connected?
80
- !@connected.nil?
104
+ !@connected.nil? && !@driver.nil?
81
105
  end
82
106
 
83
107
  protected
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'faye/websocket'
2
3
  require 'eventmachine'
3
4
 
@@ -6,12 +7,10 @@ module Slack
6
7
  module Concurrency
7
8
  module Eventmachine
8
9
  class Client < Faye::WebSocket::Client
9
- attr_reader :logger
10
- protected :logger
11
-
12
10
  def initialize(url, protocols = nil, options = {})
13
- @logger = options.delete(:logger) || Slack::RealTime::Config.logger || Slack::Config.logger
14
- super
11
+ @logger =
12
+ options.fetch(:logger) || Slack::RealTime::Config.logger || Slack::Config.logger
13
+ super url, protocols, options.except(:logger)
15
14
  end
16
15
 
17
16
  def parse(data)
@@ -23,15 +22,41 @@ module Slack
23
22
  logger.debug("#{self.class}##{__method__}") { data }
24
23
  super data
25
24
  end
25
+
26
+ protected
27
+
28
+ attr_reader :logger
26
29
  end
27
30
 
28
31
  class Socket < Slack::RealTime::Socket
29
32
  def start_async(client)
30
- thread = ensure_reactor_running
33
+ @thread = ensure_reactor_running
34
+
35
+ if client.run_ping?
36
+ EventMachine.add_periodic_timer client.websocket_ping_timer do
37
+ client.run_ping!
38
+ end
39
+ end
31
40
 
32
41
  client.run_loop
33
42
 
34
- thread
43
+ @thread
44
+ end
45
+
46
+ def restart_async(client, new_url)
47
+ @url = new_url
48
+ @last_message_at = current_time
49
+ @thread = ensure_reactor_running
50
+
51
+ client.run_loop
52
+
53
+ @thread
54
+ end
55
+
56
+ def disconnect!
57
+ super
58
+ EventMachine.stop_event_loop if EventMachine.reactor_running?
59
+ @thread = nil
35
60
  end
36
61
 
37
62
  def send_data(message)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Config
@@ -36,7 +37,7 @@ module Slack
36
37
  private
37
38
 
38
39
  def detect_concurrency
39
- %i[Eventmachine Celluloid].each do |concurrency|
40
+ %i[Async Eventmachine Celluloid].each do |concurrency|
40
41
  begin
41
42
  return Slack::RealTime::Concurrency.const_get(concurrency)
42
43
  rescue LoadError, NameError
@@ -44,7 +45,11 @@ module Slack
44
45
  end
45
46
  end
46
47
 
47
- raise NoConcurrencyError, 'Missing concurrency. Add faye-websocket or celluloid-io to your Gemfile.'
48
+ raise(
49
+ NoConcurrencyError,
50
+ 'Missing concurrency. Add async-websocket, faye-websocket ' \
51
+ 'or celluloid-io to your Gemfile.'
52
+ )
48
53
  end
49
54
  end
50
55
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require_relative 'models/base'
2
3
  require_relative 'models/user'
3
4
  require_relative 'models/bot'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  module Models
@@ -1,25 +1,25 @@
1
+ # frozen_string_literal: true
1
2
  module Slack
2
3
  module RealTime
3
4
  class Socket
4
5
  attr_accessor :url
5
6
  attr_accessor :options
6
7
  attr_reader :driver
7
- attr_reader :logger
8
- protected :logger
9
8
 
10
9
  def initialize(url, options = {})
11
10
  @url = url
12
11
  @options = options
13
12
  @driver = nil
14
13
  @logger = options.delete(:logger) || Slack::RealTime::Config.logger || Slack::Config.logger
14
+ @last_message_at = nil
15
15
  end
16
16
 
17
17
  def send_data(message)
18
18
  logger.debug("#{self.class}##{__method__}") { message }
19
19
  case message
20
- when Numeric then driver.text(message.to_s)
21
- when String then driver.text(message)
22
- when Array then driver.binary(message)
20
+ when Numeric then @driver.text(message.to_s)
21
+ when String then @driver.text(message)
22
+ when Array then @driver.binary(message)
23
23
  else false
24
24
  end
25
25
  end
@@ -28,24 +28,31 @@ module Slack
28
28
  return if connected?
29
29
 
30
30
  connect
31
- logger.debug("#{self.class}##{__method__}") { driver.class }
31
+ logger.debug("#{self.class}##{__method__}") { @driver.class }
32
+
33
+ @driver.on :message do
34
+ @last_message_at = current_time
35
+ end
32
36
 
33
- yield driver if block_given?
37
+ yield @driver if block_given?
34
38
  end
35
39
 
40
+ # Gracefully shut down the connection.
36
41
  def disconnect!
37
- driver.close
42
+ @driver.close
43
+ ensure
44
+ close
38
45
  end
39
46
 
40
47
  def connected?
41
- !driver.nil?
48
+ !@driver.nil?
42
49
  end
43
50
 
44
51
  def start_sync(client)
45
52
  thread = start_async(client)
46
- thread.join if thread
53
+ thread&.join
47
54
  rescue Interrupt
48
- thread.exit if thread
55
+ thread&.exit
49
56
  end
50
57
 
51
58
  # @return [#join]
@@ -53,12 +60,34 @@ module Slack
53
60
  raise NotImplementedError, "Expected #{self.class} to implement #{__method__}."
54
61
  end
55
62
 
63
+ def restart_async(_client, _url)
64
+ raise NotImplementedError, "Expected #{self.class} to implement #{__method__}."
65
+ end
66
+
67
+ def time_since_last_message
68
+ return 0 unless @last_message_at
69
+
70
+ current_time - @last_message_at
71
+ end
72
+
73
+ def current_time
74
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
75
+ end
76
+
56
77
  def close
78
+ # When you call `driver.emit(:close)`, it will typically end up calling `client.close`
79
+ # which will call `@socket.close` and end up back here. In order to break this infinite
80
+ # recursion, we check and set `@driver = nil` before invoking `client.close`.
81
+ return unless (driver = @driver)
82
+
57
83
  @driver = nil
84
+ driver.emit(:close)
58
85
  end
59
86
 
60
87
  protected
61
88
 
89
+ attr_reader :logger
90
+
62
91
  def addr
63
92
  URI(url).host
64
93
  end
@@ -69,9 +98,9 @@ module Slack
69
98
 
70
99
  def port
71
100
  case (uri = URI(url)).scheme
72
- when 'wss'.freeze, 'https'.freeze
101
+ when 'wss', 'https'
73
102
  URI::HTTPS::DEFAULT_PORT
74
- when 'ws', 'http'.freeze
103
+ when 'ws', 'http'
75
104
  URI::HTTP::DEFAULT_PORT
76
105
  else
77
106
  uri.port