module FloodSafe
Constants
- CHUNK_DELAY_SEC
- ChannelReply
- MAX_CHUNK_BYTES
Public Instance Methods
Source
# File lib/flood_safe.rb, line 152 def apply_progress_prefixes(pieces, network: nil, channel: nil) prefixed = pieces.reject { |entry| entry[:skip_prefix] } total_prefixed = prefixed.length prefixed_index = 0 pieces.map do |entry| next entry[:text] if entry[:skip_prefix] prefixed_index += 1 percent = MessageProgress.line_progress_percent(prefixed_index, total_prefixed) context = build_reply_context( piece_index: prefixed_index - 1, total_pieces: pieces.length, network: network, channel: channel ) MessageProgress.prefix_piece(entry[:text], percent: percent, context: context) end end
Source
# File lib/flood_safe.rb, line 144 def apply_skip_prefix_lines!(pieces, skip_prefix_lines) return if skip_prefix_lines.to_i <= 0 pieces.each do |entry| entry[:skip_prefix] = true if entry[:logical_line] <= skip_prefix_lines.to_i end end
Source
# File lib/flood_safe.rb, line 68 def build_reply_context(piece_index:, total_pieces:, network: nil, channel: nil) lag_ms = 0 ping_ms = nil if respond_to?(:bot) && bot&.respond_to?(:data) && bot.data lag_ms = bot.data[:irc_lag_ms] unless bot.data[:irc_lag_ms].nil? ping_ms = bot.data[:irc_ping_ms] end lag_ms = IrcLag.current_ms if lag_ms.to_i.zero? delivery = ReplyDelivery.current || {} MessageProgress::ReplyContext.new( lag_ms: lag_ms, ping_ms: ping_ms, piece_index: piece_index, total_pieces: total_pieces, queued: delivery[:queued], network: network, channel: channel ) end
Source
# File lib/flood_safe.rb, line 21 def chunk_text(text, max_bytes: MAX_CHUNK_BYTES) text = text.to_s return [""] if text.empty? chunks = [] remaining = text while remaining.bytesize > max_bytes slice = remaining.byteslice(0, max_bytes) break_at = if (space_index = slice.rindex(" ")) slice[0..space_index].bytesize else max_bytes end break_at = utf8_safe_byte_index(remaining, [break_at, 1].max) chunk = remaining.byteslice(0, break_at).rstrip chunks << chunk remaining = remaining.byteslice(break_at..).to_s.lstrip end chunks << remaining unless remaining.empty? chunks end
Source
# File lib/flood_safe.rb, line 126 def collect_reply_pieces(text, message:, line_byte_limit: nil) limit = line_byte_limit || outgoing_line_limit(message) pieces = [] logical_line_count = 0 text.to_s.split("\n").each do |line| line = line.rstrip next if line.empty? logical_line_count += 1 chunk_text(line, max_bytes: limit).each do |chunk| pieces << { text: chunk, skip_prefix: false, logical_line: logical_line_count } end end [pieces, logical_line_count] end
Source
# File lib/flood_safe.rb, line 221 def flood_safe_reply(message, text, with_progress: :auto, skip_prefix_lines: 0, line_byte_limit: nil) safe_reply( message, text, with_progress: with_progress, skip_prefix_lines: skip_prefix_lines, line_byte_limit: line_byte_limit ) end
Source
# File lib/flood_safe.rb, line 51 def outgoing_line_limit(message) return MAX_CHUNK_BYTES unless bot target = message.channel || message.user || message name = target.respond_to?(:name) ? target.name : target.to_s nick = bot.config&.nick.to_s return MAX_CHUNK_BYTES if nick.empty? # Do not call bot.mask — Cinch may WHOIS-sync host and block the handler thread. prefix = ":#{nick}!u@host PRIVMSG #{name} :" overhead = prefix.bytesize + RabbotConfig::IRC_SPLIT_SUFFIX_BYTES computed = [RabbotConfig::IRC_MAX_LINE_BYTES - overhead, 80].max [computed, MAX_CHUNK_BYTES].min rescue StandardError MAX_CHUNK_BYTES end
Source
# File lib/flood_safe.rb, line 89 def progress_gutter_disabled?(message) return false unless respond_to?(:bot) && bot && !bot.is_a?(Class) && bot.respond_to?(:config) && bot.config channel = reply_channel_name(message) return false if channel.to_s.empty? BotOptions.boolean( network: bot.config.server, channel: channel, key: "no_progress_gutter", default: false ) rescue StandardError false end
Source
# File lib/flood_safe.rb, line 105 def reply_channel_name(target) if target.respond_to?(:channel) && target.channel ch = target.channel if ch.is_a?(String) ch elsif ch.respond_to?(:name) ch.name end elsif target.respond_to?(:name) && !target.is_a?(String) target.name end end
Source
# File lib/flood_safe.rb, line 118 def reply_network_channel(message) network = nil if respond_to?(:bot) && bot && !bot.is_a?(Class) && bot.respond_to?(:config) network = bot.config&.server end [network, reply_channel_name(message)] end
Source
# File lib/flood_safe.rb, line 172 def reply_pieces(text, message:, with_progress: :auto, skip_prefix_lines: 0, line_byte_limit: nil) pieces, logical_line_count = collect_reply_pieces(text, message: message, line_byte_limit: line_byte_limit) apply_skip_prefix_lines!(pieces, skip_prefix_lines) return pieces.map { |entry| entry[:text] } if progress_gutter_disabled?(message) use_progress = MessageProgress.resolve_with_progress( with_progress, logical_lines: logical_line_count, pieces: pieces.length ) return pieces.map { |entry| entry[:text] } unless use_progress network, channel = reply_network_channel(message) apply_progress_prefixes(pieces, network: network, channel: channel) end
Source
# File lib/flood_safe.rb, line 189 def reply_target(message) return message if message.respond_to?(:reply) return ChannelReply.new(message) if message.respond_to?(:send) message end
Source
# File lib/flood_safe.rb, line 196 def safe_reply(message, text, with_progress: :auto, skip_prefix_lines: 0, line_byte_limit: nil) target = reply_target(message) pieces = reply_pieces( text, message: target, with_progress: with_progress, skip_prefix_lines: skip_prefix_lines, line_byte_limit: line_byte_limit ) return if pieces.empty? pieces.each_with_index do |piece, index| delay = CHUNK_DELAY_SEC * index if delay.zero? target.reply(piece) else # Use Cinch::Helpers#Timer so the timer starts immediately; raw # Cinch::Timer.new only auto-starts on the next :connect event. Timer(delay, shots: 1) do target.reply(piece) end end end end
Source
# File lib/flood_safe.rb, line 45 def utf8_safe_byte_index(text, byte_index) byte_index = [byte_index, text.bytesize].min byte_index -= 1 while byte_index.positive? && !text.byteslice(0, byte_index).valid_encoding? byte_index end