module BotLogStore
Constants
- DEFAULT_LINES
- KEY
- MAX_ENTRIES
- MAX_LINES
- MAX_SEARCH_RESULTS
- PLUGIN
- RETENTION_SECONDS
- STYLE_TITLE
- TAG
Public Instance Methods
Source
# File lib/bot_log_store.rb, line 117 def build_entry(kind:, text:, channel: nil, nick: nil, at: Time.now) { "at" => RabbotDb.iso_time(at), "kind" => kind.to_s, "channel" => channel.to_s, "nick" => nick.to_s, "text" => text.to_s } end
Source
# File lib/bot_log_store.rb, line 83 def clamp_lines(count) count.clamp(1, MAX_LINES) end
Source
# File lib/bot_log_store.rb, line 256 def dispatch(parsed, default_bot_id:, store: RabbotDb, now: Time.now) action = parsed[:action] return usage_message unless action target_bot = parsed[:bot_id] || default_bot_id case action when :recent entries = recent_entries(bot_id: target_bot, lines: parsed[:lines] || DEFAULT_LINES, store: store) format_recent_report(bot_id: target_bot, entries: entries, lines: parsed[:lines] || DEFAULT_LINES) when :search query = parsed[:query].to_s return usage_message if query.strip.empty? entries = search_entries(bot_id: target_bot, query: query, store: store) format_search_report(bot_id: target_bot, entries: entries, query: query) when :stats format_stats_report(stats_for(bot_id: target_bot, store: store, now: now)) else usage_message end end
Source
# File lib/bot_log_store.rb, line 177 def format_entry_line(entry) at = store_parse_time(entry["at"]) stamp = format_timestamp(at) kind = entry["kind"].to_s channel = entry["channel"].to_s nick = entry["nick"].to_s text = entry["text"].to_s case kind when "message" "#{stamp} #{channel} <#{nick}> #{text}" when "action" "#{stamp} #{channel} * #{nick} #{text}" when "join" "#{stamp} #{channel} +#{nick}" when "part" suffix = text.empty? ? "" : " (#{text})" "#{stamp} #{channel} -#{nick}#{suffix}" when "topic" "#{stamp} #{channel} topic by #{nick}: #{text}" when "connect" "#{stamp} [connect] #{text}" when "disconnect" "#{stamp} [disconnect] #{text}" when "notice" "#{stamp} notice #{channel} #{nick}: #{text}" else "#{stamp} [#{kind}] #{channel} #{nick} #{text}".strip end end
Source
# File lib/bot_log_store.rb, line 208 def format_recent_report(bot_id:, entries:, lines:) bot_label = normalize_bot_id(bot_id) count = entries.length title = "#{bot_label} · last #{count} of #{lines}" body = if entries.empty? "No retained log entries." else entries.map { |entry| format_entry_line(entry) }.join("\n") end [ IrcFormat.report_header(tag: TAG, title: title, style: STYLE_TITLE), body, IrcFormat.report_footer("retained #{RETENTION_SECONDS / 86_400} days") ].join("\n") end
Source
# File lib/bot_log_store.rb, line 224 def format_search_report(bot_id:, entries:, query:) bot_label = normalize_bot_id(bot_id) count = entries.length title = "#{bot_label} · search · #{query} · #{count} hits" body = if entries.empty? "No matches." else entries.map { |entry| format_entry_line(entry) }.join("\n") end [ IrcFormat.report_header(tag: TAG, title: title, style: STYLE_TITLE), body, IrcFormat.report_footer("logbook search") ].join("\n") end
Source
# File lib/bot_log_store.rb, line 240 def format_stats_report(stats) bot_label = stats[:bot_id] oldest = format_timestamp(stats[:oldest]) newest = format_timestamp(stats[:newest]) title = "#{bot_label} · #{stats[:count]} entries · #{stats[:retention_days]} days" body = [ "Oldest — #{oldest}", "Newest — #{newest}" ].join("\n") [ IrcFormat.report_header(tag: TAG, title: title, style: STYLE_TITLE), body, IrcFormat.report_footer("log retention") ].join("\n") end
Source
# File lib/bot_log_store.rb, line 171 def format_timestamp(at) return "?" unless at at.getlocal.strftime("%Y-%m-%d %H:%M") end
Source
# File lib/bot_log_store.rb, line 87 def load_entries(bot_id:, store: RabbotDb) Array(store.get_plugin_json( plugin: PLUGIN, key: KEY, nick: normalize_bot_id(bot_id), default: [] )) end
Source
# File lib/bot_log_store.rb, line 43 def normalize_bot_id(text) text.to_s.strip.downcase.gsub(/\s+/, "_") end
Source
# File lib/bot_log_store.rb, line 51 def parse_command(args) text = args.to_s.strip return { action: :recent, lines: DEFAULT_LINES } if text.empty? return { action: :stats } if text.casecmp?("stats") if (match = text.match(/\Asearch\s+(.+)\z/i)) return { action: :search, query: match[1].to_s.strip } end if (match = text.match(/\A(\S+)\s+search\s+(.+)\z/i)) return { action: :search, bot_id: normalize_bot_id(match[1]), query: match[2].to_s.strip } end if (match = text.match(/\A(\d+)\z/)) return { action: :recent, lines: clamp_lines(match[1].to_i) } end if (match = text.match(/\A(\S+)\s+(\d+)\z/)) return { action: :recent, bot_id: normalize_bot_id(match[1]), lines: clamp_lines(match[2].to_i) } end { action: :recent, bot_id: normalize_bot_id(text), lines: DEFAULT_LINES } end
Source
# File lib/bot_log_store.rb, line 105 def prune(entries, now: Time.now, retention: RETENTION_SECONDS) cutoff = now - retention Array(entries).select do |entry| at = store_parse_time(entry["at"]) at && at >= cutoff end end
Source
# File lib/bot_log_store.rb, line 136 def recent_entries(bot_id:, lines:, store: RabbotDb) load_entries(bot_id: bot_id, store: store).last(clamp_lines(lines)) end
Source
# File lib/bot_log_store.rb, line 127 def record_event(bot_id:, kind:, text:, channel: nil, nick: nil, at: Time.now, store: RabbotDb) entries = load_entries(bot_id: bot_id, store: store) entries << build_entry(kind: kind, text: text, channel: channel, nick: nick, at: at) entries = prune(entries, now: at) entries = entries.last(MAX_ENTRIES) if entries.length > MAX_ENTRIES save_entries(bot_id: bot_id, entries: entries, store: store) entries end
Source
# File lib/bot_log_store.rb, line 28 def resolve_bot_id(root:, nicks:) lookup_nicks = Array(nicks).map { |nick| nick.to_s.strip }.reject(&:empty?) return nil if lookup_nicks.empty? BotConsole::Config.discover_bots(root).each do |entry| raw = YAML.safe_load_file(entry[:config_path], aliases: false) || {} entry_nicks = [raw["nick"], *Array(raw["nicks"])].compact.map { |nick| nick.to_s.strip }.reject(&:empty?) next if entry_nicks.empty? return entry[:id] if entry_nicks.any? { |nick| lookup_nicks.any? { |candidate| candidate.casecmp(nick).zero? } } end normalize_bot_id(lookup_nicks.first) end
Source
# File lib/bot_log_store.rb, line 96 def save_entries(bot_id:, entries:, store: RabbotDb) store.set_plugin_json( plugin: PLUGIN, key: KEY, nick: normalize_bot_id(bot_id), value: entries ) end
Source
# File lib/bot_log_store.rb, line 140 def search_entries(bot_id:, query:, store: RabbotDb, limit: MAX_SEARCH_RESULTS) needle = query.to_s.downcase.strip return [] if needle.empty? matches = load_entries(bot_id: bot_id, store: store).select do |entry| haystack = [ entry["kind"], entry["channel"], entry["nick"], entry["text"], format_entry_line(entry) ].join(" ").downcase haystack.include?(needle) end matches.last(limit) end
Source
# File lib/bot_log_store.rb, line 157 def stats_for(bot_id:, store: RabbotDb, now: Time.now) entries = load_entries(bot_id: bot_id, store: store) oldest = entries.filter_map { |entry| store_parse_time(entry["at"]) }.min newest = entries.filter_map { |entry| store_parse_time(entry["at"]) }.max { bot_id: normalize_bot_id(bot_id), count: entries.length, oldest: oldest, newest: newest, retention_days: RETENTION_SECONDS / 86_400, now: now } end
Source
# File lib/bot_log_store.rb, line 113 def store_parse_time(value) RabbotDb.parse_time(value) end
Source
# File lib/bot_log_store.rb, line 47 def usage_message "Usage: !logbook [stats|search <term>|<bot> [n|search <term>]]" end