Files
lua-matrix/examples/client-cqchat.lua
2016-07-08 21:17:41 +03:00

183 lines
5.5 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#! /usr/bin/env lua
--
-- client-cqchat.lua
-- Copyright (C) 2016 Adrian Perez <aperez@igalia.com>
--
-- Distributed under terms of the MIT license.
--
local cqueues = require "cqueues"
local posix = require "posix"
local bit = require "bit32"
local matrix = require "matrix"
local function xpcall_traceback(errmsg)
local tb = debug.traceback(nil, nil, 2)
return errmsg and (errmsg .. "\n" .. tb) or tb
end
--
-- Wraps the controlling terminal into an object which can be polled
-- with cqueues.poll() and uses O_NONBLOCK for input.
--
local tty = {
file = (function ()
local f = io.open(posix.ctermid(), "a+")
local fd = posix.fileno(f)
local flags = posix.fcntl(fd, posix.F_GETFL, 0)
flags = bit.bor(flags, assert(posix.O_NONBLOCK))
if posix.O_CLOEXEC then
flags = bit.bor(flags, posix.O_CLOEXEC)
else
io.stderr:write("no O_CLOEXEC, the TTY file descriptor might leak")
io.stderr:flush()
end
if posix.fcntl(fd, posix.F_SETFL, flags) ~= 0 then
error("cannot set O_NONBLOCK/O_CLOEXEC: " .. posix.errno())
end
return f
end)(),
-- Functions expected by cqueues
pollfd = function (self) return posix.fileno(self.file) end,
events = function () return "r" end, -- Read only
timeout = function () return nil end, -- Set in the call to cqueues.poll()
-- Wrappers for tcsetattr/tcgetattr
tcgetattr = function (self) return posix.tcgetattr(self:pollfd()) end,
tcsetattr = function (self, ...) return posix.tcsetattr(self:pollfd(), ...) end,
-- Obtain the terminal size using TIOCGWINSZ
_size_width = false,
_size_height = false,
size = function (self, force)
if force then
self._size_width = false
self._size_height = false
end
if self._size_width == false then
local p = io.popen("tput cols", "r")
self._size_width = p:read("*n")
p:close()
end
if self._size_height == false then
local p = io.popen("tput lines", "r")
self._size_height = p:read("*n")
p:close()
end
return self._size_width, self._size_height
end,
-- Saves terminal attributes, runs a function, and restores attributes
-- even if the function raises an error.
wrap = function (self, f, ...)
local saved_attr = self:tcgetattr()
local ok, err = xpcall(f, xpcall_traceback, self, ...)
self:tcsetattr(posix.TCSANOW, saved_attr)
if not ok then
error("tty:wrap: Error in wrapped function:\n" .. err)
end
return self
end
}
local command_pattern = "^%s*/(%a+)%s+(.*)$"
local function main(tty, client, username, password)
do
local a = tty:tcgetattr()
a.cc[posix.VMIN] = 1
a.cc[posix.VTIME] = 0
a.lflag = bit.band(a.lflag, bit.bnot(bit.bor(posix.ECHO, posix.ICANON)))
a.iflag = bit.band(a.iflag, bit.bnot(bit.bor(posix.IXON, posix.ISTRIP)))
a.cflag = bit.band(a.cflag, bit.bnot(bit.bor(posix.CSIZE, posix.PARENB)))
a.cflag = bit.bor(a.cflag, posix.CS8)
a.oflag = bit.band(a.oflag, bit.bnot(posix.OPOST))
if tty:tcsetattr(posix.TCSANOW, a) ~= 0 then
error("tcsetattr: " .. posix.errno())
end
end
local cq = cqueues.new()
local ok, err, obj = cq:wrap(function ()
print(string.format("Terminal size: %dx%d", tty:size()))
client:login_with_password(username, password)
local clientqueue = cq:wrap(function ()
client:sync()
end)
local current_room
while true do
local line = ""
while true do
io.stdout:write(string.format("\r[%s] %s",
current_room and current_room.room_id or "*",
line))
io.stdout:flush()
local handle_tty = false
cqueues.poll(tty, 0.05)
local ch = tty.file:read(1)
if ch then
if ch == "\4" and #line == 0 then
print("\r")
cq:cancel()
return
elseif ch == "\127" then
line = line:sub(1, -2)
elseif ch == "\n" then
break
else
line = line .. ch
end
end
end
local command, params = line:match(command_pattern)
if command then
if command == "room" then
if client.rooms[params] then
current_room = client.rooms[params]
else
print("\r ! No such room")
end
end
else
if current_room then
current_room:send_text(line)
else
print("\r ! Choose a room using '/room <room_id>'")
end
end
end
end):loop()
if not ok then
error(err)
end
end
local function print_room_message(room, sender, message, event)
print(string.format("\rK[%s] <%s> %s", room.room_id, sender, message.body))
end
if #arg ~= 3 then
io.stderr:write(string.format("Usage: %s <homeserver> <username> <password>\n", arg[0]))
os.exit(1)
end
local client = matrix.client(arg[1])
:hook("logged-in", function (client)
print("\r * Logged in as " .. client.user_id)
end)
:hook("joined", function (client, room)
print("\r * Joined room " .. room.room_id)
room:hook("message", print_room_message)
end)
:hook("left", function (client, room)
print("\r * Left room " .. room.room_id)
end)
tty:wrap(main, client, arg[2], arg[3])