141 lines
3.9 KiB
Lua
141 lines
3.9 KiB
Lua
|
local serial = require "serialization"
|
||
|
local fs = require "filesystem"
|
||
|
local bboard = {}
|
||
|
|
||
|
local PSYCHOS = _OSVERSION:sub(1,8)=="PsychOS"
|
||
|
|
||
|
local f = io.open(PSYCHOS and "/boot/cfg/bboard.cfg" or "/etc/bboard.cfg", "rb")
|
||
|
if f then
|
||
|
bboard.cfg = serial.unserialize(f:read("*a"))
|
||
|
f:close()
|
||
|
else
|
||
|
bboard.cfg = {
|
||
|
path = PSYCHOS and "/boot/bboard" or "/var/spool/bboard",
|
||
|
}
|
||
|
end
|
||
|
|
||
|
local BOARDPATH = string.format("%s/%%s", bboard.cfg.path)
|
||
|
local POSTPATH = string.format("%s/%%i", BOARDPATH)
|
||
|
|
||
|
local function sanitise(str)
|
||
|
return str:gsub("/","")
|
||
|
end
|
||
|
|
||
|
local flist = PSYCHOS and fs.list or function(path)
|
||
|
local rt = {}
|
||
|
for file in fs.list(path) do
|
||
|
rt[#rt+1] = file
|
||
|
end
|
||
|
return rt
|
||
|
end
|
||
|
|
||
|
function bboard.boards()
|
||
|
local rt = {}
|
||
|
for _, file in ipairs(flist(bboard.cfg.path)) do
|
||
|
if fs.isDirectory(string.format(BOARDPATH, file)) then
|
||
|
rt[#rt+1] = sanitise(file)
|
||
|
end
|
||
|
end
|
||
|
return rt
|
||
|
end
|
||
|
|
||
|
function bboard.list(board, start, limit, trunc)
|
||
|
assert(type(board) == "string", "invalid board")
|
||
|
local rt = {}
|
||
|
local ft = flist(string.format(BOARDPATH, board))
|
||
|
local limit, start = math.max(limit or math.huge, 50), #ft
|
||
|
table.sort(ft, function(a,b) return a>b end)
|
||
|
for k,v in ipairs(ft) do
|
||
|
if tonumber(v) and tonumber(v) <= start then
|
||
|
rt[#rt+1] = bboard.getPostMeta(board, v, trunc)
|
||
|
rt[#rt].id = tonumber(v)
|
||
|
end
|
||
|
if #rt >= limit then break end
|
||
|
end
|
||
|
return rt
|
||
|
end
|
||
|
|
||
|
function bboard.getReplies(board, id, max, skip, trunc)
|
||
|
assert(type(board) == "string", "invalid board")
|
||
|
assert(type(id) == "number", "invalid post ID")
|
||
|
skip=skip or 0
|
||
|
local max = tonumber((bboard.list(board, nil, 1)[1] or {}).id or 0)
|
||
|
local rt = {}
|
||
|
for i = id, max do
|
||
|
if skip < 1 then
|
||
|
local post = bboard.getPost(board, i)
|
||
|
if post.replyTo == id then
|
||
|
rt[#rt+1] = bboard.getPostMeta(board,i,trunc)
|
||
|
end
|
||
|
if #rt >= math.max(trunc or math.huge, 50) then break end
|
||
|
end
|
||
|
skip = skip - 1
|
||
|
end
|
||
|
return rt
|
||
|
end
|
||
|
|
||
|
function bboard.getAllReplies(board, id, max, skip, trunc)
|
||
|
assert(type(board) == "string", "invalid board")
|
||
|
assert(type(id) == "number", "invalid post ID")
|
||
|
skip=skip or 0
|
||
|
local max = tonumber((bboard.list(board, nil, 1)[1] or {}).id or 0)
|
||
|
local ids,rt = {[id]=true}, {}
|
||
|
for i = id, max do
|
||
|
if skip < 1 then
|
||
|
local post = bboard.getPost(board, i)
|
||
|
if ids[post.replyTo] then
|
||
|
ids[i], rt[#rt+1] = true, bboard.getPostMeta(board,i,trunc)
|
||
|
end
|
||
|
if #rt >= math.max(trunc or math.huge, 50) then break end
|
||
|
end
|
||
|
skip = skip - 1
|
||
|
end
|
||
|
return rt
|
||
|
end
|
||
|
|
||
|
function bboard.getThread(board,id,max,skip,trunc)
|
||
|
assert(type(board) == "string", "invalid board")
|
||
|
assert(type(id) == "number", "invalid post ID")
|
||
|
skip=skip or 0
|
||
|
local op
|
||
|
repeat
|
||
|
op = bboard.getPostMeta(board, id)
|
||
|
id = op.replyTo or id
|
||
|
until not op.replyTo
|
||
|
return {op, table.unpack(bboard.getAllReplies(board,id,max,skip,trunc))}
|
||
|
end
|
||
|
|
||
|
function bboard.getPost(board,id)
|
||
|
board, id = sanitise(board), tonumber(id)
|
||
|
assert(type(board) == "string", "invalid board")
|
||
|
assert(type(id) == "number", "invalid post ID")
|
||
|
local f, e = io.open(string.format(POSTPATH, board, id), "rb")
|
||
|
assert(f, e)
|
||
|
local rt = serial.unserialize(f:read("*a"))
|
||
|
f:close()
|
||
|
rt.date = fs.lastModified(string.format(POSTPATH, board, id))
|
||
|
rt.id = id
|
||
|
return rt
|
||
|
end
|
||
|
function bboard.getPostMeta(board,id,trunc)
|
||
|
local rt = bboard.getPost(board,id)
|
||
|
rt.content = rt.content:match("^[^\n]+"):sub(1,math.min(trunc or 80, 160))
|
||
|
return rt
|
||
|
end
|
||
|
|
||
|
function bboard.post(board,post)
|
||
|
assert(type(board) == "string", "invalid board")
|
||
|
assert(type(post.author) == "string", "invalid author")
|
||
|
assert(type(post.subject) == "string" and #post.subject <= 40, "invalid subject")
|
||
|
assert(type(post.content) == "string", "no content")
|
||
|
post.author = string.format("%s@%s", post.author, os.getenv("RPC_CLIENT") or "localhost")
|
||
|
post.id = tonumber((bboard.list(board, nil, 1)[1] or {}).id or 0)+1
|
||
|
local f, e = io.open(string.format(POSTPATH, board, post.id), "wb")
|
||
|
assert(f, e)
|
||
|
f:write(serial.serialize(post))
|
||
|
f:close()
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
return bboard
|