Class: SlackSocketModeBot

Inherits:
Object
  • Object
show all
Defined in:
lib/slack_socket_mode_bot.rb,
lib/slack_socket_mode_bot/version.rb,
lib/slack_socket_mode_bot/test_mode.rb,
sig/slack_socket_mode_bot.rbs

Defined Under Namespace

Classes: Error, SimpleWebSocket, TestMode

Constant Summary collapse

API_BASE =
"https://slack.com/api/"
RETRY_AFTER_CAP =

Max seconds to sleep on a 429 Retry-After; #call may run in the event loop.

60
SELECT_TIMEOUT =

#run wakes at least this often so an empty watch set never blocks forever.

30
VERSION =
"0.9.3"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(token:, app_token: nil, num_of_connections: 4, debug: false, logger: nil) {|arg0| ... } ⇒ SlackSocketModeBot

: (token: String, ?app_token: String, ?num_of_connections: Integer, ?debug: boolean, ?logger: Logger) { (untyped) -> untyped } -> void

Yields:

Yield Parameters:

  • arg0 (Object)

Yield Returns:

  • (Object)

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/slack_socket_mode_bot.rb', line 34

def initialize(token:, app_token: nil, num_of_connections: 4, debug: false, logger: nil, &callback)
  @token = token
  @app_token = app_token
  @conns = []
  @debug = debug
  @logger = logger
  @events = {}
  @callback = callback
  @test_mode = nil
  if token == "test"
    # Offline test double; #call is stubbed and dispatch is driven without connecting.
    require_relative "slack_socket_mode_bot/test_mode"
    @test_mode = TestMode.new(self)
  end
  # app_token and the handler come as a pair, else the bot hangs or crashes on the first event.
  raise ArgumentError, "app_token is required on Socket Mode" if callback && !app_token
  raise ArgumentError, "block is required on Socket Mode" if app_token && !callback
  # No app token: Web API calls only, no Socket Mode connections.
  @num_of_connections = app_token ? num_of_connections : 0
  replenish_connections unless test_mode?
end

Instance Attribute Details

#test_mode ⇒ TestMode? (readonly)

The offline test double when built with token: "test", otherwise nil.



57
58
59
# File 'lib/slack_socket_mode_bot.rb', line 57

def test_mode
  @test_mode
end

Instance Method Details

#call(method, data, token: @token) ⇒ Object

: (String method, untyped data, ?token: String) -> untyped



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/slack_socket_mode_bot.rb', line 65

def call(method, data, token: @token)
  return @test_mode.call(method, data) if @test_mode

  url = URI(API_BASE + method)
  body = JSON.generate(data)
  headers = {
    "Content-type" => "application/json; charset=utf-8",
    "Authorization" => "Bearer " + token,
  }

  retries = 0
  begin
    res = Net::HTTP.post(url, body, headers)

    case res
    when Net::HTTPSuccess
      json = JSON.parse(res.body, symbolize_names: true)
      raise Error, json[:error] unless json[:ok]
      json
    when Net::HTTPTooManyRequests
      # Rejected, not processed: safe to retry after Retry-After seconds.
      raise Retry, Integer(res["retry-after"] || retries + 1)
    else
      # 5xx etc.: may already be processed, so don't retry; just don't crash.
      raise Error, "HTTP #{ res.code } #{ res.message }"
    end
  rescue Socket::ResolutionError, Net::OpenTimeout
    # Never sent (DNS / connect failure): safe to retry.
    retries += 1
    raise if retries >= 3
    sleep 1
    retry
  rescue Retry => e
    # Don't block the event loop on an absurdly long wait.
    retries += 1
    raise Error, "rate limited (retry-after: #{ e.wait }s)" if retries >= 3 || e.wait > RETRY_AFTER_CAP
    sleep e.wait
    retry
  end
end

#run ⇒ void

This method returns an undefined value.

: -> bot



202
203
204
205
206
207
# File 'lib/slack_socket_mode_bot.rb', line 202

def run
  while true
    read_ios, write_ios = step
    IO.select(read_ios, write_ios, nil, SELECT_TIMEOUT)
  end
end

#step ⇒ [ Array[IO], Array[IO] ]

: -> [Array[IO], Array[IO]]

Raises:



193
194
195
196
197
198
199
# File 'lib/slack_socket_mode_bot.rb', line 193

def step
  raise Error, "not supported in test mode" if @test_mode
  read_ios, write_ios = [], []
  @conns.select! {|ws| ws.step(read_ios, write_ios) }
  replenish_connections
  return read_ios, write_ios
end

#test_mode? ⇒ Boolean

: -> bool



60
61
62
# File 'lib/slack_socket_mode_bot.rb', line 60

def test_mode?
  !!@test_mode
end