--A chat server. Supports connection/disconnection announcing and /nick command --to let the user change her nick. Also, /kick can be used from terminal. --Warning: there's currently hardly any security checks there. chat_connections = {} return { print_nonl = function (self, msg) io.stdout:write(msg) io.stdout:flush() end, broadcast = function (self, msg) self:print_nonl(msg) --Note that connections[k] means that we can't easily say if --the connection belongs to us or not. chat_connections[k] --would solve it, but then we'd skip the filters below the --connection. for k, v in pairs(connections) do connections[k]:send(msg) end end, connect = function (self, host, port) ret = self.super:connect(host, port) self.our_id = self:get_nick() msg = "* " .. self.our_id .. " connected to the server.\n" self:broadcast(msg) return ret end, close = function (self) msg = "* " .. self.our_id .. " disconnected from the server.\n" self:broadcast(msg) self.super:close() end, get_nick = function (self) for k, v in pairs(chat_connections) do if chat_connections[k] == self then return k end end --If we got there, we clearly aren't registered yet. Find a name. --We probably could have come with a cooler nick, but it's a PoC. local new_id = tostring(self) chat_connections[new_id] = self return new_id end, is_valid_nick = function (self, new_id) return chat_connections[new_id] == nil end, recv = function (self) local line, err = self.super:recv() if line == nil then return line, err end --During testing, this happened in UDP mode. No idea why you'd want --a single-user chat though since that's all you'd get without -k. if self.our_id == nil then self:connect() end local msg = '<' .. self.our_id .. '> ' .. line if line:sub(1, 6) == "/nick " then local old_id = self.our_id local new_id = line:sub(7):gsub("\n", "") if self:is_valid_nick(new_id) then self.our_id = new_id chat_connections[old_id] = nil chat_connections[self.our_id] = self msg = "* " .. old_id .. " changed nickname to " .. self.our_id .. "\n" else self:send "Nickname not allowed!\n" return "" end end self:broadcast(msg) return "" end, send = function (self, str) if str:sub(1, 6) ~= "/kick " then return self.super:send(str) end local kicked_nick = str:sub(7):gsub("\n", "") if chat_connections[kicked_nick] == nil then --We're not printing "no such nick" because admin would see the --message multiplied for each user connected. return end chat_connections[kicked_nick]:send "You have been kicked.\n" chat_connections[kicked_nick].super:close() chat_connections[kicked_nick] = nil self:broadcast(("* %s got kicked by the chat administrator.\n"):format(kicked_nick)) end, }