OC-misc/bbs/OpenOS/lib/bboard.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