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 =

Returns:

  • (String)
"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

Parameters:

  • token: (String)
  • app_token: (String, nil) (defaults to: nil)
  • num_of_connections: (Integer) (defaults to: 4)
  • debug: (Boolean) (defaults to: false)
  • logger: (Logger, nil) (defaults to: nil)

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_modeTestMode? (readonly)

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

Returns:



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

Parameters:

  • method (String)
  • data (Object)
  • token: (String) (defaults to: @token)

Returns:

  • (Object)


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

#runvoid

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]]

Returns:

  • ([ 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

Returns:

  • (Boolean)


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

def test_mode?
  !!@test_mode
end