mirror of
https://github.com/sys4-fr/server-nalc.git
synced 2024-12-27 03:00:38 +01:00
275 lines
7.6 KiB
Lua
Executable File
275 lines
7.6 KiB
Lua
Executable File
-- This file is licensed under the terms of the BSD 2-clause license.
|
||
-- See LICENSE.txt for details.
|
||
|
||
-- MIME is part of LuaSocket
|
||
local b64e = require("mime").b64
|
||
|
||
irc.hooks = {}
|
||
irc.registered_hooks = {}
|
||
|
||
local accent_chars = { {"À", "A"},{"Á", "A"},{"Â", "A"},{"Ã", "A"},{"Ä", "A"},{"Å", "A"},{"Ç", "C"},{"È", "E"},{"É", "E"},{"Ê", "E"},
|
||
{"Ë", "E"},{"<EFBFBD>", "I"},{"Í", "I"},{"<EFBFBD>", "I"},{"Ï", "I"},{"Ñ", "N"},{"Ò", "O"},{"Ó", "O"},{"Ô", "O"},{"Õ", "O"},
|
||
{"Ö", "O"},{"<EFBFBD>", "O"},{"Ù", "U"},{"Ú", "U"},{"Û", "U"},{"Ü", "U"},{"<EFBFBD>", "Y"},{"à", "a"},{"á", "a"},{"â", "a"},
|
||
{"ã", "a"},{"ä", "a"},{"å", "a"},{"ç", "c"},{"è", "e"},{"é", "e"},{"ê", "e"},{"ë", "e"},{"ì", "i"},{"í", "i"},
|
||
{"î", "i"},{"ï", "i"},{"ñ", "n"},{"ò", "o"},{"ó", "o"},{"ô", "o"},{"õ", "o"},{"ö", "o"},{"<EFBFBD>", "o"},{"ù", "u"},
|
||
{"ú", "u"},{"û", "u"},{"ü", "u"},{"<EFBFBD>", "Y"}
|
||
}
|
||
|
||
local function subaccent(text)
|
||
for _, c in pairs(accent_chars) do
|
||
text = text:gsub(c[1], c[2])
|
||
end
|
||
return text
|
||
end
|
||
|
||
-- TODO: Add proper conversion from CP1252 to UTF-8.
|
||
local stripped_chars = {"\2", "\31"}
|
||
for c = 127, 255 do
|
||
table.insert(stripped_chars, string.char(c))
|
||
end
|
||
stripped_chars = "["..table.concat(stripped_chars, "").."]"
|
||
|
||
local function normalize(text)
|
||
-- Strip colors
|
||
text = text:gsub("\3[0-9][0-9,]*", "")
|
||
text = subaccent(text)
|
||
return text:gsub(stripped_chars, "")
|
||
end
|
||
|
||
|
||
function irc:doHook(conn)
|
||
for name, hook in pairs(self.registered_hooks) do
|
||
for _, func in pairs(hook) do
|
||
conn:hook(name, func)
|
||
end
|
||
end
|
||
end
|
||
|
||
|
||
function irc:register_hook(name, func)
|
||
self.registered_hooks[name] = self.registered_hooks[name] or {}
|
||
table.insert(self.registered_hooks[name], func)
|
||
end
|
||
|
||
|
||
function irc.hooks.raw(line)
|
||
if irc.config.debug then
|
||
print("RECV: "..line)
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.send(line)
|
||
if irc.config.debug then
|
||
print("SEND: "..line)
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.chat(msg)
|
||
local channel, text = msg.args[1], msg.args[2]
|
||
if text:sub(1, 1) == string.char(1) then
|
||
irc.conn:invoke("OnCTCP", msg)
|
||
return
|
||
end
|
||
|
||
if channel == irc.conn.nick then
|
||
irc.last_from = msg.user.nick
|
||
irc.conn:invoke("PrivateMessage", msg)
|
||
else
|
||
irc.last_from = channel
|
||
irc.conn:invoke("OnChannelChat", msg)
|
||
end
|
||
end
|
||
|
||
|
||
local function get_core_version()
|
||
local status = minetest.get_server_status()
|
||
local start_pos = select(2, status:find("version=", 1, true))
|
||
local end_pos = status:find(",", start_pos, true)
|
||
return status:sub(start_pos + 1, end_pos - 1)
|
||
end
|
||
|
||
|
||
function irc.hooks.ctcp(msg)
|
||
local text = msg.args[2]:sub(2, -2) -- Remove ^C
|
||
local args = text:split(' ')
|
||
local command = args[1]:upper()
|
||
|
||
local function reply(s)
|
||
irc:queue(irc.msgs.notice(msg.user.nick,
|
||
("\1%s %s\1"):format(command, s)))
|
||
end
|
||
|
||
if command == "ACTION" and msg.args[1] == irc.config.channel then
|
||
local action = text:sub(8, -1)
|
||
irc:sendLocal(("* %s@IRC %s"):format(msg.user.nick, action))
|
||
elseif command == "VERSION" then
|
||
reply(("Minetest version %s, IRC mod version %s.")
|
||
:format(get_core_version(), irc.version))
|
||
elseif command == "PING" then
|
||
reply(args[2])
|
||
elseif command == "TIME" then
|
||
reply(os.date())
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.channelChat(msg)
|
||
local text = normalize(msg.args[2])
|
||
|
||
irc:check_botcmd(msg)
|
||
|
||
-- Don't let a user impersonate someone else by using the nick "IRC"
|
||
if msg.user.nick == "IRC" then
|
||
irc:sendLocal("<IRC@IRC> "..text)
|
||
return
|
||
end
|
||
|
||
-- Support multiple servers in a channel better by converting:
|
||
-- "<server@IRC> <player> message" into "<player@server> message"
|
||
-- "<server@IRC> *** player joined/left the game" into "*** player joined/left server"
|
||
-- and "<server@IRC> * player orders a pizza" into "* player@server orders a pizza"
|
||
local foundchat, _, chatnick, chatmessage =
|
||
text:find("^<([^>]+)> (.*)$")
|
||
local foundjoin, _, joinnick =
|
||
text:find("^%*%*%* ([^%s]+) joined the game$")
|
||
local foundleave, _, leavenick =
|
||
text:find("^%*%*%* ([^%s]+) left the game$")
|
||
local foundaction, _, actionnick, actionmessage =
|
||
text:find("^%* ([^%s]+) (.*)$")
|
||
|
||
if text:sub(1, 5) == "[off]" then
|
||
return
|
||
elseif foundchat then
|
||
irc:sendLocal(("<%s@%s> %s")
|
||
:format(chatnick, msg.user.nick, chatmessage))
|
||
elseif foundjoin then
|
||
irc:sendLocal(("*** %s joined %s")
|
||
:format(joinnick, msg.user.nick))
|
||
elseif foundleave then
|
||
irc:sendLocal(("*** %s left %s")
|
||
:format(leavenick, msg.user.nick))
|
||
elseif foundaction then
|
||
irc:sendLocal(("* %s@%s %s")
|
||
:format(actionnick, msg.user.nick, actionmessage))
|
||
else
|
||
irc:sendLocal(("<%s@IRC> %s"):format(msg.user.nick, text))
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.pm(msg)
|
||
-- Trim prefix if it is found
|
||
local text = msg.args[2]
|
||
local prefix = irc.config.command_prefix
|
||
if prefix and text:sub(1, #prefix) == prefix then
|
||
text = text:sub(#prefix + 1)
|
||
end
|
||
irc:bot_command(msg, text)
|
||
end
|
||
|
||
|
||
function irc.hooks.kick(channel, target, prefix, reason)
|
||
if target == irc.conn.nick then
|
||
minetest.chat_send_all("IRC: kicked from "..channel.." by "..prefix.nick..".")
|
||
irc:disconnect("Kicked")
|
||
else
|
||
irc:sendLocal(("-!- %s was kicked from %s by %s [%s]")
|
||
:format(target, channel, prefix.nick, reason))
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.notice(user, target, message)
|
||
if user.nick and target == irc.config.channel then
|
||
irc:sendLocal("-"..user.nick.."@IRC- "..message)
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.mode(user, target, modes, ...)
|
||
local by = ""
|
||
if user.nick then
|
||
by = " by "..user.nick
|
||
end
|
||
local options = ""
|
||
if select("#", ...) > 0 then
|
||
options = " "
|
||
end
|
||
options = options .. table.concat({...}, " ")
|
||
minetest.chat_send_all(("-!- mode/%s [%s%s]%s")
|
||
:format(target, modes, options, by))
|
||
end
|
||
|
||
|
||
function irc.hooks.nick(user, newNick)
|
||
irc:sendLocal(("-!- %s is now known as %s")
|
||
:format(user.nick, newNick))
|
||
end
|
||
|
||
|
||
function irc.hooks.join(user, channel)
|
||
irc:sendLocal(("-!- %s joined %s")
|
||
:format(user.nick, channel))
|
||
end
|
||
|
||
|
||
function irc.hooks.part(user, channel, reason)
|
||
reason = reason or ""
|
||
irc:sendLocal(("-!- %s has left %s [%s]")
|
||
:format(user.nick, channel, reason))
|
||
end
|
||
|
||
|
||
function irc.hooks.quit(user, reason)
|
||
irc:sendLocal(("-!- %s has quit [%s]")
|
||
:format(user.nick, reason))
|
||
end
|
||
|
||
|
||
function irc.hooks.disconnect(message, isError)
|
||
irc.connected = false
|
||
if isError then
|
||
minetest.log("error", "IRC: Error: Disconnected, reconnecting in one minute.")
|
||
minetest.chat_send_all("IRC: Error: Disconnected, reconnecting in one minute.")
|
||
minetest.after(60, irc.connect, irc)
|
||
else
|
||
minetest.log("action", "IRC: Disconnected.")
|
||
minetest.chat_send_all("IRC: Disconnected.")
|
||
end
|
||
end
|
||
|
||
|
||
function irc.hooks.preregister(conn)
|
||
if not (irc.config["sasl.user"] and irc.config["sasl.pass"]) then return end
|
||
local authString = b64e(
|
||
("%s\x00%s\x00%s"):format(
|
||
irc.config["sasl.user"],
|
||
irc.config["sasl.user"],
|
||
irc.config["sasl.pass"])
|
||
)
|
||
conn:send("CAP REQ sasl")
|
||
conn:send("AUTHENTICATE PLAIN")
|
||
conn:send("AUTHENTICATE "..authString)
|
||
conn:send("CAP END")
|
||
end
|
||
|
||
|
||
irc:register_hook("PreRegister", irc.hooks.preregister)
|
||
irc:register_hook("OnRaw", irc.hooks.raw)
|
||
irc:register_hook("OnSend", irc.hooks.send)
|
||
irc:register_hook("DoPrivmsg", irc.hooks.chat)
|
||
irc:register_hook("OnPart", irc.hooks.part)
|
||
irc:register_hook("OnKick", irc.hooks.kick)
|
||
irc:register_hook("OnJoin", irc.hooks.join)
|
||
irc:register_hook("OnQuit", irc.hooks.quit)
|
||
irc:register_hook("NickChange", irc.hooks.nick)
|
||
irc:register_hook("OnCTCP", irc.hooks.ctcp)
|
||
irc:register_hook("PrivateMessage", irc.hooks.pm)
|
||
irc:register_hook("OnNotice", irc.hooks.notice)
|
||
irc:register_hook("OnChannelChat", irc.hooks.channelChat)
|
||
irc:register_hook("OnModeChange", irc.hooks.mode)
|
||
irc:register_hook("OnDisconnect", irc.hooks.disconnect)
|
||
|