Class: Xeno::Channels::Slack::Streamer

Inherits:
Object
  • Object
show all
Defined in:
lib/xeno/channels/slack.rb

Overview

Post-then-edit delivery for one streaming reply: the first content delta posts the thread message, later deltas edit it at most once per EDIT_INTERVAL, finish writes the final text. Rate limits defer the next edit; a hard failure turns streaming off for this reply and the completion post is the fallback.

Constant Summary collapse

EDIT_INTERVAL =

seconds — comfortably under chat.update's tier

1.0

Instance Method Summary collapse

Constructor Details

#initialize(slack, channel:, thread_ts:) ⇒ Streamer

Returns a new instance of Streamer.



128
129
130
131
132
133
134
135
136
137
# File 'lib/xeno/channels/slack.rb', line 128

def initialize(slack, channel:, thread_ts:)
  @slack = slack
  @channel = channel
  @thread_ts = thread_ts
  @buffer = +""
  @ts = nil
  @posted_text = nil
  @next_edit_at = 0.0
  @dead = false
end

Instance Method Details

#finish(final_text) ⇒ Object

Returns true when this streamer delivered the reply (the runner then skips the completion post); false hands delivery back.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/xeno/channels/slack.rb', line 149

def finish(final_text)
  return false if @ts.nil?

  text = final_text.to_s
  return true if @posted_text == text

  wait = @next_edit_at - now
  sleep([ wait, 3.0 ].min) if wait.positive? # respect a pending rate-limit window
  @slack.api_post("chat.update", channel: @channel, ts: @ts, text: text)
  @posted_text = text
  true
rescue StandardError => e
  Rails.logger.warn("xeno: slack streaming final edit failed: #{e.class}: #{e.message}")
  false
end

#push(chunk) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/xeno/channels/slack.rb', line 139

def push(chunk)
  content = chunk.respond_to?(:content) ? chunk.content : nil
  return if @dead || content.to_s.empty?

  @buffer << content
  @ts.nil? ? start_message : edit_message(@buffer.dup)
end