Port to Lua 5.3
This commit is contained in:
parent
09628cded4
commit
783fefdcb3
@ -1,5 +1,5 @@
|
||||
# Path to your Lua library directory (LUA_PATH)
|
||||
LUA_DIR = /usr/local/share/lua/5.1
|
||||
LUA_DIR = /usr/local/share/lua/5.3
|
||||
|
||||
# Set this to the path of your luadoc executable if you want to regenerate the
|
||||
# documentation and the luadoc script isn't in your PATH
|
||||
|
9
README
9
README
@ -1,5 +1,6 @@
|
||||
LuaIRC v0.3
|
||||
LuaIRC v0.3 (Lua 5.3 Port)
|
||||
Jesse Luehrs (jluehrs2@uiuc.edu)
|
||||
Elias Fleckenstein (fleckenstein@elidragon.com)
|
||||
|
||||
OVERVIEW
|
||||
========
|
||||
@ -7,16 +8,18 @@ LuaIRC is a fully featured IRC framework written entirely in Lua. It provides an
|
||||
|
||||
INSTALL
|
||||
=======
|
||||
This module requires LuaSocket (http://www.cs.princeton.edu/~diego/professional/luasocket/) and Lua 5.1. To install, modify the Make.config file with paths appropriate to your system and run 'make install'.
|
||||
This module requires LuaSocket (http://www.cs.princeton.edu/~diego/professional/luasocket/) and Lua 5.3. To install, modify the Make.config file with paths appropriate to your system and run 'make install'.
|
||||
|
||||
DOCUMENTATION
|
||||
=============
|
||||
Documentation of the API can be found in the doc/ directory. It was autogenerated from the source files by LuaDoc (http://luadoc.luaforge.net/).
|
||||
|
||||
LuaIRC has only been tested on Freenode so far, but I plan to expand this to other servers in the future. It's quite possible that it works on other servers anyway, however, so feel free to try it out, and send in bug reports for things that break.
|
||||
LuaIRC has only been tested on Freenode and Libera.chat so far, but I plan to expand this to other servers in the future. It's quite possible that it works on other servers anyway, however, so feel free to try it out, and send in bug reports for things that break.
|
||||
|
||||
CHANGES
|
||||
=======
|
||||
0.3 (Lua 5.3 Port)
|
||||
- Unofficial port to work with Lua 5.3
|
||||
0.3
|
||||
- Major cleanup and restructuring again, documentation added, first public release
|
||||
0.2
|
||||
|
239
src/irc.lua
239
src/irc.lua
@ -1,31 +1,46 @@
|
||||
---
|
||||
-- Implementation of the main LuaIRC module
|
||||
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local constants = require 'irc.constants'
|
||||
local ctcp = require 'irc.ctcp'
|
||||
local c = ctcp._ctcp_quote
|
||||
local irc_debug = require 'irc.debug'
|
||||
local message = require 'irc.message'
|
||||
local misc = require 'irc.misc'
|
||||
local socket = require 'socket'
|
||||
local os = require 'os'
|
||||
local string = require 'string'
|
||||
local table = require 'table'
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- LuaIRC - IRC framework written in Lua
|
||||
-- @release 0.3
|
||||
module 'irc'
|
||||
local irc = {}
|
||||
|
||||
-- constants {{{
|
||||
_VERSION = 'LuaIRC 0.3'
|
||||
irc._VERSION = 'LuaIRC 0.3 (Lua 5.3 Port)'
|
||||
-- }}}
|
||||
|
||||
-- libraries {{{
|
||||
local libs = {}
|
||||
|
||||
libs.irc = irc
|
||||
libs.socket = require 'socket'
|
||||
|
||||
local old_libs = _G.libs
|
||||
_G.libs = libs
|
||||
|
||||
libs.constants = require 'irc.constants'
|
||||
libs.ctcp = require 'irc.ctcp'
|
||||
libs.debug = require 'irc.debug'
|
||||
libs.misc = require 'irc.misc'
|
||||
libs.channel = require 'irc.channel'
|
||||
libs.dcc = require 'irc.dcc'
|
||||
libs.message = require 'irc.message'
|
||||
|
||||
_G.libs = old_libs
|
||||
|
||||
-- localize modules {{{
|
||||
local constants = libs.constants
|
||||
local ctcp = libs.ctcp
|
||||
local c = ctcp._ctcp_quote
|
||||
local irc_debug = libs.debug
|
||||
local message = libs.message
|
||||
local misc = libs.misc
|
||||
local socket = libs.socket
|
||||
-- }}}
|
||||
|
||||
-- classes {{{
|
||||
local Channel = base.require 'irc.channel'
|
||||
local Channel = libs.channel
|
||||
-- }}}
|
||||
|
||||
-- local variables {{{
|
||||
@ -51,14 +66,14 @@ local ip = nil
|
||||
-- }}}
|
||||
|
||||
-- defaults {{{
|
||||
TIMEOUT = 60 -- connection timeout
|
||||
NETWORK = "localhost" -- default network
|
||||
PORT = 6667 -- default port
|
||||
NICK = "luabot" -- default nick
|
||||
USERNAME = "LuaIRC" -- default username
|
||||
REALNAME = "LuaIRC" -- default realname
|
||||
DEBUG = false -- whether we want extra debug information
|
||||
OUTFILE = nil -- file to send debug output to - nil is stdout
|
||||
irc.TIMEOUT = 60 -- connection timeout
|
||||
irc.NETWORK = "localhost" -- default network
|
||||
irc.PORT = 6667 -- default port
|
||||
irc.NICK = "luabot" -- default nick
|
||||
irc.USERNAME = "LuaIRC" -- default username
|
||||
irc.REALNAME = "LuaIRC" -- default realname
|
||||
irc.DEBUG = false -- whether we want extra debug information
|
||||
irc.OUTFILE = nil -- file to send debug output to - nil is stdout
|
||||
-- }}}
|
||||
|
||||
-- private functions {{{
|
||||
@ -68,21 +83,21 @@ local function main_loop_iter()
|
||||
local rready, wready, err = socket.select(rsockets, wsockets)
|
||||
if err then irc_debug._err(err); return false; end
|
||||
|
||||
for _, sock in base.ipairs(rready) do
|
||||
for _, sock in ipairs(rready) do
|
||||
local cb = socket.protect(rcallbacks[sock])
|
||||
local ret, err = cb(sock)
|
||||
if not ret then
|
||||
irc_debug._warn("socket error: " .. err)
|
||||
_unregister_socket(sock, 'r')
|
||||
irc._unregister_socket(sock, 'r')
|
||||
end
|
||||
end
|
||||
|
||||
for _, sock in base.ipairs(wready) do
|
||||
for _, sock in ipairs(wready) do
|
||||
local cb = socket.protect(wcallbacks[sock])
|
||||
local ret, err = cb(sock)
|
||||
if not ret then
|
||||
irc_debug._warn("socket error: " .. err)
|
||||
_unregister_socket(sock, 'w')
|
||||
irc._unregister_socket(sock, 'w')
|
||||
end
|
||||
end
|
||||
|
||||
@ -103,7 +118,7 @@ local function incoming_message(sock)
|
||||
local msg = message._parse(raw_msg)
|
||||
misc._try_call_warn("Unhandled server message: " .. msg.command,
|
||||
handlers["on_" .. msg.command:lower()],
|
||||
(misc._parse_user(msg.from)), base.unpack(msg.args))
|
||||
(misc._parse_user(msg.from)), table.unpack(msg.args))
|
||||
return true
|
||||
end
|
||||
-- }}}
|
||||
@ -119,7 +134,7 @@ end
|
||||
-- command handlers {{{
|
||||
-- on_nick {{{
|
||||
function handlers.on_nick(from, new_nick)
|
||||
for chan in channels() do
|
||||
for chan in irc.channels() do
|
||||
chan:_change_nick(from, new_nick)
|
||||
end
|
||||
callback("nick_change", new_nick, from)
|
||||
@ -128,7 +143,7 @@ end
|
||||
|
||||
-- on_join {{{
|
||||
function handlers.on_join(from, chan)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received join message for unknown channel: " .. chan)
|
||||
if serverinfo.channels[chan].join_complete then
|
||||
serverinfo.channels[chan]:_add_user(from)
|
||||
@ -157,7 +172,7 @@ function handlers.on_mode(from, to, mode_string, ...)
|
||||
|
||||
if to:sub(1, 1) == "#" then
|
||||
-- handle channel mode requests {{{
|
||||
base.assert(serverinfo.channels[to],
|
||||
assert(serverinfo.channels[to],
|
||||
"Received mode change for unknown channel: " .. to)
|
||||
local chan = serverinfo.channels[to]
|
||||
local ind = 1
|
||||
@ -204,7 +219,7 @@ end
|
||||
|
||||
-- on_topic {{{
|
||||
function handlers.on_topic(from, chan, new_topic)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received topic message for unknown channel: " .. chan)
|
||||
serverinfo.channels[chan]._topic.text = new_topic
|
||||
serverinfo.channels[chan]._topic.user = from
|
||||
@ -223,7 +238,7 @@ end
|
||||
|
||||
-- on_kick {{{
|
||||
function handlers.on_kick(from, chan, to)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received kick message for unknown channel: " .. chan)
|
||||
if serverinfo.channels[chan].join_complete then
|
||||
serverinfo.channels[chan]:_remove_user(to)
|
||||
@ -235,7 +250,7 @@ end
|
||||
-- on_privmsg {{{
|
||||
function handlers.on_privmsg(from, to, msg)
|
||||
local msgs = ctcp._ctcp_split(msg)
|
||||
for _, v in base.ipairs(msgs) do
|
||||
for _, v in ipairs(msgs) do
|
||||
local msg = v.str
|
||||
if v.ctcp then
|
||||
-- ctcp message {{{
|
||||
@ -245,16 +260,16 @@ function handlers.on_privmsg(from, to, msg)
|
||||
table.remove(words, 1)
|
||||
-- not using try_call here because the ctcp specification requires
|
||||
-- an error response to nonexistant commands
|
||||
if base.type(ctcp_handlers[cb]) == "function" then
|
||||
if type(ctcp_handlers[cb]) == "function" then
|
||||
ctcp_handlers[cb](from, to, table.concat(words, " "))
|
||||
else
|
||||
notice(from, c("ERRMSG", received_command, ":Unknown query"))
|
||||
irc.notice(from, c("ERRMSG", received_command, ":Unknown query"))
|
||||
end
|
||||
-- }}}
|
||||
else
|
||||
-- normal message {{{
|
||||
if to:sub(1, 1) == "#" then
|
||||
base.assert(serverinfo.channels[to],
|
||||
assert(serverinfo.channels[to],
|
||||
"Received channel msg from unknown channel: " .. to)
|
||||
callback("channel_msg", serverinfo.channels[to], from, msg)
|
||||
else
|
||||
@ -269,7 +284,7 @@ end
|
||||
-- on_notice {{{
|
||||
function handlers.on_notice(from, to, msg)
|
||||
local msgs = ctcp._ctcp_split(msg)
|
||||
for _, v in base.ipairs(msgs) do
|
||||
for _, v in ipairs(msgs) do
|
||||
local msg = v.str
|
||||
if v.ctcp then
|
||||
-- ctcp message {{{
|
||||
@ -283,7 +298,7 @@ function handlers.on_notice(from, to, msg)
|
||||
else
|
||||
-- normal message {{{
|
||||
if to:sub(1, 1) == "#" then
|
||||
base.assert(serverinfo.channels[to],
|
||||
assert(serverinfo.channels[to],
|
||||
"Received channel msg from unknown channel: " .. to)
|
||||
callback("channel_notice", serverinfo.channels[to], from, msg)
|
||||
else
|
||||
@ -297,7 +312,7 @@ end
|
||||
|
||||
-- on_quit {{{
|
||||
function handlers.on_quit(from, quit_msg)
|
||||
for name, chan in base.pairs(serverinfo.channels) do
|
||||
for name, chan in pairs(serverinfo.channels) do
|
||||
chan:_remove_user(from)
|
||||
end
|
||||
callback("quit", from, quit_msg)
|
||||
@ -307,7 +322,7 @@ end
|
||||
-- on_ping {{{
|
||||
-- respond to server pings to make sure it knows we are alive
|
||||
function handlers.on_ping(from, respond_to)
|
||||
send("PONG", respond_to)
|
||||
irc.send("PONG", respond_to)
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
@ -316,7 +331,7 @@ end
|
||||
-- on_rpl_topic {{{
|
||||
-- catch topic changes
|
||||
function handlers.on_rpl_topic(from, chan, topic)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received topic information about unknown channel: " .. chan)
|
||||
serverinfo.channels[chan]._topic.text = topic
|
||||
end
|
||||
@ -324,7 +339,7 @@ end
|
||||
|
||||
-- on_rpl_notopic {{{
|
||||
function handlers.on_rpl_notopic(from, chan)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received topic information about unknown channel: " .. chan)
|
||||
serverinfo.channels[chan]._topic.text = ""
|
||||
end
|
||||
@ -333,21 +348,21 @@ end
|
||||
-- on_rpl_topicdate {{{
|
||||
-- "topic was set by <user> at <time>"
|
||||
function handlers.on_rpl_topicdate(from, chan, user, time)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received topic information about unknown channel: " .. chan)
|
||||
serverinfo.channels[chan]._topic.user = user
|
||||
serverinfo.channels[chan]._topic.time = base.tonumber(time)
|
||||
serverinfo.channels[chan]._topic.time = tonumber(time)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
-- on_rpl_namreply {{{
|
||||
-- handles a NAMES reply
|
||||
function handlers.on_rpl_namreply(from, chanmode, chan, userlist)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received user information about unknown channel: " .. chan)
|
||||
serverinfo.channels[chan]._chanmode = constants.chanmodes[chanmode]
|
||||
local users = misc._split(userlist)
|
||||
for k,v in base.ipairs(users) do
|
||||
for k,v in ipairs(users) do
|
||||
if v:sub(1, 1) == "@" or v:sub(1, 1) == "+" then
|
||||
local nick = v:sub(2)
|
||||
serverinfo.channels[chan]:_add_user(nick, v:sub(1, 1))
|
||||
@ -362,7 +377,7 @@ end
|
||||
-- when we get this message, the channel join has completed, so call the
|
||||
-- external cb
|
||||
function handlers.on_rpl_endofnames(from, chan)
|
||||
base.assert(serverinfo.channels[chan],
|
||||
assert(serverinfo.channels[chan],
|
||||
"Received user information about unknown channel: " .. chan)
|
||||
if not serverinfo.channels[chan].join_complete then
|
||||
callback("me_join", serverinfo.channels[chan])
|
||||
@ -425,7 +440,7 @@ function handlers.on_rpl_whoischannels(from, nick, channel_list)
|
||||
if not requestinfo.whois[nick].channels then
|
||||
requestinfo.whois[nick].channels = {}
|
||||
end
|
||||
for _, channel in base.ipairs(misc._split(channel_list)) do
|
||||
for _, channel in ipairs(misc._split(channel_list)) do
|
||||
table.insert(requestinfo.whois[nick].channels, channel)
|
||||
end
|
||||
end
|
||||
@ -466,7 +481,7 @@ function handlers.on_rpl_endofwhois(from, nick)
|
||||
local cb = table.remove(icallbacks.whois[nick], 1)
|
||||
cb(requestinfo.whois[nick])
|
||||
requestinfo.whois[nick] = nil
|
||||
if #icallbacks.whois[nick] > 0 then send("WHOIS", nick)
|
||||
if #icallbacks.whois[nick] > 0 then irc.send("WHOIS", nick)
|
||||
else icallbacks.whois[nick] = nil
|
||||
end
|
||||
end
|
||||
@ -476,7 +491,7 @@ end
|
||||
function handlers.on_rpl_version(from, version, server, comments)
|
||||
local cb = table.remove(icallbacks.serverversion[server], 1)
|
||||
cb({version = version, server = server, comments = comments})
|
||||
if #icallbacks.serverversion[server] > 0 then send("VERSION", server)
|
||||
if #icallbacks.serverversion[server] > 0 then irc.send("VERSION", server)
|
||||
else icallbacks.serverversion[server] = nil
|
||||
end
|
||||
end
|
||||
@ -486,7 +501,7 @@ end
|
||||
function on_rpl_time(from, server, time)
|
||||
local cb = table.remove(icallbacks.servertime[server], 1)
|
||||
cb({time = time, server = server})
|
||||
if #icallbacks.servertime[server] > 0 then send("TIME", server)
|
||||
if #icallbacks.servertime[server] > 0 then irc.send("TIME", server)
|
||||
else icallbacks.servertime[server] = nil
|
||||
end
|
||||
end
|
||||
@ -498,7 +513,7 @@ end
|
||||
-- on_action {{{
|
||||
function ctcp_handlers.on_action(from, to, message)
|
||||
if to:sub(1, 1) == "#" then
|
||||
base.assert(serverinfo.channels[to],
|
||||
assert(serverinfo.channels[to],
|
||||
"Received channel msg from unknown channel: " .. to)
|
||||
callback("channel_act", serverinfo.channels[to], from, message)
|
||||
else
|
||||
@ -511,7 +526,7 @@ end
|
||||
-- TODO: can we not have this handler be registered unless the dcc module is
|
||||
-- loaded?
|
||||
function ctcp_handlers.on_dcc(from, to, message)
|
||||
local type, argument, address, port, size = base.unpack(misc._split(message, " ", nil, '"', '"'))
|
||||
local type, argument, address, port, size = table.unpack(misc._split(message, " ", nil, '"', '"'))
|
||||
address = misc._ip_int_to_str(address)
|
||||
if type == "SEND" then
|
||||
if callback("dcc_send", from, to, argument, address, port, size) then
|
||||
@ -525,25 +540,25 @@ end
|
||||
|
||||
-- on_version {{{
|
||||
function ctcp_handlers.on_version(from, to)
|
||||
notice(from, c("VERSION", _VERSION .. " running under " .. base._VERSION .. " with " .. socket._VERSION))
|
||||
irc.notice(from, c("VERSION", irc._VERSION .. " running under " .. _VERSION .. " with " .. socket._VERSION))
|
||||
end
|
||||
-- }}}
|
||||
|
||||
-- on_errmsg {{{
|
||||
function ctcp_handlers.on_errmsg(from, to, message)
|
||||
notice(from, c("ERRMSG", message, ":No error has occurred"))
|
||||
irc.notice(from, c("ERRMSG", message, ":No error has occurred"))
|
||||
end
|
||||
-- }}}
|
||||
|
||||
-- on_ping {{{
|
||||
function ctcp_handlers.on_ping(from, to, timestamp)
|
||||
notice(from, c("PING", timestamp))
|
||||
irc.notice(from, c("PING", timestamp))
|
||||
end
|
||||
-- }}}
|
||||
|
||||
-- on_time {{{
|
||||
function ctcp_handlers.on_time(from, to)
|
||||
notice(from, c("TIME", os.date()))
|
||||
irc.notice(from, c("TIME", os.date()))
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
@ -559,7 +574,7 @@ function ctcp_handlers.on_rpl_version(from, to, version)
|
||||
local lfrom = from:lower()
|
||||
local cb = table.remove(icallbacks.ctcp_version[lfrom], 1)
|
||||
cb({version = version, nick = from})
|
||||
if #icallbacks.ctcp_version[lfrom] > 0 then say(from, c("VERSION"))
|
||||
if #icallbacks.ctcp_version[lfrom] > 0 then irc.say(from, c("VERSION"))
|
||||
else icallbacks.ctcp_version[lfrom] = nil
|
||||
end
|
||||
end
|
||||
@ -576,7 +591,7 @@ function ctcp_handlers.on_rpl_ping(from, to, timestamp)
|
||||
local lfrom = from:lower()
|
||||
local cb = table.remove(icallbacks.ctcp_ping[lfrom], 1)
|
||||
cb({time = os.time() - timestamp, nick = from})
|
||||
if #icallbacks.ctcp_ping[lfrom] > 0 then say(from, c("PING", os.time()))
|
||||
if #icallbacks.ctcp_ping[lfrom] > 0 then irc.say(from, c("PING", os.time()))
|
||||
else icallbacks.ctcp_ping[lfrom] = nil
|
||||
end
|
||||
end
|
||||
@ -587,7 +602,7 @@ function ctcp_handlers.on_rpl_time(from, to, time)
|
||||
local lfrom = from:lower()
|
||||
local cb = table.remove(icallbacks.ctcp_time[lfrom], 1)
|
||||
cb({time = time, nick = from})
|
||||
if #icallbacks.ctcp_time[lfrom] > 0 then say(from, c("TIME"))
|
||||
if #icallbacks.ctcp_time[lfrom] > 0 then irc.say(from, c("TIME"))
|
||||
else icallbacks.ctcp_time[lfrom] = nil
|
||||
end
|
||||
end
|
||||
@ -605,7 +620,7 @@ end
|
||||
-- @param mode 'r' if the socket is for reading, 'w' if for writing
|
||||
-- @param cb Callback to call when the socket is ready for reading/writing.
|
||||
-- It will be called with the socket as the single argument.
|
||||
function _register_socket(sock, mode, cb)
|
||||
function irc._register_socket(sock, mode, cb)
|
||||
local socks, cbs
|
||||
if mode == 'r' then
|
||||
socks = rsockets
|
||||
@ -614,7 +629,7 @@ function _register_socket(sock, mode, cb)
|
||||
socks = wsockets
|
||||
cbs = wcallbacks
|
||||
end
|
||||
base.assert(not cbs[sock], "socket already registered")
|
||||
assert(not cbs[sock], "socket already registered")
|
||||
table.insert(socks, sock)
|
||||
cbs[sock] = cb
|
||||
end
|
||||
@ -625,7 +640,7 @@ end
|
||||
-- Remove a previously registered socket.
|
||||
-- @param sock Socket to unregister
|
||||
-- @param mode 'r' to unregister it for reading, 'w' for writing
|
||||
function _unregister_socket(sock, mode)
|
||||
function irc._unregister_socket(sock, mode)
|
||||
local socks, cbs
|
||||
if mode == 'r' then
|
||||
socks = rsockets
|
||||
@ -634,7 +649,7 @@ function _unregister_socket(sock, mode)
|
||||
socks = wsockets
|
||||
cbs = wcallbacks
|
||||
end
|
||||
for i, v in base.ipairs(socks) do
|
||||
for i, v in ipairs(socks) do
|
||||
if v == sock then table.remove(socks, i); break; end
|
||||
end
|
||||
cbs[sock] = nil
|
||||
@ -670,22 +685,22 @@ end
|
||||
-- dropping an idle connection
|
||||
-- (default: '60')</li>
|
||||
-- </ul>
|
||||
function connect(args)
|
||||
local network = args.network or NETWORK
|
||||
local port = args.port or PORT
|
||||
local nick = args.nick or NICK
|
||||
local username = args.username or USERNAME
|
||||
local realname = args.realname or REALNAME
|
||||
local timeout = args.timeout or TIMEOUT
|
||||
function irc.connect(args)
|
||||
local network = args.network or irc.NETWORK
|
||||
local port = args.port or irc.PORT
|
||||
local nick = args.nick or irc.NICK
|
||||
local username = args.username or irc.USERNAME
|
||||
local realname = args.realname or irc.REALNAME
|
||||
local timeout = args.timeout or irc.TIMEOUT
|
||||
serverinfo.connecting = true
|
||||
if OUTFILE then irc_debug.set_output(OUTFILE) end
|
||||
if DEBUG then irc_debug.enable() end
|
||||
irc_sock = base.assert(socket.connect(network, port))
|
||||
if irc.OUTFILE then irc_debug.set_output(irc.OUTFILE) end
|
||||
if irc.DEBUG then irc_debug.enable() end
|
||||
irc_sock = assert(socket.connect(network, port))
|
||||
irc_sock:settimeout(timeout)
|
||||
_register_socket(irc_sock, 'r', incoming_message)
|
||||
if args.pass then send("PASS", args.pass) end
|
||||
send("NICK", nick)
|
||||
send("USER", username, get_ip(), network, realname)
|
||||
irc._register_socket(irc_sock, 'r', incoming_message)
|
||||
if args.pass then irc.send("PASS", args.pass) end
|
||||
irc.send("NICK", nick)
|
||||
irc.send("USER", username, irc.get_ip(), network, realname)
|
||||
begin_main_loop()
|
||||
end
|
||||
-- }}}
|
||||
@ -694,9 +709,9 @@ end
|
||||
---
|
||||
-- Close the connection to the irc server.
|
||||
-- @param message Quit message (optional, defaults to 'Leaving')
|
||||
function quit(message)
|
||||
function irc.quit(message)
|
||||
message = message or "Leaving"
|
||||
send("QUIT", message)
|
||||
irc.send("QUIT", message)
|
||||
serverinfo.connected = false
|
||||
end
|
||||
-- }}}
|
||||
@ -705,10 +720,10 @@ end
|
||||
---
|
||||
-- Join a channel.
|
||||
-- @param channel Channel to join
|
||||
function join(channel)
|
||||
function irc.join(channel)
|
||||
if not channel then return end
|
||||
serverinfo.channels[channel] = Channel.new(channel)
|
||||
send("JOIN", channel)
|
||||
irc.send("JOIN", channel)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
@ -716,10 +731,10 @@ end
|
||||
---
|
||||
-- Leave a channel.
|
||||
-- @param channel Channel to leave
|
||||
function part(channel)
|
||||
function irc.part(channel)
|
||||
if not channel then return end
|
||||
serverinfo.channels[channel] = nil
|
||||
send("PART", channel)
|
||||
irc.send("PART", channel)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
@ -728,10 +743,10 @@ end
|
||||
-- Send a message to a user or channel.
|
||||
-- @param name User or channel to send the message to
|
||||
-- @param message Message to send
|
||||
function say(name, message)
|
||||
function irc.say(name, message)
|
||||
if not name then return end
|
||||
message = message or ""
|
||||
send("PRIVMSG", name, message)
|
||||
irc.send("PRIVMSG", name, message)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
@ -740,10 +755,10 @@ end
|
||||
-- Send a notice to a user or channel.
|
||||
-- @param name User or channel to send the notice to
|
||||
-- @param message Message to send
|
||||
function notice(name, message)
|
||||
function irc.notice(name, message)
|
||||
if not name then return end
|
||||
message = message or ""
|
||||
send("NOTICE", name, message)
|
||||
irc.send("NOTICE", name, message)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
@ -752,10 +767,10 @@ end
|
||||
-- Perform a /me action.
|
||||
-- @param name User or channel to send the action to
|
||||
-- @param action Action to send
|
||||
function act(name, action)
|
||||
function irc.act(name, action)
|
||||
if not name then return end
|
||||
action = action or ""
|
||||
send("PRIVMSG", name, c("ACTION", action))
|
||||
irc.send("PRIVMSG", name, c("ACTION", action))
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
@ -771,13 +786,13 @@ end
|
||||
-- <li><i>version:</i> the server version</li>
|
||||
-- <li><i>comments:</i> other data provided by the server</li>
|
||||
-- </ul>
|
||||
function server_version(cb)
|
||||
function irc.server_version(cb)
|
||||
-- apparently the optional server parameter isn't supported for servers
|
||||
-- which you are not directly connected to (freenode specific?)
|
||||
local server = serverinfo.host
|
||||
if not icallbacks.serverversion[server] then
|
||||
icallbacks.serverversion[server] = {cb}
|
||||
send("VERSION", server)
|
||||
irc.send("VERSION", server)
|
||||
else
|
||||
table.insert(icallbacks.serverversion[server], cb)
|
||||
end
|
||||
@ -808,12 +823,12 @@ end
|
||||
-- joined</li>
|
||||
-- </ul>
|
||||
-- @param nick User to request WHOIS information about
|
||||
function whois(cb, nick)
|
||||
function irc.whois(cb, nick)
|
||||
nick = nick:lower()
|
||||
requestinfo.whois[nick] = {}
|
||||
if not icallbacks.whois[nick] then
|
||||
icallbacks.whois[nick] = {cb}
|
||||
send("WHOIS", nick)
|
||||
irc.send("WHOIS", nick)
|
||||
else
|
||||
table.insert(icallbacks.whois[nick], cb)
|
||||
end
|
||||
@ -829,13 +844,13 @@ end
|
||||
-- <li><i>server:</i> the server which responded to the request</li>
|
||||
-- <li><i>time:</i> the time reported by the server</li>
|
||||
-- </ul>
|
||||
function server_time(cb)
|
||||
function irc.server_time(cb)
|
||||
-- apparently the optional server parameter isn't supported for servers
|
||||
-- which you are not directly connected to (freenode specific?)
|
||||
local server = serverinfo.host
|
||||
if not icallbacks.servertime[server] then
|
||||
icallbacks.servertime[server] = {cb}
|
||||
send("TIME", server)
|
||||
irc.send("TIME", server)
|
||||
else
|
||||
table.insert(icallbacks.servertime[server], cb)
|
||||
end
|
||||
@ -854,11 +869,11 @@ end
|
||||
-- <li><i>time:</i> the roundtrip ping time, in seconds</li>
|
||||
-- </ul>
|
||||
-- @param nick User to ping
|
||||
function ctcp_ping(cb, nick)
|
||||
function irc.ctcp_ping(cb, nick)
|
||||
nick = nick:lower()
|
||||
if not icallbacks.ctcp_ping[nick] then
|
||||
icallbacks.ctcp_ping[nick] = {cb}
|
||||
say(nick, c("PING", os.time()))
|
||||
irc.say(nick, c("PING", os.time()))
|
||||
else
|
||||
table.insert(icallbacks.ctcp_ping[nick], cb)
|
||||
end
|
||||
@ -875,11 +890,11 @@ end
|
||||
-- <li><i>time:</i> the localtime reported by the remote client</li>
|
||||
-- </ul>
|
||||
-- @param nick User to request the localtime from
|
||||
function ctcp_time(cb, nick)
|
||||
function irc.ctcp_time(cb, nick)
|
||||
nick = nick:lower()
|
||||
if not icallbacks.ctcp_time[nick] then
|
||||
icallbacks.ctcp_time[nick] = {cb}
|
||||
say(nick, c("TIME"))
|
||||
irc.say(nick, c("TIME"))
|
||||
else
|
||||
table.insert(icallbacks.ctcp_time[nick], cb)
|
||||
end
|
||||
@ -896,11 +911,11 @@ end
|
||||
-- <li><i>version:</i> the version reported by the remote client</li>
|
||||
-- </ul>
|
||||
-- @param nick User to request the client version from
|
||||
function ctcp_version(cb, nick)
|
||||
function irc.ctcp_version(cb, nick)
|
||||
nick = nick:lower()
|
||||
if not icallbacks.ctcp_version[nick] then
|
||||
icallbacks.ctcp_version[nick] = {cb}
|
||||
say(nick, c("VERSION"))
|
||||
irc.say(nick, c("VERSION"))
|
||||
else
|
||||
table.insert(icallbacks.ctcp_version[nick], cb)
|
||||
end
|
||||
@ -917,7 +932,7 @@ end
|
||||
-- callback for this event
|
||||
-- @return Value of the original callback for this event (or nil if no previous
|
||||
-- callback had been set)
|
||||
function register_callback(name, fn)
|
||||
function irc.register_callback(name, fn)
|
||||
local old_handler = user_handlers[name]
|
||||
user_handlers[name] = fn
|
||||
return old_handler
|
||||
@ -936,10 +951,10 @@ end
|
||||
-- an array. Strings are sent literally, arrays are CTCP quoted
|
||||
-- as a group. The last argument (if it exists) is preceded by
|
||||
-- a : (so it may contain spaces).
|
||||
function send(command, ...)
|
||||
function irc.send(command, ...)
|
||||
if not serverinfo.connected and not serverinfo.connecting then return end
|
||||
local message = command
|
||||
for i, v in base.ipairs({...}) do
|
||||
for i, v in ipairs({...}) do
|
||||
if i == #{...} then
|
||||
v = ":" .. v
|
||||
end
|
||||
@ -958,7 +973,7 @@ end
|
||||
-- Get the local IP address for the server connection.
|
||||
-- @return A string representation of the local IP address that the IRC server
|
||||
-- connection is communicating on
|
||||
function get_ip()
|
||||
function irc.get_ip()
|
||||
return (ip or irc_sock:getsockname())
|
||||
end
|
||||
-- }}}
|
||||
@ -967,7 +982,7 @@ end
|
||||
---
|
||||
-- Set the local IP manually (to allow for NAT workarounds)
|
||||
-- @param new_ip IP address to set
|
||||
function set_ip(new_ip)
|
||||
function irc.set_ip(new_ip)
|
||||
ip = new_ip
|
||||
end
|
||||
-- }}}
|
||||
@ -979,7 +994,7 @@ end
|
||||
-- channels() is an iterator function for use in for loops.
|
||||
-- For example, <pre>for chan in irc.channels() do print(chan:name) end</pre>
|
||||
-- @see irc.channel
|
||||
function channels()
|
||||
function irc.channels()
|
||||
return function(state, arg)
|
||||
return misc._value_iter(state, arg,
|
||||
function(v)
|
||||
@ -992,3 +1007,5 @@ end
|
||||
-- }}}
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return irc
|
||||
|
@ -2,17 +2,15 @@
|
||||
-- Implementation of the Channel class
|
||||
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local irc = require 'irc'
|
||||
local misc = require 'irc.misc'
|
||||
local socket = require 'socket'
|
||||
local table = require 'table'
|
||||
local irc = libs.irc
|
||||
local misc = libs.misc
|
||||
local socket = libs.socket
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- This module implements a channel object representing a single channel we
|
||||
-- have joined.
|
||||
module 'irc.channel'
|
||||
local channel = {}
|
||||
|
||||
-- object metatable {{{
|
||||
-- TODO: this <br /> shouldn't be necessary - bug in luadoc
|
||||
@ -37,7 +35,7 @@ local mt = {
|
||||
elseif key == "chanmode" then
|
||||
return self._chanmode
|
||||
else
|
||||
return _M[key]
|
||||
return channel[key]
|
||||
end
|
||||
end,
|
||||
-- }}}
|
||||
@ -50,7 +48,7 @@ local mt = {
|
||||
elseif key == "chanmode" then
|
||||
return
|
||||
else
|
||||
base.rawset(self, key, value)
|
||||
rawset(self, key, value)
|
||||
end
|
||||
end,
|
||||
-- }}}
|
||||
@ -58,12 +56,12 @@ local mt = {
|
||||
__concat = function(first, second)
|
||||
local first_str, second_str
|
||||
|
||||
if base.type(first) == "table" then
|
||||
if type(first) == "table" then
|
||||
first_str = first._name
|
||||
else
|
||||
first_str = first
|
||||
end
|
||||
if base.type(second) == "table" then
|
||||
if type(second) == "table" then
|
||||
second_str = second._name
|
||||
else
|
||||
second_str = second
|
||||
@ -88,7 +86,7 @@ local mt = {
|
||||
-- @param self Channel object
|
||||
-- @param set True to set the mode, false to unset it
|
||||
-- @param letter Letter of the mode
|
||||
local function set_basic_mode(self, set, letter)
|
||||
local function set_basic_mode(set, letter)
|
||||
if set then
|
||||
irc.send("MODE", self.name, "+" .. letter)
|
||||
else
|
||||
@ -107,7 +105,7 @@ end
|
||||
-- @param self Channel object
|
||||
-- @param user Nick of the user to add
|
||||
-- @param mode Mode (op/voice) of the user, in symbolic form (@/+)
|
||||
function _add_user(self, user, mode)
|
||||
function channel:_add_user(user, mode)
|
||||
mode = mode or ''
|
||||
self._members[user] = mode .. user
|
||||
end
|
||||
@ -118,7 +116,7 @@ end
|
||||
-- Remove a user from the channel's internal user list.
|
||||
-- @param self Channel object
|
||||
-- @param user Nick of the user to remove
|
||||
function _remove_user(self, user)
|
||||
function channel:_remove_user(user)
|
||||
self._members[user] = nil
|
||||
end
|
||||
-- }}}
|
||||
@ -130,7 +128,7 @@ end
|
||||
-- @param user Nick of the user to affect
|
||||
-- @param on True if the mode is being set, false if it's being unset
|
||||
-- @param mode 'o' for op, 'v' for voice
|
||||
function _change_status(self, user, on, mode)
|
||||
function channel:_change_status(user, on, mode)
|
||||
if on then
|
||||
if mode == 'o' then
|
||||
self._members[user] = '@' .. user
|
||||
@ -152,7 +150,7 @@ end
|
||||
-- @param self Channel object
|
||||
-- @param old_nick User's old nick
|
||||
-- @param new_nick User's new nick
|
||||
function _change_nick(self, old_nick, new_nick)
|
||||
function channel:_change_nick(old_nick, new_nick)
|
||||
for member in self:each_member() do
|
||||
local member_nick = member:gsub('@+', '')
|
||||
if member_nick == old_nick then
|
||||
@ -172,8 +170,8 @@ end
|
||||
-- Creates a new Channel object.
|
||||
-- @param chan Name of the new channel
|
||||
-- @return The new channel instance
|
||||
function new(chan)
|
||||
return base.setmetatable({_name = chan, _topic = {}, _chanmode = "",
|
||||
function channel.new(chan)
|
||||
return setmetatable({_name = chan, _topic = {}, _chanmode = "",
|
||||
_members = {}}, mt)
|
||||
end
|
||||
-- }}}
|
||||
@ -184,7 +182,7 @@ end
|
||||
---
|
||||
-- Iterator over the ops in the channel
|
||||
-- @param self Channel object
|
||||
function each_op(self)
|
||||
function channel:each_op()
|
||||
return function(state, arg)
|
||||
return misc._value_iter(state, arg,
|
||||
function(v)
|
||||
@ -200,7 +198,7 @@ end
|
||||
---
|
||||
-- Iterator over the voiced users in the channel
|
||||
-- @param self Channel object
|
||||
function each_voice(self)
|
||||
function channel:each_voice()
|
||||
return function(state, arg)
|
||||
return misc._value_iter(state, arg,
|
||||
function(v)
|
||||
@ -216,7 +214,7 @@ end
|
||||
---
|
||||
-- Iterator over the normal users in the channel
|
||||
-- @param self Channel object
|
||||
function each_user(self)
|
||||
function channel:each_user()
|
||||
return function(state, arg)
|
||||
return misc._value_iter(state, arg,
|
||||
function(v)
|
||||
@ -233,7 +231,7 @@ end
|
||||
---
|
||||
-- Iterator over all users in the channel
|
||||
-- @param self Channel object
|
||||
function each_member(self)
|
||||
function channel:each_member()
|
||||
return misc._value_iter, self._members, nil
|
||||
end
|
||||
-- }}}
|
||||
@ -245,7 +243,7 @@ end
|
||||
-- Gets an array of all the ops in the channel.
|
||||
-- @param self Channel object
|
||||
-- @return Array of channel ops
|
||||
function ops(self)
|
||||
function channel:ops()
|
||||
local ret = {}
|
||||
for nick in self:each_op() do
|
||||
table.insert(ret, nick)
|
||||
@ -259,7 +257,7 @@ end
|
||||
-- Gets an array of all the voiced users in the channel.
|
||||
-- @param self Channel object
|
||||
-- @return Array of channel voiced users
|
||||
function voices(self)
|
||||
function channel:voices()
|
||||
local ret = {}
|
||||
for nick in self:each_voice() do
|
||||
table.insert(ret, nick)
|
||||
@ -273,7 +271,7 @@ end
|
||||
-- Gets an array of all the normal users in the channel.
|
||||
-- @param self Channel object
|
||||
-- @return Array of channel normal users
|
||||
function users(self)
|
||||
function channel:users()
|
||||
local ret = {}
|
||||
for nick in self:each_user() do
|
||||
table.insert(ret, nick)
|
||||
@ -287,7 +285,7 @@ end
|
||||
-- Gets an array of all the users in the channel.
|
||||
-- @param self Channel object
|
||||
-- @return Array of channel users
|
||||
function members(self)
|
||||
function channel:members()
|
||||
local ret = {}
|
||||
-- not just returning self._members, since the return value shouldn't be
|
||||
-- modifiable
|
||||
@ -306,7 +304,7 @@ end
|
||||
-- Ban a user from a channel.
|
||||
-- @param self Channel object
|
||||
-- @param name User to ban
|
||||
function ban(self, name)
|
||||
function channel:ban(name)
|
||||
irc.send("MODE", self.name, "+b", name)
|
||||
end
|
||||
-- }}}
|
||||
@ -317,7 +315,7 @@ end
|
||||
-- Remove a ban on a user.
|
||||
-- @param self Channel object
|
||||
-- @param name User to unban
|
||||
function unban(self, name)
|
||||
function channel:unban(name)
|
||||
irc.send("MODE", self.name, "-b", name)
|
||||
end
|
||||
-- }}}
|
||||
@ -327,7 +325,7 @@ end
|
||||
-- Give a user voice on a channel.
|
||||
-- @param self Channel object
|
||||
-- @param name User to give voice to
|
||||
function voice(self, name)
|
||||
function channel:voice(name)
|
||||
irc.send("MODE", self.name, "+v", name)
|
||||
end
|
||||
-- }}}
|
||||
@ -337,7 +335,7 @@ end
|
||||
-- Remove voice from a user.
|
||||
-- @param self Channel object
|
||||
-- @param name User to remove voice from
|
||||
function devoice(self, name)
|
||||
function channel:devoice(name)
|
||||
irc.send("MODE", self.name, "-v", name)
|
||||
end
|
||||
-- }}}
|
||||
@ -347,7 +345,7 @@ end
|
||||
-- Give a user ops on a channel.
|
||||
-- @param self Channel object
|
||||
-- @param name User to op
|
||||
function op(self, name)
|
||||
function channel:op(name)
|
||||
irc.send("MODE", self.name, "+o", name)
|
||||
end
|
||||
-- }}}
|
||||
@ -357,7 +355,7 @@ end
|
||||
-- Remove ops from a user.
|
||||
-- @param self Channel object
|
||||
-- @param name User to remove ops from
|
||||
function deop(self, name)
|
||||
function channel:deop(name)
|
||||
irc.send("MODE", self.name, "-o", name)
|
||||
end
|
||||
-- }}}
|
||||
@ -368,7 +366,7 @@ end
|
||||
-- @param self Channel object
|
||||
-- @param new_limit New value for the channel limit (optional; limit is unset
|
||||
-- if this argument isn't passed)
|
||||
function set_limit(self, new_limit)
|
||||
function channel:set_limit(new_limit)
|
||||
if new_limit then
|
||||
irc.send("MODE", self.name, "+l", new_limit)
|
||||
else
|
||||
@ -383,7 +381,7 @@ end
|
||||
-- @param self Channel object
|
||||
-- @param key New channel password (optional; password is unset if this
|
||||
-- argument isn't passed)
|
||||
function set_key(self, key)
|
||||
function channel:set_key(key)
|
||||
if key then
|
||||
irc.send("MODE", self.name, "+k", key)
|
||||
else
|
||||
@ -397,7 +395,7 @@ end
|
||||
-- Set the private state of a channel.
|
||||
-- @param self Channel object
|
||||
-- @param set True to set the channel as private, false to unset it
|
||||
function set_private(self, set)
|
||||
function channel:set_private(set)
|
||||
set_basic_mode(self, set, "p")
|
||||
end
|
||||
-- }}}
|
||||
@ -407,7 +405,7 @@ end
|
||||
-- Set the secret state of a channel.
|
||||
-- @param self Channel object
|
||||
-- @param set True to set the channel as secret, false to unset it
|
||||
function set_secret(self, set)
|
||||
function channel:set_secret(set)
|
||||
set_basic_mode(self, set, "s")
|
||||
end
|
||||
-- }}}
|
||||
@ -417,7 +415,7 @@ end
|
||||
-- Set whether joining the channel requires an invite.
|
||||
-- @param self Channel object
|
||||
-- @param set True to set the channel invite only, false to unset it
|
||||
function set_invite_only(self, set)
|
||||
function channel:set_invite_only(set)
|
||||
set_basic_mode(self, set, "i")
|
||||
end
|
||||
-- }}}
|
||||
@ -427,7 +425,7 @@ end
|
||||
-- If true, the topic can only be changed by an op.
|
||||
-- @param self Channel object
|
||||
-- @param set True to lock the topic, false to unlock it
|
||||
function set_topic_lock(self, set)
|
||||
function channel:set_topic_lock(set)
|
||||
set_basic_mode(self, set, "t")
|
||||
end
|
||||
-- }}}
|
||||
@ -438,7 +436,7 @@ end
|
||||
-- @param self Channel object
|
||||
-- @param set True to require users to be in the channel to send messages to
|
||||
-- it, false to remove this restriction
|
||||
function set_no_outside_messages(self, set)
|
||||
function channel:set_no_outside_messages(set)
|
||||
set_basic_mode(self, set, "n")
|
||||
end
|
||||
-- }}}
|
||||
@ -448,7 +446,7 @@ end
|
||||
-- Set whether voice is required to speak.
|
||||
-- @param self Channel object
|
||||
-- @param set True to set the channel as moderated, false to unset it
|
||||
function set_moderated(self, set)
|
||||
function channel:set_moderated(set)
|
||||
set_basic_mode(self, set, "m")
|
||||
end
|
||||
-- }}}
|
||||
@ -461,7 +459,7 @@ end
|
||||
-- @param self Channel object
|
||||
-- @param nick Nick to search for
|
||||
-- @return True if the nick is in the channel, false otherwise
|
||||
function contains(self, nick)
|
||||
function channel:contains(nick)
|
||||
for member in self:each_member() do
|
||||
local member_nick = member:gsub('@+', '')
|
||||
if member_nick == nick then
|
||||
@ -473,3 +471,5 @@ end
|
||||
-- }}}
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return channel
|
||||
|
@ -1,13 +1,13 @@
|
||||
---
|
||||
-- This module holds various constants used by the IRC protocol.
|
||||
module "irc.constants"
|
||||
local constants = {}
|
||||
|
||||
-- protocol constants {{{
|
||||
IRC_MAX_MSG = 512
|
||||
constants.IRC_MAX_MSG = 512
|
||||
-- }}}
|
||||
|
||||
-- server replies {{{
|
||||
replies = {
|
||||
constants.replies = {
|
||||
-- Command responses {{{
|
||||
[001] = "RPL_WELCOME",
|
||||
[002] = "RPL_YOURHOST",
|
||||
@ -183,9 +183,11 @@ replies = {
|
||||
-- }}}
|
||||
|
||||
-- chanmodes {{{
|
||||
chanmodes = {
|
||||
constants.chanmodes = {
|
||||
["@"] = "secret",
|
||||
["*"] = "private",
|
||||
["="] = "public"
|
||||
}
|
||||
-- }}}
|
||||
|
||||
return constants
|
||||
|
@ -1,14 +1,10 @@
|
||||
---
|
||||
-- Implementation of the CTCP protocol
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local table = require "table"
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- This module implements the various quoting and escaping requirements of the
|
||||
-- CTCP protocol.
|
||||
module "irc.ctcp"
|
||||
local ctcp = {}
|
||||
|
||||
-- internal functions {{{
|
||||
-- _low_quote {{{
|
||||
@ -17,7 +13,7 @@ module "irc.ctcp"
|
||||
-- to appear in an IRC packet).
|
||||
-- @param ... Strings to quote together, space separated
|
||||
-- @return Quoted string
|
||||
function _low_quote(...)
|
||||
function ctcp._low_quote(...)
|
||||
local str = table.concat({...}, " ")
|
||||
return str:gsub("[%z\n\r\020]", {["\000"] = "\0200",
|
||||
["\n"] = "\020n",
|
||||
@ -31,7 +27,7 @@ end
|
||||
-- Removes low level quoting done by low_quote.
|
||||
-- @param str String with low level quoting applied to it
|
||||
-- @return String with those quoting methods stripped off
|
||||
function _low_dequote(str)
|
||||
function ctcp._low_dequote(str)
|
||||
return str:gsub("\020(.?)", function(s)
|
||||
if s == "0" then return "\000" end
|
||||
if s == "n" then return "\n" end
|
||||
@ -48,7 +44,7 @@ end
|
||||
-- data (by the calling program).
|
||||
-- @param ... Strings to apply CTCP quoting to together, space separated
|
||||
-- @return String with CTCP quoting applied
|
||||
function _ctcp_quote(...)
|
||||
function ctcp._ctcp_quote(...)
|
||||
local str = table.concat({...}, " ")
|
||||
local ret = str:gsub("[\001\\]", {["\001"] = "\\a",
|
||||
["\\"] = "\\\\"})
|
||||
@ -62,7 +58,7 @@ end
|
||||
-- data (likely by ctcp_split).
|
||||
-- @param str String with CTCP quoting
|
||||
-- @return String with all CTCP quoting stripped
|
||||
function _ctcp_dequote(str)
|
||||
function ctcp._ctcp_dequote(str)
|
||||
local ret = str:gsub("^\001", ""):gsub("\001$", "")
|
||||
return ret:gsub("\\(.?)", function(s)
|
||||
if s == "a" then return "\001" end
|
||||
@ -84,7 +80,7 @@ end
|
||||
-- <li><i>ctcp:</i> True if the section was a CTCP message, false
|
||||
-- otherwise</li>
|
||||
-- </ul>
|
||||
function _ctcp_split(str)
|
||||
function ctcp._ctcp_split(str)
|
||||
local ret = {}
|
||||
local iter = 1
|
||||
while true do
|
||||
@ -103,7 +99,7 @@ function _ctcp_split(str)
|
||||
end
|
||||
if not s then break end
|
||||
if ctcp_string ~= "" then
|
||||
table.insert(ret, {str = _ctcp_dequote(ctcp_string), ctcp = true})
|
||||
table.insert(ret, {str = ctcp._ctcp_dequote(ctcp_string), ctcp = true})
|
||||
end
|
||||
|
||||
iter = e + 1
|
||||
@ -113,3 +109,5 @@ function _ctcp_split(str)
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return ctcp
|
||||
|
@ -1,26 +1,22 @@
|
||||
---
|
||||
-- Implementation of the DCC protocol
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local irc = require 'irc'
|
||||
local ctcp = require 'irc.ctcp'
|
||||
local irc = libs.irc
|
||||
local ctcp = libs.ctcp
|
||||
local c = ctcp._ctcp_quote
|
||||
local irc_debug = require 'irc.debug'
|
||||
local misc = require 'irc.misc'
|
||||
local socket = require 'socket'
|
||||
local coroutine = require 'coroutine'
|
||||
local io = require 'io'
|
||||
local string = require 'string'
|
||||
local irc_debug = libs.debug
|
||||
local misc = libs.misc
|
||||
local socket = libs.socket
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- This module implements the DCC protocol. File transfers (DCC SEND) are
|
||||
-- handled, but DCC CHAT is not, as of yet.
|
||||
module 'irc.dcc'
|
||||
local dcc = {}
|
||||
|
||||
-- defaults {{{
|
||||
FIRST_PORT = 1028
|
||||
LAST_PORT = 5000
|
||||
dcc.FIRST_PORT = 1028
|
||||
dcc.LAST_PORT = 5000
|
||||
-- }}}
|
||||
|
||||
-- private functions {{{
|
||||
@ -138,13 +134,13 @@ end
|
||||
-- @param address IP address of the remote user in low level int form
|
||||
-- @param port Port to connect to at the remote user
|
||||
-- @param packet_size Size of the packets the remote user will be sending
|
||||
function _accept(filename, address, port, packet_size)
|
||||
function dcc._accept(filename, address, port, packet_size)
|
||||
debug_dcc("Accepting a DCC SEND request from " .. address .. ":" .. port)
|
||||
packet_size = packet_size or 1024
|
||||
local sock = base.assert(socket.tcp())
|
||||
base.assert(sock:connect(address, port))
|
||||
local sock = assert(socket.tcp())
|
||||
assert(sock:connect(address, port))
|
||||
sock:settimeout(0.1)
|
||||
local file = base.assert(io.open(misc._get_unique_filename(filename), "w"))
|
||||
local file = assert(io.open(misc._get_unique_filename(filename), "w"))
|
||||
irc._register_socket(sock, 'r',
|
||||
coroutine.wrap(function(s)
|
||||
return accept_file(s, file, packet_size)
|
||||
@ -162,17 +158,17 @@ end
|
||||
-- @param port Port to accept connections on (optional, defaults to
|
||||
-- choosing an available port between FIRST_PORT and LAST_PORT
|
||||
-- above)
|
||||
function send(nick, filename, port)
|
||||
port = port or FIRST_PORT
|
||||
function dcc.send(nick, filename, port)
|
||||
port = port or dcc.FIRST_PORT
|
||||
local sock
|
||||
repeat
|
||||
sock = base.assert(socket.tcp())
|
||||
sock = assert(socket.tcp())
|
||||
err, msg = sock:bind('*', port)
|
||||
port = port + 1
|
||||
until msg ~= "address already in use" and port <= LAST_PORT + 1
|
||||
until msg ~= "address already in use" and port <= dcc.LAST_PORT + 1
|
||||
port = port - 1
|
||||
base.assert(err, msg)
|
||||
base.assert(sock:listen(1))
|
||||
assert(err, msg)
|
||||
assert(sock:listen(1))
|
||||
local ip = misc._ip_str_to_int(irc.get_ip())
|
||||
local file, err = io.open(filename)
|
||||
if not file then
|
||||
@ -194,3 +190,5 @@ function send(nick, filename, port)
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return dcc
|
||||
|
@ -1,17 +1,13 @@
|
||||
---
|
||||
-- Basic debug output
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local io = require 'io'
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- This module implements a few useful debug functions for use throughout the
|
||||
-- rest of the code.
|
||||
module 'irc.debug'
|
||||
local irc_debug = {}
|
||||
|
||||
-- defaults {{{
|
||||
COLOR = true
|
||||
irc_debug.COLOR = true
|
||||
-- }}}
|
||||
|
||||
-- local variables {{{
|
||||
@ -27,10 +23,10 @@ local outfile = io.output()
|
||||
-- @param msg Message text
|
||||
-- @param color Which terminal code to use for color output (defaults to
|
||||
-- dark gray)
|
||||
function _message(msg_type, msg, color)
|
||||
function irc_debug._message(msg_type, msg, color)
|
||||
if ON then
|
||||
local endcolor = ""
|
||||
if COLOR and outfile == io.stdout then
|
||||
if irc_debug.COLOR and outfile == io.stdout then
|
||||
color = color or "\027[1;30m"
|
||||
endcolor = "\027[0m"
|
||||
else
|
||||
@ -48,9 +44,9 @@ end
|
||||
-- error().
|
||||
-- @param msg Error message
|
||||
-- @see error
|
||||
function _err(msg)
|
||||
_message("ERR", msg, "\027[0;31m")
|
||||
base.error(msg, 2)
|
||||
function irc_debug._err(msg)
|
||||
irc_debug._message("ERR", msg, "\027[0;31m")
|
||||
error(msg, 2)
|
||||
end
|
||||
-- }}}
|
||||
|
||||
@ -58,8 +54,8 @@ end
|
||||
--
|
||||
-- Signal a warning. Writes the warning message to the screen in yellow.
|
||||
-- @param msg Warning message
|
||||
function _warn(msg)
|
||||
_message("WARN", msg, "\027[0;33m")
|
||||
function irc_debug._warn(msg)
|
||||
irc_debug._message("WARN", msg, "\027[0;33m")
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
@ -68,7 +64,7 @@ end
|
||||
-- enable {{{
|
||||
---
|
||||
-- Turns on debug output.
|
||||
function enable()
|
||||
function irc_debug.enable()
|
||||
ON = true
|
||||
end
|
||||
-- }}}
|
||||
@ -76,7 +72,7 @@ end
|
||||
-- disable {{{
|
||||
---
|
||||
-- Turns off debug output.
|
||||
function disable()
|
||||
function irc_debug.disable()
|
||||
ON = false
|
||||
end
|
||||
-- }}}
|
||||
@ -85,8 +81,10 @@ end
|
||||
---
|
||||
-- Redirects output to a file rather than stdout.
|
||||
-- @param file File to write debug output to
|
||||
function set_output(file)
|
||||
outfile = base.assert(io.open(file))
|
||||
function irc_debug.set_output(file)
|
||||
outfile = assert(io.open(file))
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return irc_debug
|
||||
|
@ -1,19 +1,16 @@
|
||||
---
|
||||
-- Implementation of IRC server message parsing
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local constants = require 'irc.constants'
|
||||
local ctcp = require 'irc.ctcp'
|
||||
local irc_debug = require 'irc.debug'
|
||||
local misc = require 'irc.misc'
|
||||
local socket = require 'socket'
|
||||
local string = require 'string'
|
||||
local table = require 'table'
|
||||
local constants = libs.constants
|
||||
local ctcp = libs.ctcp
|
||||
local irc_debug = libs.debug
|
||||
local misc = libs.misc
|
||||
local socket = libs.socket
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- This module contains parsing functions for IRC server messages.
|
||||
module 'irc.message'
|
||||
local message = {}
|
||||
|
||||
-- internal functions {{{
|
||||
-- _parse {{{
|
||||
@ -32,7 +29,7 @@ module 'irc.message'
|
||||
-- to the received command</li>
|
||||
--
|
||||
-- </ul>
|
||||
function _parse(str)
|
||||
function message._parse(str)
|
||||
-- low-level ctcp quoting {{{
|
||||
str = ctcp._low_dequote(str)
|
||||
-- }}}
|
||||
@ -49,8 +46,8 @@ function _parse(str)
|
||||
local reply = false
|
||||
if command:find("^%d%d%d$") then
|
||||
reply = true
|
||||
if constants.replies[base.tonumber(command)] then
|
||||
command = constants.replies[base.tonumber(command)]
|
||||
if constants.replies[tonumber(command)] then
|
||||
command = constants.replies[tonumber(command)]
|
||||
else
|
||||
irc_debug._warn("Unknown server reply: " .. command)
|
||||
end
|
||||
@ -67,3 +64,5 @@ function _parse(str)
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return message
|
||||
|
@ -1,25 +1,20 @@
|
||||
---
|
||||
-- Various useful functions that didn't fit anywhere else
|
||||
-- initialization {{{
|
||||
local base = _G
|
||||
local irc_debug = require 'irc.debug'
|
||||
local socket = require 'socket'
|
||||
local math = require 'math'
|
||||
local os = require 'os'
|
||||
local string = require 'string'
|
||||
local table = require 'table'
|
||||
local irc_debug = libs.debug
|
||||
local socket = libs.socket
|
||||
-- }}}
|
||||
|
||||
---
|
||||
-- This module contains various useful functions which didn't fit in any of the
|
||||
-- other modules.
|
||||
module 'irc.misc'
|
||||
local misc = {}
|
||||
|
||||
-- defaults {{{
|
||||
DELIM = ' '
|
||||
PATH_SEP = '/'
|
||||
ENDIANNESS = "big"
|
||||
INT_BYTES = 4
|
||||
misc.DELIM = ' '
|
||||
misc.PATH_SEP = '/'
|
||||
misc.ENDIANNESS = "big"
|
||||
misc.INT_BYTES = 4
|
||||
-- }}}
|
||||
|
||||
-- private functions {{{
|
||||
@ -49,9 +44,9 @@ end
|
||||
-- in str will be considered one substring)
|
||||
-- @param rquotes String of characters to use as closing quotes
|
||||
-- @return Array of strings, one for each substring that was separated out
|
||||
function _split(str, delim, end_delim, lquotes, rquotes)
|
||||
function misc._split(str, delim, end_delim, lquotes, rquotes)
|
||||
-- handle arguments {{{
|
||||
delim = "["..(delim or DELIM).."]"
|
||||
delim = "["..(delim or misc.DELIM).."]"
|
||||
if end_delim then end_delim = "["..end_delim.."]" end
|
||||
if lquotes then lquotes = "["..lquotes.."]" end
|
||||
if rquotes then rquotes = "["..rquotes.."]" end
|
||||
@ -111,8 +106,8 @@ end
|
||||
-- @param path Path to the file
|
||||
-- @param sep Directory separator (optional, defaults to PATH_SEP)
|
||||
-- @return The basename of the file
|
||||
function _basename(path, sep)
|
||||
sep = sep or PATH_SEP
|
||||
function misc._basename(path, sep)
|
||||
sep = sep or misc.PATH_SEP
|
||||
if not path:find(sep) then return path end
|
||||
return socket.skip(2, path:find(".*" .. sep .. "(.*)"))
|
||||
end
|
||||
@ -124,8 +119,8 @@ end
|
||||
-- @param path Path to the file
|
||||
-- @param sep Directory separator (optional, defaults to PATH_SEP)
|
||||
-- @return The dirname of the file
|
||||
function _dirname(path, sep)
|
||||
sep = sep or PATH_SEP
|
||||
function misc._dirname(path, sep)
|
||||
sep = sep or misc.PATH_SEP
|
||||
if not path:find(sep) then return "." end
|
||||
return socket.skip(2, path:find("(.*)" .. sep .. ".*"))
|
||||
end
|
||||
@ -139,9 +134,9 @@ end
|
||||
-- @param endian Which endianness to use (big, little, host, network) (defaultsi
|
||||
-- to ENDIANNESS)
|
||||
-- @return A string whose first INT_BYTES characters make a low-level int
|
||||
function _str_to_int(str, bytes, endian)
|
||||
bytes = bytes or INT_BYTES
|
||||
endian = endian or ENDIANNESS
|
||||
function misc._str_to_int(str, bytes, endian)
|
||||
bytes = bytes or misc.INT_BYTES
|
||||
endian = endian or misc.ENDIANNESS
|
||||
local ret = ""
|
||||
for i = 0, bytes - 1 do
|
||||
local new_byte = string.char(math.fmod(str / (2^(8 * i)), 256))
|
||||
@ -159,8 +154,8 @@ end
|
||||
-- @param int String whose bytes correspond to the bytes of a low-level int
|
||||
-- @param endian Endianness of the int argument (defaults to ENDIANNESS)
|
||||
-- @return String representation of the low-level int argument
|
||||
function _int_to_str(int, endian)
|
||||
endian = endian or ENDIANNESS
|
||||
function misc._int_to_str(int, endian)
|
||||
endian = endian or misc.ENDIANNESS
|
||||
local ret = 0
|
||||
for i = 1, int:len() do
|
||||
if endian == "big" or endian == "network" then ind = int:len() - i + 1
|
||||
@ -178,7 +173,7 @@ end
|
||||
-- Converts a string IP address to a low-level int.
|
||||
-- @param ip_str String representation of an IP address
|
||||
-- @return Low-level int representation of that IP address
|
||||
function _ip_str_to_int(ip_str)
|
||||
function misc._ip_str_to_int(ip_str)
|
||||
local i = 3
|
||||
local ret = 0
|
||||
for num in ip_str:gmatch("%d+") do
|
||||
@ -195,7 +190,7 @@ end
|
||||
-- Converts an int to a string IP address.
|
||||
-- @param ip_int Low-level int representation of an IP address
|
||||
-- @return String representation of that IP address
|
||||
function _ip_int_to_str(ip_int)
|
||||
function misc._ip_int_to_str(ip_int)
|
||||
local ip = {}
|
||||
for i = 3, 0, -1 do
|
||||
local new_num = math.floor(ip_int / 2^(i * 8))
|
||||
@ -212,7 +207,7 @@ end
|
||||
-- @param filename Filename to start with
|
||||
-- @return Filename (same as the one we started with, except possibly with some
|
||||
-- numbers appended) which does not currently exist on the filesystem
|
||||
function _get_unique_filename(filename)
|
||||
function misc._get_unique_filename(filename)
|
||||
if not exists(filename) then return filename end
|
||||
|
||||
local count = 1
|
||||
@ -231,8 +226,8 @@ end
|
||||
-- @param fn Function to try to call
|
||||
-- @param ... Arguments to fn
|
||||
-- @return The return values of fn, if it was successfully called
|
||||
function _try_call(fn, ...)
|
||||
if base.type(fn) == "function" then
|
||||
function misc._try_call(fn, ...)
|
||||
if type(fn) == "function" then
|
||||
return fn(...)
|
||||
end
|
||||
end
|
||||
@ -245,8 +240,8 @@ end
|
||||
-- @param fn Function to try to call
|
||||
-- @param ... Arguments to fn
|
||||
-- @return The return values of fn, if it was successfully called
|
||||
function _try_call_warn(msg, fn, ...)
|
||||
if base.type(fn) == "function" then
|
||||
function misc._try_call_warn(msg, fn, ...)
|
||||
if type(fn) == "function" then
|
||||
return fn(...)
|
||||
else
|
||||
irc_debug._warn(msg)
|
||||
@ -257,16 +252,16 @@ end
|
||||
-- _value_iter {{{
|
||||
--
|
||||
-- Iterator to iterate over just the values of a table.
|
||||
function _value_iter(state, arg, pred)
|
||||
for k, v in base.pairs(state) do
|
||||
function misc._value_iter(state, arg, pred)
|
||||
for k, v in pairs(state) do
|
||||
if arg == v then arg = k end
|
||||
end
|
||||
local key, val = base.next(state, arg)
|
||||
local key, val = next(state, arg)
|
||||
if not key then return end
|
||||
|
||||
if base.type(pred) == "function" then
|
||||
if type(pred) == "function" then
|
||||
while not pred(val) do
|
||||
key, val = base.next(state, key)
|
||||
key, val = next(state, key)
|
||||
if not key then return end
|
||||
end
|
||||
end
|
||||
@ -281,7 +276,7 @@ end
|
||||
-- @return nick
|
||||
-- @return username (if it exists)
|
||||
-- @return hostname (if it exists)
|
||||
function _parse_user(user)
|
||||
function misc._parse_user(user)
|
||||
local found, bang, nick = user:find("^([^!]*)!")
|
||||
if found then
|
||||
user = user:sub(bang + 1)
|
||||
@ -301,3 +296,5 @@ function _parse_user(user)
|
||||
end
|
||||
-- }}}
|
||||
-- }}}
|
||||
|
||||
return misc
|
||||
|
@ -225,4 +225,4 @@ local function on_dcc_send()
|
||||
end
|
||||
irc.register_callback("dcc_send", on_dcc_send)
|
||||
|
||||
irc.connect{network = "irc.freenode.net", nick = "doylua"}
|
||||
irc.connect{network = "irc.libera.chat", nick = "doylua"}
|
||||
|
Loading…
x
Reference in New Issue
Block a user