Module: Silas::Slack
- Defined in:
- lib/silas/slack.rb
Overview
Thin Slack Web API helper — no gem dependency. Posting (chat.postMessage), approval Block Kit, and inbound request-signature verification.
Constant Summary collapse
- POST_MESSAGE =
"https://slack.com/api/chat.postMessage".freeze
- REPLAY_WINDOW =
seconds
300
Class Method Summary collapse
-
.approval_blocks(invocation) ⇒ Object
Approve/Decline buttons; the button value is the invocation id, so the actions webhook maps a click straight to invocation.approve!/decline!.
- .post_message(channel:, text:, blocks: nil, thread_ts: nil, token: Silas.config.slack_bot_token) ⇒ Object
-
.verify_signature(signing_secret:, timestamp:, body:, signature:, now: Time.now.to_i) ⇒ Object
Slack signs each request (v0 scheme) — HMAC-SHA256 over "v0:ts:body" plus a 5-minute replay window.
Class Method Details
.approval_blocks(invocation) ⇒ Object
Approve/Decline buttons; the button value is the invocation id, so the actions webhook maps a click straight to invocation.approve!/decline!.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/silas/slack.rb', line 29 def approval_blocks(invocation) [ { "type" => "section", "text" => { "type" => "mrkdwn", "text" => "*Approval needed:* `#{invocation.tool_name}`\n```#{JSON.generate(invocation.arguments)}```" } }, { "type" => "actions", "block_id" => "silas_approval", "elements" => [ { "type" => "button", "action_id" => "silas_approve", "style" => "primary", "text" => { "type" => "plain_text", "text" => "Approve" }, "value" => invocation.id.to_s }, { "type" => "button", "action_id" => "silas_decline", "style" => "danger", "text" => { "type" => "plain_text", "text" => "Decline" }, "value" => invocation.id.to_s } ] } ] end |
.post_message(channel:, text:, blocks: nil, thread_ts: nil, token: Silas.config.slack_bot_token) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/silas/slack.rb', line 14 def (channel:, text:, blocks: nil, thread_ts: nil, token: Silas.config.slack_bot_token) raise Error, "no Slack bot token configured (credentials.silas.slack.bot_token)" if token.blank? body = { channel: channel, text: text } body[:blocks] = blocks if blocks body[:thread_ts] = thread_ts if thread_ts res = Net::HTTP.post(URI(POST_MESSAGE), body.to_json, "content-type" => "application/json; charset=utf-8", "authorization" => "Bearer #{token}") JSON.parse(res.body) end |
.verify_signature(signing_secret:, timestamp:, body:, signature:, now: Time.now.to_i) ⇒ Object
Slack signs each request (v0 scheme) — HMAC-SHA256 over "v0:ts:body" plus a 5-minute replay window. Returns true only for a genuine, fresh request.
46 47 48 49 50 51 52 53 |
# File 'lib/silas/slack.rb', line 46 def verify_signature(signing_secret:, timestamp:, body:, signature:, now: Time.now.to_i) return false if signing_secret.blank? || signature.blank? || .blank? return false if (now - .to_i).abs > REPLAY_WINDOW basestring = "v0:#{}:#{body}" expected = "v0=" + OpenSSL::HMAC.hexdigest("SHA256", signing_secret, basestring) ActiveSupport::SecurityUtils.secure_compare(expected, signature) end |