ZyklonB: add a seen plugin
This commit is contained in:
parent
d6a9e1dca1
commit
d97f28e7f7
|
@ -0,0 +1,160 @@
|
||||||
|
#!/usr/bin/env lua
|
||||||
|
--
|
||||||
|
-- ZyklonB seen plugin
|
||||||
|
--
|
||||||
|
-- Copyright 2016 Přemysl Janouch <p.janouch@gmail.com>
|
||||||
|
-- See the file LICENSE for licensing information.
|
||||||
|
--
|
||||||
|
|
||||||
|
function parse (line)
|
||||||
|
local msg = { params = {} }
|
||||||
|
line = line:match ("[^\r]*")
|
||||||
|
for start, word in line:gmatch ("()([^ ]+)") do
|
||||||
|
local colon = word:match ("^:(.*)")
|
||||||
|
if start == 1 and colon then
|
||||||
|
msg.prefix = colon
|
||||||
|
elseif not msg.command then
|
||||||
|
msg.command = word
|
||||||
|
elseif colon then
|
||||||
|
table.insert (msg.params, line:sub (start + 1))
|
||||||
|
break
|
||||||
|
elseif start ~= #line then
|
||||||
|
table.insert (msg.params, word)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_config (name)
|
||||||
|
io.write ("ZYKLONB get_config :", name, "\r\n")
|
||||||
|
return parse (io.read ()).params[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
io.output ():setvbuf ('line')
|
||||||
|
local prefix = get_config ('prefix')
|
||||||
|
io.write ("ZYKLONB register\r\n")
|
||||||
|
|
||||||
|
local db = {}
|
||||||
|
local db_filename = "seen.db"
|
||||||
|
local db_garbage = 0
|
||||||
|
|
||||||
|
function remember (who, where, when, what)
|
||||||
|
if not db[who] then db[who] = {} end
|
||||||
|
if db[who][where] then db_garbage = db_garbage + 1 end
|
||||||
|
db[who][where] = { tonumber (when), what }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
local db_file, e = io.open (db_filename, "a+")
|
||||||
|
if not db_file then error ("cannot open database: " .. e, 0) end
|
||||||
|
|
||||||
|
function db_store (who, where, when, what)
|
||||||
|
db_file:write (string.format
|
||||||
|
(":%s %s %s %s :%s\n", who, "PRIVMSG", where, when, what))
|
||||||
|
end
|
||||||
|
|
||||||
|
function db_compact ()
|
||||||
|
db_file:close ()
|
||||||
|
|
||||||
|
-- Unfortunately, default Lua doesn't have anything like mkstemp()
|
||||||
|
local db_tmpname = db_filename .. "." .. os.time ()
|
||||||
|
db_file, e = io.open (db_tmpname, "a+")
|
||||||
|
if not db_file then error ("cannot save database: " .. e, 0) end
|
||||||
|
|
||||||
|
for who, places in pairs (db) do
|
||||||
|
for where, data in pairs (places) do
|
||||||
|
db_store (who, where, data[1], data[2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
db_file:flush ()
|
||||||
|
|
||||||
|
local ok, e = os.rename (db_tmpname, db_filename)
|
||||||
|
if not ok then error ("cannot save database: " .. e, 0) end
|
||||||
|
db_garbage = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in db_file:lines () do
|
||||||
|
local msg = parse (line)
|
||||||
|
remember (msg.prefix, table.unpack (msg.params))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
function seen (who, where, args)
|
||||||
|
local respond = function (...)
|
||||||
|
local privmsg = function (target, ...)
|
||||||
|
io.write ("PRIVMSG ", target, " :", table.concat { ... }, "\r\n")
|
||||||
|
end
|
||||||
|
if where:match ("^[#&!+]") then
|
||||||
|
privmsg (where, who, ": ", ...)
|
||||||
|
else
|
||||||
|
privmsg (who, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local whom, e, garbage = args:match ("^(%S+)()%s*(.*)")
|
||||||
|
if not whom or #garbage ~= 0 then
|
||||||
|
return respond ("usage: <name>")
|
||||||
|
elseif who:lower () == whom:lower () then
|
||||||
|
return respond ("I can see you right now.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local top = {}
|
||||||
|
-- That is, * acts like a wildcard, otherwise everything is escaped
|
||||||
|
local pattern = "^" .. whom:gsub ("[%^%$%(%)%%%.%[%]%+%-%?]", "%%%0")
|
||||||
|
:gsub ("%*", ".*"):lower () .. "$"
|
||||||
|
for name, places in pairs (db) do
|
||||||
|
if places[where] and name:lower ():match (pattern) then
|
||||||
|
local when, what = table.unpack (places[where])
|
||||||
|
table.insert (top, { name = name, when = when, what = what })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #top == 0 then
|
||||||
|
return respond ("I have not seen \x02" .. whom .. "\x02 here.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get all matching nicknames ordered from the most recently active
|
||||||
|
-- and make the list case insensitive (remove older duplicates)
|
||||||
|
table.sort (top, function (a, b) return a.when > b.when end)
|
||||||
|
for i = #top, 2, -1 do
|
||||||
|
if top[i - 1].name:lower () == top[i].name:lower () then
|
||||||
|
table.remove (top, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Hopefully the formatting mess will disrupt highlights in clients
|
||||||
|
for i = 1, math.min (#top, 3) do
|
||||||
|
local name = top[i].name:gsub ("^.", "%0\x02\x02")
|
||||||
|
respond (string.format ("\x02%s\x02 -> %s -> %s",
|
||||||
|
name, os.date ("%c", top[i].when), top[i].what))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function handle (msg)
|
||||||
|
local who = msg.prefix:match ("^[^!@]*")
|
||||||
|
local where, what = table.unpack (msg.params)
|
||||||
|
local when = os.time ()
|
||||||
|
|
||||||
|
local what_log = what:gsub ("^\x01ACTION", "*"):gsub ("\x01$", "")
|
||||||
|
remember (who, where, when, what_log)
|
||||||
|
db_store (who, where, when, what_log)
|
||||||
|
|
||||||
|
-- Comment out to reduce both disk load and reliability
|
||||||
|
db_file:flush ()
|
||||||
|
|
||||||
|
if db_garbage > 5000 then db_compact () end
|
||||||
|
|
||||||
|
if what:sub (1, #prefix) == prefix then
|
||||||
|
local command = what:sub (#prefix + 1)
|
||||||
|
local name, e = command:match ("^(%S+)%s*()")
|
||||||
|
if name == 'seen' then seen (who, where, command:sub (e)) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in io.lines () do
|
||||||
|
local msg = parse (line)
|
||||||
|
if msg.command == "PRIVMSG" then handle (msg) end
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
# Creates a database for the "seen" plugin from logs for degesch.
|
||||||
|
# The results may not be completely accurate but are good for jumpstarting.
|
||||||
|
# Usage: ./seen-import-degesch.pl LOG-FILE... > seen.db
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use File::Basename;
|
||||||
|
use Time::Piece;
|
||||||
|
|
||||||
|
my $db = {};
|
||||||
|
for (@ARGV) {
|
||||||
|
my $where = (basename($_) =~ /\.(.*).log/)[0];
|
||||||
|
unless ($where) {
|
||||||
|
print STDERR "Invalid filename: $_\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
open my $fh, '<', $_ or die "Failed to open log file: $!";
|
||||||
|
while (<$fh>) {
|
||||||
|
my ($when, $who, $who_action, $what) =
|
||||||
|
/^(.{19}) (?:<[~&@%+]*(.*?)>| \* (\S+)) (.*)/;
|
||||||
|
next unless $when;
|
||||||
|
|
||||||
|
if ($who_action) {
|
||||||
|
$who = $who_action;
|
||||||
|
$what = "* $what";
|
||||||
|
}
|
||||||
|
$db->{$who}->{$where} =
|
||||||
|
[Time::Piece->strptime($when, "%Y-%m-%d %T")->epoch, $what];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (my ($who, $places) = each %$db) {
|
||||||
|
while (my ($where, $data) = each %$places) {
|
||||||
|
my ($when, $what) = @$data;
|
||||||
|
print ":$who PRIVMSG $where $when :$what\n";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue