273 lines
6.5 KiB
Lua
273 lines
6.5 KiB
Lua
--[[
|
|
This file is part of Ice Lua Components.
|
|
|
|
Ice Lua Components is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Ice Lua Components is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with Ice Lua Components. If not, see <http://www.gnu.org/licenses/>.
|
|
]]
|
|
|
|
--[[
|
|
IMPORTANT NOTE FROM RFC 1459:
|
|
|
|
IRC messages are always lines of characters terminated with a CR-LF
|
|
(Carriage Return - Line Feed) pair, and these messages shall not
|
|
exceed 512 characters in length, counting all characters including
|
|
the trailing CR-LF. Thus, there are 510 characters maximum allowed
|
|
for the command and its parameters. There is no provision for
|
|
continuation message lines.
|
|
]]
|
|
|
|
irc = {}
|
|
irc.conns = {}
|
|
|
|
function ircbot_conn_new(settings)
|
|
local this = {}
|
|
|
|
this.addr = settings.addr or error("IRC bot address not set!")
|
|
this.port = settings.port or 6667
|
|
this.nick = settings.nick or "iceball"
|
|
this.user = settings.user or "iceball"
|
|
this.rname = settings.rname or "Iceball Server Bot"
|
|
this.channel = settings.channel or error("IRC bot channel not set!")
|
|
this.chat_prefix = settings.chat_prefix or "."
|
|
this.cmd_prefix = settings.cmd_prefix or "!"
|
|
this.chat_color = settings.chat_color or 0xFFFFAAFF
|
|
|
|
this.working = false
|
|
this.sockfd = nil
|
|
this.recvq = ""
|
|
this.sendq = ""
|
|
this.can_write = false
|
|
this.in_server = false
|
|
this.in_channel = false
|
|
|
|
function this.connect()
|
|
this.sockfd = common.tcp_connect(this.addr, this.port)
|
|
if this.sockfd == nil then
|
|
this.working = false
|
|
return false
|
|
end
|
|
|
|
this.working = true
|
|
this.sendq = this.sendq .. (
|
|
"USER"..
|
|
" "..this.user..
|
|
" ".."*"..
|
|
" ".."*"..
|
|
" :"..this.rname..
|
|
"\r\n")
|
|
this.sendq = this.sendq .. (
|
|
"NICK"..
|
|
" "..this.nick..
|
|
"\r\n")
|
|
|
|
return true
|
|
end
|
|
|
|
function this.write(msg)
|
|
if not this.working then return false end
|
|
if not this.can_write then return false end
|
|
|
|
-- TODO: SANITISE!
|
|
this.sendq = this.sendq .. (
|
|
"PRIVMSG"..
|
|
" "..this.channel..
|
|
" :"..msg..
|
|
"\r\n")
|
|
end
|
|
|
|
function this.handle_msg(sec_current, sec_delta, m)
|
|
if m.cmd == "PING" then
|
|
this.sendq = this.sendq .. (
|
|
"PONG"..
|
|
" :"..m.params[1]..
|
|
"\r\n")
|
|
|
|
elseif not this.in_server then
|
|
if m.cmd == "001" then
|
|
print("IRC bot entered server")
|
|
this.in_server = true
|
|
this.sendq = this.sendq .. (
|
|
"JOIN"..
|
|
" "..this.channel..
|
|
"\r\n")
|
|
|
|
end
|
|
elseif not this.in_channel then
|
|
if m.cmd == "JOIN" then
|
|
if m.params[1]:lower() == this.channel:lower() then
|
|
print("IRC bot entered channel")
|
|
this.in_channel = true
|
|
this.can_write = true
|
|
--this.write("channel join test")
|
|
else
|
|
print("IRC bot was force joined to a different channel")
|
|
this.sendq = this.sendq .. (
|
|
"PART"..
|
|
" "..m.params[1]..
|
|
" :".."not assigned to this channel"..
|
|
"\r\n")
|
|
end
|
|
|
|
end
|
|
elseif m.cmd == "353" then
|
|
-- RPL_NAMREPLY
|
|
-- TODO: parse oper status and whatnot
|
|
-- TODO: track nicks
|
|
elseif m.cmd == "PRIVMSG" then
|
|
print("privmsg: ("..m.params[1]..") <"..m.nick.."> "..m.params[2])
|
|
if m.params[1]:lower() == this.channel:lower() then
|
|
local msg = m.params[2]
|
|
if msg:sub(1,this.chat_prefix:len()) == this.chat_prefix then
|
|
local rmsg = msg:sub(this.chat_prefix:len()+1)
|
|
net_broadcast(nil, common.net_pack("BIz", PKT_CHAT_ADD_TEXT,
|
|
this.chat_color, "<"..m.nick.."> "..rmsg))
|
|
elseif msg:sub(1,this.cmd_prefix:len()) == this.cmd_prefix then
|
|
local msg = m.params[2]
|
|
local params = string.split(string.sub(msg,2), " ")
|
|
command_handle(nil, nil, m.nick, params, msg)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function this.update(sec_current, sec_delta, ...)
|
|
-- Cut off if connection broken
|
|
if not this.working then return false end
|
|
|
|
-- Send what we can
|
|
if this.sendq ~= "" then
|
|
local s = common.tcp_send(this.sockfd, this.sendq)
|
|
if s == false then
|
|
print("CANNOT SEND, ERROR OCCURRED")
|
|
this.working = false
|
|
common.tcp_close(this.sockfd)
|
|
this.sockfd = nil
|
|
return false
|
|
end
|
|
this.sendq = s
|
|
end
|
|
|
|
-- Receive what we can
|
|
local r = common.tcp_recv(this.sockfd)
|
|
if r == false then
|
|
print("CANNOT RECV, ERROR OCCURRED")
|
|
this.working = false
|
|
common.tcp_close(this.sockfd)
|
|
this.sockfd = nil
|
|
return false
|
|
end
|
|
this.recvq = this.recvq .. r
|
|
|
|
-- Parse recvq
|
|
while true do
|
|
local idx = this.recvq:find("\r\n")
|
|
if idx == nil then break end
|
|
local s = this.recvq:sub(1, idx-1)
|
|
this.recvq = this.recvq:sub(idx+2)
|
|
|
|
-- Hostmask
|
|
local mmask = nil
|
|
local mnick = nil
|
|
local muser = nil
|
|
local mhost = nil
|
|
if s:sub(1,1) == ":" then
|
|
-- Separate hostmask from message
|
|
idx = s:find(" ")
|
|
if not idx then
|
|
-- Shouldn't happen, but keep this robust anyway
|
|
mmask = s:sub(2)
|
|
s = ""
|
|
else
|
|
mmask = s:sub(2, idx-1)
|
|
s = s:sub(idx+1)
|
|
end
|
|
|
|
-- Split hostmask
|
|
local ts = mmask
|
|
idx = ts:find("@")
|
|
if idx then
|
|
mhost = ts:sub(idx+1)
|
|
ts = ts:sub(1, idx-1)
|
|
end
|
|
idx = ts:find("!")
|
|
if idx then
|
|
muser = ts:sub(idx+1)
|
|
ts = ts:sub(1, idx-1)
|
|
end
|
|
mnick = ts
|
|
end
|
|
|
|
-- Command
|
|
idx = s:find(" ")
|
|
local cmd = (idx and s:sub(1, idx-1)) or s
|
|
s = (idx and s:sub(idx+1))
|
|
|
|
local params = {}
|
|
|
|
while s do
|
|
if s:sub(1,1) == ":" then
|
|
s = s:sub(2)
|
|
params[1+#params] = s
|
|
s = nil
|
|
else
|
|
idx = s:find(" ")
|
|
params[1+#params] = (idx and s:sub(1, idx-1)) or s
|
|
s = (idx and s:sub(idx+1))
|
|
end
|
|
end
|
|
|
|
s = ""
|
|
local i
|
|
for i = 1,#params do
|
|
s = s .. " {" .. params[i] .. "}"
|
|
end
|
|
|
|
print("IRC:", mmask, mnick, muser, mhost, cmd, s)
|
|
|
|
this.handle_msg(sec_current, sec_delta, {
|
|
mask = mmask,
|
|
nick = mnick,
|
|
user = muser,
|
|
host = mhost,
|
|
cmd = cmd,
|
|
params = params,
|
|
})
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
if this.connect() then
|
|
irc.conns[1+#(irc.conns)] = this
|
|
end
|
|
end
|
|
|
|
function irc.connect(settings)
|
|
ircbot_conn_new(settings)
|
|
end
|
|
|
|
function irc.update(sec_current, sec_delta, ...)
|
|
local k,v
|
|
for k,v in pairs(irc.conns) do
|
|
v.update(sec_current, sec_delta, ...)
|
|
end
|
|
end
|
|
|
|
function irc.write(msg)
|
|
local k,v
|
|
for k,v in pairs(irc.conns) do
|
|
v.write(msg)
|
|
end
|
|
end
|
|
|