Class: Lich::DragonRealms::SlackBot
- Inherits:
-
Object
- Object
- Lich::DragonRealms::SlackBot
- Defined in:
- documented/dragonrealms/commons/slackbot.rb
Overview
Represents a Slack bot for interacting with the Slack API.
Defined Under Namespace
Classes: ApiError, Error, NetworkError, ThrottlingError
Constant Summary collapse
- LNET_CONNECTION_TIMEOUT =
The timeout duration for lnet connection attempts (in seconds).
30- LNET_ACTIVITY_TIMEOUT =
Consider lnet connection stale if no activity for 2 minutes The duration to consider lnet connection stale if no activity occurs (in seconds).
120- BASE_RETRY_DELAY_SECONDS =
Base delay for exponential backoff on API retries. Slack recommends waiting at least 30 seconds before retrying after rate limits. See: api.slack.com/docs/rate-limits The base delay for exponential backoff on API retries (in seconds).
30- MAX_RETRY_DELAY_SECONDS =
Maximum delay before giving up on retries (2 minutes) The maximum delay before giving up on retries (in seconds).
120
Instance Attribute Summary collapse
-
#error_message ⇒ Object
readonly
Returns the value of attribute error_message.
Instance Method Summary collapse
- #authed?(token) ⇒ Boolean
-
#direct_message(username, message) ⇒ void
Sends a direct message to a specified user on Slack.
-
#find_token ⇒ Boolean
Attempts to find a valid Slack token from known lichbots.
-
#get_dm_channel(username) ⇒ String?
Retrieves the direct message channel ID for a specified user.
-
#initialize ⇒ SlackBot
constructor
Initializes a new instance of SlackBot.
-
#initialized? ⇒ Boolean
Checks if the Slack bot has been initialized.
- #lnet_connected? ⇒ Boolean
-
#post(method, params) ⇒ Hash
Sends a POST request to the Slack API.
-
#request_token(lichbot) ⇒ String, false
Requests a Slack token from the specified lichbot.
Constructor Details
#initialize ⇒ SlackBot
Initializes a new instance of SlackBot.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 42 def initialize @api_url = 'https://slack.com/api/' @initialized = false @error_message = nil unless authed?(UserVars.slack_token) unless lnet_connected? unless Script.running?('lnet') unless Script.exists?('lnet') @error_message = "lnet.lic not found - cannot retrieve Slack token" return end start_script('lnet') end start_time = Time.now until lnet_connected? if (Time.now - start_time) > LNET_CONNECTION_TIMEOUT @error_message = "lnet did not connect within #{LNET_CONNECTION_TIMEOUT} seconds." return end pause 1 end end @lnet = (Script.running + Script.hidden).find { |val| val.name == 'lnet' } unless find_token @error_message = "Unable to locate a Slack token" return end end begin @users_list = post('users.list', { 'token' => UserVars.slack_token }) rescue ApiError => e Lich.log "error fetching user list: #{e.}" @users_list = { 'members' => [] } end @initialized = true end |
Instance Attribute Details
#error_message ⇒ Object (readonly)
Returns the value of attribute error_message.
20 21 22 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 20 def @error_message end |
Instance Method Details
#authed?(token) ⇒ Boolean
104 105 106 107 108 109 110 111 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 104 def authed?(token) return false unless token begin post('auth.test', { 'token' => token })['ok'] rescue ApiError, NetworkError false end end |
#direct_message(username, message) ⇒ void
This method returns an undefined value.
Sends a direct message to a specified user on Slack.
212 213 214 215 216 217 218 219 220 221 222 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 212 def (username, ) begin dm_channel = get_dm_channel(username) raise Error, "Could not find DM channel for #{username}" unless dm_channel params = { 'token' => UserVars.slack_token, 'channel' => dm_channel, 'text' => "#{checkname}: #{}", 'as_user' => true } post('chat.postMessage', params) rescue Error => e Lich.log "Failed to send Slack message to #{username}: #{e.}" end end |
#find_token ⇒ Boolean
Attempts to find a valid Slack token from known lichbots.
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 137 def find_token lichbots = %w[Quilsilgas] echo 'Looking for a token...' pause until @lnet lichbots.any? do |bot| token = request_token(bot) authed = authed?(token) if token UserVars.slack_token = token if token && authed authed end end |
#get_dm_channel(username) ⇒ String?
Retrieves the direct message channel ID for a specified user.
227 228 229 230 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 227 def get_dm_channel(username) user = @users_list['members'].find { |u| u['name'] == username } user ? user['id'] : nil end |
#initialized? ⇒ Boolean
Checks if the Slack bot has been initialized.
85 86 87 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 85 def initialized? @initialized end |
#lnet_connected? ⇒ Boolean
89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 89 def lnet_connected? return false unless defined?(LNet) return false unless LNet.server return false if LNet.server.closed? # Check last activity if method exists if LNet.respond_to?(:last_recv) && LNet.last_recv return false if (Time.now - LNet.last_recv) > LNET_ACTIVITY_TIMEOUT end true rescue IOError, Errno::EBADF, Errno::EPIPE, NoMethodError false end |
#post(method, params) ⇒ Hash
Sends a POST request to the Slack API.
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 156 def post(method, params) retries = 0 max_retries = 5 begin uri = URI.parse("#{@api_url}#{method}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER req = Net::HTTP::Post.new(uri.path) req.set_form_data(params) res = http.request(req) if res.code == '429' retry_after = res['Retry-After']&.to_i raise ThrottlingError.new("Throttled by Slack API", retry_after) end raise NetworkError, "HTTP Error: #{res.code} #{res.}" unless res.is_a?(Net::HTTPSuccess) body = JSON.parse(res.body) raise ApiError, "Slack API Error: #{body['error']}" unless body['ok'] return body rescue JSON::ParserError => e raise ApiError, "Failed to parse Slack API response: #{e.}" rescue ThrottlingError => e raise ApiError, "Throttled by Slack API. Max retries (#{max_retries}) exceeded." if retries >= max_retries delay = e.retry_after || (BASE_RETRY_DELAY_SECONDS * (2**retries)) if delay > MAX_RETRY_DELAY_SECONDS raise ApiError, "Throttled by Slack API. Retry delay (#{delay}s) exceeds maximum." end Lich.log "Throttled by Slack API. Retrying in #{delay} seconds..." sleep delay retries += 1 retry rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError => e raise NetworkError, "Network error. Max retries (#{max_retries}) exceeded." if retries >= max_retries delay = BASE_RETRY_DELAY_SECONDS * (2**retries) if delay > MAX_RETRY_DELAY_SECONDS raise NetworkError, "Network error. Retry delay (#{delay}s) exceeds maximum." end Lich.log "Network error: #{e.}. Retrying in #{delay} seconds..." sleep delay retries += 1 retry end end |
#request_token(lichbot) ⇒ String, false
Requests a Slack token from the specified lichbot.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'documented/dragonrealms/commons/slackbot.rb', line 116 def request_token(lichbot) ttl = 10 send_time = Time.now @lnet.unique_buffer.push("chat to #{lichbot} RequestSlackToken") loop do line = get pause 0.05 return false if Time.now - send_time > ttl case line when /\[Private\]-.*:#{lichbot}: "slack_token: (.*)"/ msg = Regexp.last_match(1) return msg != 'Not Found' ? msg : false when /\[server\]: "no user .*/ return false end end end |