diff --git a/minitel-chat/chat.lua b/minitel-chat/chat.lua new file mode 100644 index 0000000..1f57bd4 --- /dev/null +++ b/minitel-chat/chat.lua @@ -0,0 +1,110 @@ +local term = require "term" +local event = require "event" +local process = require "process" +local net = require "net" +local computer = require "computer" +local gpu = term.gpu() + +local tArgs = {...} +local server, port = (tArgs[1]:match("(.+):%d+") or tArgs[1]), (tonumber(tArgs[1]:match(".+:(%d+)")) or 194) +local nick = tArgs[2] +local x, y = gpu.getResolution() +local colours = {"37","31","32","34"} +local run = true +local s = false +local ow = process.info().data.window +local nw = term.internal.open(0,0,x,y-1) +term.bind(term.gpu(), nw) + +term.clear() + +local function gncolour(nick) + local n = 0 + for c in nick:gmatch(".") do + n = n + c:byte() + end + n = (n % #colours) + 1 + return colours[n] +end +local function colourize(nick) + return "\27["..gncolour(nick).."m"..nick.."\27[0m" +end + +local function formatLine(...) + local tA = {...} + return string.format("%s"..("\t%s"):rep(#tA-1).."\n", ...) +end + +local function linemsg(message) + process.info().data.window = nw + gpu.copy(1,2,x,y-2,0,-1) + local ox, oy = term.getCursor() + term.setCursor(1,y-1) + term.clearLine() + io.write(message) + term.setCursor(ox,oy) + process.info().data.window = ow +end +local function handlemsg(nick,message) + linemsg("<"..colourize(nick).."> "..message) +end + +function connect() + linemsg("Connecting to "..server..":"..tostring(port)) + s = net.open(server,port) + linemsg("Connected! Sending nick...") + s:write("nick\t"..nick.."\n") +end + +connect() + +local function readLoop() + local line = s.rbuffer:match("(.-)\n") + local tCmd = {} + for w in line:gmatch("[^\t]+") do + tCmd[#tCmd+1] = w + end + s:read(line:len()+1) + if tCmd[1] == "msg" and tCmd[2] and tCmd[3] then + handlemsg(tCmd[2], tCmd[3]) + elseif tCmd[1] == "topic" then + linemsg("Topic: "..tCmd[2]) + elseif tCmd[1] == "nick" then + linemsg(colourize(tCmd[2]).." changed nick to " .. colourize(tCmd[3])) + elseif tCmd[1] == "quit" then + linemsg(colourize(tCmd[2]) .. " has quit.") + elseif tCmd[1] == "join" then + linemsg(colourize(tCmd[2]) .. " has joined.") + end +end + +local timer = event.timer(0.5,readLoop,math.huge) + +while run do + io.write("\27["..tostring(y)..";1H["..nick.."] ") + msg = term.read({},false):sub(1,-2) + term.clearLine() + if msg:sub(1,1) == "/" then + local cmd = msg:sub(2) + local tCmd = {} + for w in cmd:gmatch("%S+") do + tCmd[#tCmd+1] = w + end + if tCmd[1] == "quit" then + linemsg("Disconnecting from server...") + s:close() + run = false + elseif tCmd[1] == "nick" then + nick = tCmd[2] + s:write(formatLine("nick",nick)) + end + else + s:write(formatLine("msg",msg)) + end + if s.state ~= "open" then + linemsg("Disconnected from server, exiting") + run = false + end +end + +event.cancel(timer) diff --git a/minitel-chat/chatd.lua b/minitel-chat/chatd.lua new file mode 100644 index 0000000..0d55c52 --- /dev/null +++ b/minitel-chat/chatd.lua @@ -0,0 +1,94 @@ +local net = require "net" +local event = require "event" +local syslog = require "syslog" +local serial = require "serialization" + +local cfgfile = "/etc/chatd.cfg" +local clients = {} +local cfg = {} +cfg.port = 194 +cfg.topic = "Welcome to the "..os.getenv("HOSTNAME").." chat server." +local timer = false + +local function saveconfig() + local f = io.open(cfgfile,"wb") + if f then + f:write(serial.serialize(cfg)) + f:close() + end +end +local function loadconfig() + local f = io.open(cfgfile,"rb") + if f then + local newcfg = serial.unserialize(f:read("*a")) + f:close() + for k,v in pairs(newcfg) do + cfg[k] = v + end + else + saveconfig() + end +end + +local function log(msg,level) + syslog(msg,level,"chatd") +end + +local function sendToAll(data) + for k,v in pairs(clients) do + v:write(data) + end +end +local function formatLine(...) + local tA = {...} + return string.format("%s"..("\t%s"):rep(#tA-1).."\n", ...) +end + +local function clientLoop() + for sn, socket in pairs(clients) do + local line = socket.rbuffer:match("(.+)\n") + if line then + socket:read(line:len()+1) + local tCmd = {} + for w in line:gmatch("[^\t]+") do + tCmd[#tCmd+1] = w + end + if tCmd[1] == "nick" then + if socket.nick then + sendToAll(formatLine("nick",socket.nick,tCmd[2])) + else + log(tostring(socket.addr)..":"..tostring(socket.port).." connected as "..tCmd[2]) + socket:write(formatLine("topic",cfg.topic)) + sendToAll(formatLine("join",tCmd[2])) + end + socket.nick = tCmd[2] + elseif tCmd[1] == "msg" and socket.nick then + sendToAll(formatLine("msg",socket.nick,tCmd[2])) + end + end + if socket.state ~= "open" then + log(tostring(socket.addr)..":"..tostring(socket.port).." ("..tostring(socket.nick)..") quit") + clients[sn] = nil + if socket.nick then + sendToAll(formatLine("quit",socket.nick)) + end + end + end +end + +local function handleSocket(s) + clients[tostring(s.addr)..":"..tostring(s.port)] = s +end + +function start() + loadconfig() + net.flisten(cfg.port,handleSocket) + timer = event.timer(1,clientLoop,math.huge) +end +function stop() + event.ignore("net_msg",handleSocket) + event.cancel(timer) + for k,v in pairs(clients) do + v:close() + end +end diff --git a/programs.cfg b/programs.cfg new file mode 100644 index 0000000..f4f090b --- /dev/null +++ b/programs.cfg @@ -0,0 +1,27 @@ +{ + ["minitel-chat"] = { + files = { + ["master/minitel-chat/chat.lua"] = "/bin", + }, + dependencies = { + ["minitel"] = "", + }, + name = "Minitel Chat", + description = "Exceedingly dumb chat client", + authors = "Izaya", + repo = "tree/master/" + }, + ["minitel-chatd"] = { + files = { + ["master/minitel-chat/chatd.lua"] = "//etc/rc.d", + }, + dependencies = { + ["minitel"] = "", + ["libsyslog"] = "", + }, + name = "Minitel Chat (Server)", + description = "Exceedingly dumb chat server", + authors = "Izaya", + repo = "tree/master/" + }, +}