absurdor

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 1dc59c9ede62842dbbaa76f058df3088105f7da2
parent 9954ac5aa193f27492fd86d2bba17c0c369b3f7f
Author: Juan F. Meleiro <juan@juanmeleiro.mat.br>
Date:   Mon, 26 Jun 2023 09:50:09 -0300

Implement event recording and fix report logic

Diffstat:
Aabsurdor | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dreport | 117-------------------------------------------------------------------------------
Mtemplate.m4 | 1-
3 files changed, 164 insertions(+), 118 deletions(-)

diff --git a/absurdor b/absurdor @@ -0,0 +1,164 @@ +#!/usr/bin/env lua +local argparse = require "argparse" +local json = require "json" +local path = require "path" +local fs = require "path.fs" +local date = require "date" + +function die(err, msg) + msg = tostring(msg or err) + if err then + io.stderr:write(msg .. "\n") + os.exit(1) + end +end + +function maildate(s) + local d = date(s) + local sec = date.diff(d, date.epoch()):spanseconds() + return sec +end + +function yn(prompt) + meaning = {Y = true, y = true, n = false, N = false} + while meaning[ans] == nil do + io.write(prompt) + ans = io.read(1) + end + return ans +end + +function decodewith(decoder, fn) + die(not path.exists(fn), fn.." does not exist.") + die(not path.isfile(fn), fn.." is not a file.") + local f, err = io.open(fn, "r") + die(err) + status, res = xpcall(decoder, die, f:read("a")) + f:close() + die(not status, res) + return res +end + +function encodewith(encoder, fn, data) + local f, err = io.open(fn, "w") + die(not status, err) + status, err = f:write(encoder(data)) + die(not status, err) +end + +function format(fmt, dict, sett) + -- A identifier is + -- > any string of Latin letters, Arabic-Indic digits, and + -- > underscores, not beginning with a digit and not being a reserved + -- > word + + -- local reserved = { + -- "and", "break", "do", "else", "elseif", "end", "false", "for", + -- "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", + -- "return", "then", "true", "until", "while" + -- } + res = fmt + for s in string.gmatch(fmt, "%{([_a-zA-Z][._a-zA-Z0-9]*)%}") do + local value = dict + local pref = sett + for k in string.gmatch(s, "([_a-zA-Z][_a-zA-Z0-9]*)") do + if type(value) == "table" then value = value[k] end + if type(pref) == "table" then pref = pref[k] end + end + if pref and value then string.format(pref, value) end + res = fmt.gsub(res, "{"..s.."}", value or "nil") + end + return res +end + +function fmt_event(e) + date = os.date("%Y-%m-%d %H:%M:%S %z", e.when) + args = {ts = date} + if e.what == "report" then + fmt = "[{ts}] Reported boulder at height {height}" + args.height = e.height + elseif e.what == "push" then + fmt = "[{ts}] {who} pushed the boulder" + args.who = e.who + end + return format(fmt, args) +end + +local parser = argparse("absurdor", "Manage Absurdor duties") + :command_target("command") +parser:command("report", "Generate and send absurdor report") +local cmd_record = parser:command("record", "Record events") + :command_target("what") +local cmd_press = cmd_record:command("push", "A push of The Boulder") +cmd_press:argument("who", "Name of the player") +cmd_press:argument("when", "Timestamp of boulder push") + :convert(maildate) +parser:command("log", "Display log") + +local args = parser:parse() + +fn = "log.json" +log = decodewith(json.decode, fn) + +if args.command == "report" then + + height = 0 + + for i,e in ipairs(log) do + if e.what == "push" then + height = height + 1 + end + end + + height = height % 100 + + vars = { + YYYY = os.date("%Y"), + MM = os.date("%m"), + DD = os.date("%d"), + N = height + } + + defs = "" + + for k, v in pairs(vars) do + defs = defs .. string.format(" --define=%s=%s", k, v) + end + + tmpname = ".tmp" + os.execute(string.format("m4 %s template.m4 >> %s", defs, tmpname)) + + f = io.open(tmpname, "a") + f:write("There is no log, for there is no point.\n") + f:write("There is no log, for there is no hope.\n") + f:write("There is no log. But do not despair;\n\n rebel\n\n") + f:write("======================================================================") + f:close() + + os.execute(string.format("neomutt -H %s -E", tmpname)) + + if yn("Archive report? [Yn] ") then + table.insert(log, { + when = os.time(), + what = "report", + height = height, + }) + os.rename(tmpname, string.format("archive/%s", os.date("%F"))) + end +elseif args.command == "record" then + if args.what == "push" then + io.write(string.format("%s pushed the boulder at %d\n", args.who, args.when)) + table.insert(log, { + when = args.when, + what = "push", + who = args.who + }) + end +elseif args.command == "log" then + for _,e in ipairs(log) do + io.write(fmt_event(e).."\n") + end +end + +encodewith(json.encode, fn, log) + diff --git a/report b/report @@ -1,117 +0,0 @@ -#!/usr/bin/env lua -local argparse = require "argparse" -local json = require "json" -local path = require "path" -local fs = require "path.fs" - -function die(err, msg) - msg = tostring(msg or err) - if err then - io.stderr:write(msg .. "\n") - os.exit(1) - end -end - -function decodewith(decoder, fn) - die(not path.exists(fn), fn.." does not exist.") - die(not path.isfile(fn), fn.." is not a file.") - local f, err = io.open(fn, "r") - die(err) - status, res = xpcall(decoder, die, f:read("a")) - f:close() - die(not status, res) - return res -end - -function encodewith(encoder, fn, data) - local f, err = io.open(fn, "w") - die(not status, err) - status, err = f:write(encoder(data)) - die(not status, err) -end - -function format(fmt, dict, sett) - -- A identifier is - -- > any string of Latin letters, Arabic-Indic digits, and - -- > underscores, not beginning with a digit and not being a reserved - -- > word - - -- local reserved = { - -- "and", "break", "do", "else", "elseif", "end", "false", "for", - -- "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", - -- "return", "then", "true", "until", "while" - -- } - res = fmt - for s in string.gmatch(fmt, "%{([_a-zA-Z][._a-zA-Z0-9]*)%}") do - local value = dict - local pref = sett - for k in string.gmatch(s, "([_a-zA-Z][_a-zA-Z0-9]*)") do - if type(value) == "table" then value = value[k] end - if type(pref) == "table" then pref = pref[k] end - end - if pref and value then string.format(pref, value) end - res = fmt.gsub(res, "{"..s.."}", value or "nil") - end - return res -end - -local parser = argparse("report", "Generate and send the Absurdor report") --- parser:argument("input", "Input file.") --- parser:option("-o --output", "Output file.", "a.out") - -local args = parser:parse() - -fn = "log.json" -log = decodewith(json.decode, fn) - -height = 0 - -for i,e in ipairs(log) do - if e.type == "push" then - height = height + 1 - end -end - -height = height % 100 - -vars = { - YYYY = os.date("%Y"), - MM = os.date("%m"), - DD = os.date("%d"), - N = height -} - -defs = "" - -for k, v in pairs(vars) do - defs = defs .. string.format(" --define=%s=%s", k, v) -end - -tmpname = ".tmp" -os.execute(string.format("m4 %s template.m4 >> %s", defs, tmpname)) -os.execute(string.format("neomutt -H %s -E", tmpname)) - -table.insert(log, { - timestamp = os.time(), - ["type"] = "report", - height = height -}) - -valid = { - Y = true, - y = true, - n = false, - N = false -} - -ans = nil -while valid[ans] == nil do - io.write("Archive report? [Yn] ") - ans = io.read(1) -end - -if valid[ans] then - encodewith(json.encode, fn, log) - os.rename(tmpname, string.format("archive/%s", os.date("%F"))) -end - diff --git a/template.m4 b/template.m4 @@ -22,5 +22,4 @@ absurdor: juan The State of the Absurd YYYY-MM-DD `_/ / -======================================================================