Port to Lua 5.3

master
Elias Fleckenstein 2021-11-21 19:16:03 +01:00
parent 09628cded4
commit 783fefdcb3
No known key found for this signature in database
GPG Key ID: 06927A5199D6C9B2
11 changed files with 270 additions and 258 deletions

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"}