subrepo:
  subdir:   "mods/irc"
  merged:   "ae0cb08"
upstream:
  origin:   "https://github.com/minetest-mods/irc.git"
  branch:   "master"
  commit:   "ae0cb08"
git-subrepo:
  version:  "0.3.1"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "a7ee886"
master
dicebox 2017-02-10 18:25:03 +01:00
parent de24695a84
commit 7e335f46e3
17 changed files with 1300 additions and 0 deletions

2
mods/irc/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~

3
mods/irc/.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/LuaIRC"]
path = irc
url = https://github.com/ShadowNinja/LuaIRC.git

11
mods/irc/.gitrepo Normal file
View File

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/minetest-mods/irc.git
branch = master
commit = ae0cb08e3e79f3a36fe8148a3fe35ae0cf9bfbf4
parent = de24695a841b8946b8593db8336f4577b364078e
cmdver = 0.3.1

90
mods/irc/API.md Normal file
View File

@ -0,0 +1,90 @@
IRC Mod API
===========
This file documents the Minetest IRC mod API.
Basics
------
In order to allow your mod to interface with this mod, you must add `irc`
to your mod's `depends.txt` file.
Reference
---------
irc:say([name,] message)
Sends <message> to either the channel (if <name> is nil or not specified),
or to the given user (if <name> is specified).
Example:
irc:say("Hello, Channel!")
irc:say("john1234", "How are you?")
irc:register_bot_command(name, cmdDef)
Registers a new bot command named <name>.
When an user sends a private message to the bot with the command name, the
command's function is called.
Here's the format of a command definition (<cmdDef>):
cmdDef = {
params = "<param1> ...", -- A description of the command's parameters
description = "My command", -- A description of what the command does. (one-liner)
func = function(user, args)
-- This function gets called when the command is invoked.
-- <user> is a user table for the user that ran the command.
-- (See the LuaIRC documentation for details.)
-- It contains fields such as 'nick' and 'ident'
-- <args> is a string of arguments to the command (may be "")
-- This function should return boolean success and a message.
end,
};
Example:
irc:register_bot_command("hello", {
params = "",
description = "Greet user",
func = function(user, param)
return true, "Hello!"
end,
});
irc.joined_players[name]
This table holds the players who are currently on the channel (may be less
than the players in the game). It is modified by the /part and /join chat
commands.
Example:
if irc.joined_players["joe"] then
-- Joe is talking on IRC
end
irc:register_hook(name, func)
Registers a function to be called when an event happens. <name> is the name
of the event, and <func> is the function to be called. See HOOKS below
for more information
Example:
irc:register_hook("OnSend", function(line)
print("SEND: "..line)
end)
This mod also supplies some utility functions:
string.expandvars(string, vars)
Expands all occurrences of the pattern "$(varname)" with the value of
'varname' in the <vars> table. Variable names not found on the table
are left verbatim in the string.
Example:
local tpl = "$(foo) $(bar) $(baz)"
local s = tpl:expandvars({foo=1, bar="Hello"})
assert(s == "1 Hello $(baz)")
In addition, all the configuration options decribed in `README.txt` are
available to other mods, though they should be considered read-only. Do
not modify these settings at runtime or you might crash the server!
Hooks
-----
The `irc:register_hook` function can register functions to be called
when some events happen. The events supported are the same as the LuaIRC
ones with a few added (mostly for internal use).
See src/LuaIRC/doc/irc.luadoc for more information.

22
mods/irc/LICENSE.txt Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2013, Diego Martinez (kaeza)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

181
mods/irc/README.md Normal file
View File

@ -0,0 +1,181 @@
IRC Mod for Minetest
====================
Introduction
------------
This mod is just a glue between IRC and Minetest. It provides two-way
communication between the in-game chat, and an arbitrary IRC channel.
The forum topic is [here][forum].
[forum]: https://forum.minetest.net/viewtopic.php?f=11&t=3905
Installing
----------
Quick one line install for Linux:
cd <Mods directory> && git clone --recursive https://github.com/minetest-mods/irc.git
Please change `<Mods directory>` to fit your installation of Minetest.
For more information, see [the wiki][wiki].
The IRC mod's git repository uses submodules, therefore you will have to run
`git submodule init` when first installing the mod (unless you used
`--recursive` as above), and `git submodule update` every time that a submodule
is updated. These steps can be combined into `git submodule update --init`.
You'll need to install LuaSocket. You can do so with your package manager on
many distributions, for example:
# # On Arch Linux:
# pacman -S lua51-socket
# # On Debian/Ubuntu:
# # Debian/Ubuntu's LuaSocket packages are broken, so use LuaRocks.
# apt-get install luarocks
# luarocks install luasocket
You will also need to add IRC to your trusted mods if you haven't disabled mod
security. Here's an example configuration line:
secure.trusted_mods = irc
[wiki]: https://wiki.minetest.net/Installing_mods
Settings
--------
All settings are changed in `minetest.conf`. If any of these settings
are not set, the default value is used.
* `irc.server` (string):
The address of the IRC server to connect to.
* `irc.channel` (string):
The IRC channel to join.
* `irc.interval` (number, default 2.0):
This prevents the server from flooding. It should be at
least 2.0 but can be higher. After four messages this much
time must pass between folowing messages.
* `irc.nick` (string):
Nickname the server uses when it connects to IRC.
* `irc.password` (string, default nil):
Password to use when connecting to the server.
* `irc.NSPass` (string, default nil):
NickServ password. Don't set this if you use SASL authentication.
* `irc.sasl.pass` (string, default nil):
SASL password, same as nickserv password.
You should use this instead of NickServ authentication
if the server supports it.
* `irc.sasl.user` (string, default `irc.nick`):
The SASL username. This should normaly be set to your
NickServ account name.
* `irc.debug` (boolean, default false):
Whether to output debug information.
* `irc.disable_auto_connect` (boolean, default false):
If false, the bot is connected by default. If true, a player with
the 'irc_admin' privilege has to use the `/irc_connect` command to
connect to the server.
* `irc.disable_auto_join` (boolean, default false):
If false, players join the channel automatically upon entering the
game. If true, each user must manually use the `/join` command to
join the channel. In any case, the players may use the `/part`
command to opt-out of being in the channel.
* `irc.send_join_part` (boolean, default true):
Determines whether to send player join and part messages to the channel.
Usage
-----
Once the game is connected to the IRC channel, chatting in-game will send
messages to the channel, and will be visible by anyone. Also, messages sent
to the channel will be visible in-game.
Messages that begin with `[off]` from in-game or IRC are not sent to the
other side.
This mod also adds a few chat commands:
* `/irc_msg <nick> <message>`:
Send a private message to a IRC user.
* `/join`:
Join the IRC chat.
* `/part`:
Part the IRC chat.
* `/irc_connect`:
Connect the bot manually to the IRC network.
* `/irc_disconnect`:
Disconnect the bot manually from the IRC network (this does not
shutdown the game).
* `/irc_reconnect`:
Equivalent to `/irc_disconnect` followed by `/irc_connect`.
You can also send private messages from IRC to in-game players
by sending a private message to the bot (set with the `irc.nick`
option above), in the following format:
@playername message
For example, if there's a player named `mtuser`, you can send him/her
a private message from IRC with:
/msg server_nick @mtuser Hello!
The bot also supports some basic commands, which are invoked by saying
the bot name followed by either a colon or a comma and the command, or
sending a private message to it. For example: `ServerBot: help whereis`.
* `help [<command>]`:
Prints help about a command, or a list of supported commands if no
command is given.
* `uptime`:
Prints the server's running time.
* `whereis <player>`:
Prints the coordinates of the given player.
* `players`:
Lists players currently in the server.
Thanks
------
I'd like to thank the users who supported this mod both on the Minetest
Forums and on the `#minetest` channel. In no particular order:
0gb.us, ShadowNinja, Shaun/kizeren, RAPHAEL, DARGON, Calinou, Exio,
vortexlabs/mrtux, marveidemanis, marktraceur, jmf/john\_minetest,
sdzen/Muadtralk, VanessaE, PilzAdam, sfan5, celeron55, KikaRz,
OldCoder, RealBadAngel, and all the people who commented in the
forum topic. Thanks to you all!
License
-------
See `LICENSE.txt` for details.
The files in the `irc` directory are part of the LuaIRC project.
See `irc/LICENSE.txt` for details.

173
mods/irc/botcmds.lua Normal file
View File

@ -0,0 +1,173 @@
irc.bot_commands = {}
-- From RFC1459:
-- "Because of IRCs scandanavian origin, the characters {}| are
-- considered to be the lower case equivalents of the characters
-- []\, respectively."
local irctolower = { ["["]="{", ["\\"]="|", ["]"]="}" }
local function irclower(s)
return (s:lower():gsub("[%[%]\\]", irctolower))
end
local function nickequals(nick1, nick2)
return irclower(nick1) == irclower(nick2)
end
function irc:check_botcmd(msg)
local prefix = irc.config.command_prefix
local nick = irc.conn.nick
local text = msg.args[2]
local nickpart = text:sub(1, #nick)
local suffix = text:sub(#nick+1, #nick+2)
-- First check for a nick prefix
if nickequals(nickpart, nick)
and (suffix == ": " or suffix == ", ") then
self:bot_command(msg, text:sub(#nick + 3))
return true
-- Then check for the configured prefix
elseif prefix and text:sub(1, #prefix):lower() == prefix:lower() then
self:bot_command(msg, text:sub(#prefix + 1))
return true
end
return false
end
function irc:bot_command(msg, text)
-- Remove leading whitespace
text = text:match("^%s*(.*)")
if text:sub(1, 1) == "@" then
local found, _, player_to, message = text:find("^.([^%s]+)%s(.+)$")
if not minetest.get_player_by_name(player_to) then
irc:reply("User '"..player_to.."' is not in the game.")
return
elseif not irc.joined_players[player_to] then
irc:reply("User '"..player_to.."' is not using IRC.")
return
end
minetest.chat_send_player(player_to,
"PM from "..msg.user.nick.."@IRC: "..message, false)
irc:reply("Message sent!")
return
end
local pos = text:find(" ", 1, true)
local cmd, args
if pos then
cmd = text:sub(1, pos - 1)
args = text:sub(pos + 1)
else
cmd = text
args = ""
end
if not self.bot_commands[cmd] then
self:reply("Unknown command '"..cmd.."'. Try 'list'."
.." Or use @playername <message> to send a private message")
return
end
local success, message = self.bot_commands[cmd].func(msg.user, args)
if message then
self:reply(message)
end
end
function irc:register_bot_command(name, def)
if (not def.func) or (type(def.func) ~= "function") then
error("Erroneous bot command definition. def.func missing.", 2)
elseif name:sub(1, 1) == "@" then
error("Erroneous bot command name. Command name begins with '@'.", 2)
end
self.bot_commands[name] = def
end
irc:register_bot_command("help", {
params = "<command>",
description = "Get help about a command",
func = function(user, args)
if args == "" then
local cmdlist = { }
for name, cmd in pairs(irc.bot_commands) do
cmdlist[#cmdlist+1] = name
end
return true, "Available commands: "..table.concat(cmdlist, ", ")
.." -- Use 'help <command name>' to get"
.." help about a specific command."
end
local cmd = irc.bot_commands[args]
if not cmd then
return false, "Unknown command '"..args.."'."
end
return true, ("Usage: %s%s %s -- %s"):format(
irc.config.command_prefix or "",
args,
cmd.params or "<no parameters>",
cmd.description or "<no description>")
end
})
irc:register_bot_command("list", {
params = "",
description = "List available commands.",
func = function(user, args)
return false, "The `list` command has been merged into `help`."
.." Use `help` with no arguments to get a list."
end
})
irc:register_bot_command("whereis", {
params = "<player>",
description = "Tell the location of <player>",
func = function(user, args)
if args == "" then
return false, "Player name required."
end
local player = minetest.get_player_by_name(args)
if not player then
return false, "There is no player named '"..args.."'"
end
local fmt = "Player %s is at (%.2f,%.2f,%.2f)"
local pos = player:getpos()
return true, fmt:format(args, pos.x, pos.y, pos.z)
end
})
local starttime = os.time()
irc:register_bot_command("uptime", {
description = "Tell how much time the server has been up",
func = function(user, args)
local cur_time = os.time()
local diff = os.difftime(cur_time, starttime)
local fmt = "Server has been running for %d:%02d:%02d"
return true, fmt:format(
math.floor(diff / 60 / 60),
math.floor(diff / 60) % 60,
math.floor(diff) % 60
)
end
})
irc:register_bot_command("players", {
description = "List the players on the server",
func = function(user, args)
local players = minetest.get_connected_players()
local names = {}
for _, player in pairs(players) do
table.insert(names, player:get_player_name())
end
return true, "Connected players: "
..table.concat(names, ", ")
end
})

41
mods/irc/callback.lua Normal file
View File

@ -0,0 +1,41 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
if irc.connected and irc.config.send_join_part then
irc:say("*** "..name.." joined the game")
end
end)
minetest.register_on_leaveplayer(function(player, timed_out)
local name = player:get_player_name()
if irc.connected and irc.config.send_join_part then
irc:say("*** "..name.." left the game"..
(timed_out and " (Timed out)" or ""))
end
end)
minetest.register_on_chat_message(function(name, message)
if not irc.connected
or message:sub(1, 1) == "/"
or message:sub(1, 5) == "[off]"
or not irc.joined_players[name]
or (not minetest.check_player_privs(name, {shout=true})) then
return
end
local nl = message:find("\n", 1, true)
if nl then
message = message:sub(1, nl - 1)
end
irc:say(irc:playerMessage(name, message))
end)
minetest.register_on_shutdown(function()
irc:disconnect("Game shutting down.")
end)

120
mods/irc/chatcmds.lua Normal file
View File

@ -0,0 +1,120 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
-- Note: This file does NOT conatin every chat command, only general ones.
-- Feature-specific commands (like /join) are in their own files.
minetest.register_chatcommand("irc_msg", {
params = "<name> <message>",
description = "Send a private message to an IRC user",
privs = {shout=true},
func = function(name, param)
if not irc.connected then
return false, "Not connected to IRC. Use /irc_connect to connect."
end
local found, _, toname, message = param:find("^([^%s]+)%s(.+)")
if not found then
return false, "Invalid usage, see /help irc_msg."
end
local toname_l = toname:lower()
local validNick = false
local hint = "They have to be in the channel"
for nick, user in pairs(irc.conn.channels[irc.config.channel].users) do
if nick:lower() == toname_l then
validNick = true
break
end
end
if toname_l:find("serv$") or toname_l:find("bot$") then
hint = "it looks like a bot or service"
validNick = false
end
if not validNick then
return false, "You can not message that user. ("..hint..")"
end
irc:say(toname, irc:playerMessage(name, message))
return true, "Message sent!"
end
})
minetest.register_chatcommand("irc_names", {
params = "",
description = "List the users in IRC.",
func = function(name, params)
if not irc.connected then
return false, "Not connected to IRC. Use /irc_connect to connect."
end
local users = { }
for k, v in pairs(irc.conn.channels[irc.config.channel].users) do
table.insert(users, k)
end
return true, "Users in IRC: "..table.concat(users, ", ")
end
})
minetest.register_chatcommand("irc_connect", {
description = "Connect to the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if irc.connected then
return false, "You are already connected to IRC."
end
minetest.chat_send_player(name, "IRC: Connecting...")
irc:connect()
end
})
minetest.register_chatcommand("irc_disconnect", {
params = "[message]",
description = "Disconnect from the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if not irc.connected then
return false, "Not connected to IRC. Use /irc_connect to connect."
end
if params == "" then
params = "Manual disconnect by "..name
end
irc:disconnect(param)
end
})
minetest.register_chatcommand("irc_reconnect", {
description = "Reconnect to the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if not irc.connected then
return false, "Not connected to IRC. Use /irc_connect to connect."
end
minetest.chat_send_player(name, "IRC: Reconnecting...")
irc:disconnect("Reconnecting...")
irc:connect()
end
})
minetest.register_chatcommand("irc_quote", {
params = "<command>",
description = "Send a raw command to the IRC server.",
privs = {irc_admin=true},
func = function(name, param)
if not irc.connected then
return false, "Not connected to IRC. Use /irc_connect to connect."
end
irc:queue(param)
minetest.chat_send_player(name, "Command sent!")
end
})
local oldme = minetest.chatcommands["me"].func
minetest.chatcommands["me"].func = function(name, param, ...)
irc:say(("* %s %s"):format(name, param))
return oldme(name, param, ...)
end

53
mods/irc/config.lua Normal file
View File

@ -0,0 +1,53 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
irc.config = {}
local function setting(stype, name, default, required)
local value
if stype == "bool" then
value = minetest.setting_getbool("irc."..name)
elseif stype == "string" then
value = minetest.setting_get("irc."..name)
elseif stype == "number" then
value = tonumber(minetest.setting_get("irc."..name))
end
if value == nil then
if required then
error("Required configuration option irc."..
name.." missing.")
end
value = default
end
irc.config[name] = value
end
-------------------------
-- BASIC USER SETTINGS --
-------------------------
setting("string", "nick", nil, true) -- Nickname
setting("string", "server", nil, true) -- Server address to connect to
setting("number", "port", 6667) -- Server port to connect to
setting("string", "NSPass") -- NickServ password
setting("string", "sasl.user", irc.config.nick) -- SASL username
setting("string", "sasl.pass") -- SASL password
setting("string", "channel", nil, true) -- Channel to join
setting("string", "key") -- Key for the channel
setting("bool", "send_join_part", true) -- Whether to send player join and part messages to the channel
-----------------------
-- ADVANCED SETTINGS --
-----------------------
setting("string", "password") -- Server password
setting("bool", "secure", false) -- Enable a TLS connection, requires LuaSEC
setting("number", "timeout", 60) -- Underlying socket timeout in seconds.
setting("number", "reconnect", 600) -- Time between reconnection attempts, in seconds.
setting("string", "command_prefix") -- Prefix to use for bot commands
setting("bool", "debug", false) -- Enable debug output
setting("bool", "enable_player_part", true) -- Whether to enable players joining and parting the channel
setting("bool", "auto_join", true) -- Whether to automatically show players in the channel when they join
setting("bool", "auto_connect", true) -- Whether to automatically connect to the server on mod load

257
mods/irc/hooks.lua Normal file
View File

@ -0,0 +1,257 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
local ie = ...
-- MIME is part of LuaSocket
local b64e = ie.require("mime").b64
irc.hooks = {}
irc.registered_hooks = {}
local stripped_chars = "[\2\31]"
local function normalize(text)
-- Strip colors
text = text:gsub("\3[0-9][0-9,]*", "")
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)

187
mods/irc/init.lua Normal file
View File

@ -0,0 +1,187 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
local modpath = minetest.get_modpath(minetest.get_current_modname())
-- Handle mod security if needed
local ie, req_ie = _G, minetest.request_insecure_environment
if req_ie then ie = req_ie() end
if not ie then
error("The IRC mod requires access to insecure functions in order "..
"to work. Please add the irc mod to your secure.trusted_mods "..
"setting or disable the irc mod.")
end
ie.package.path =
-- To find LuaIRC's init.lua
modpath.."/?/init.lua;"
-- For LuaIRC to find its files
..modpath.."/?.lua;"
..ie.package.path
-- The build of Lua that Minetest comes with only looks for libraries under
-- /usr/local/share and /usr/local/lib but LuaSocket is often installed under
-- /usr/share and /usr/lib.
if not rawget(_G, "jit") and package.config:sub(1, 1) == "/" then
ie.package.path = ie.package.path..
";/usr/share/lua/5.1/?.lua"..
";/usr/share/lua/5.1/?/init.lua"
ie.package.cpath = ie.package.cpath..
";/usr/lib/lua/5.1/?.so"
end
-- Temporarily set require so that LuaIRC can access it
local old_require = require
require = ie.require
-- Silence warnings about `module` in `ltn12`.
local old_module = rawget(_G, "module")
rawset(_G, "module", ie.module)
local lib = ie.require("irc")
irc = {
version = "0.2.0",
connected = false,
cur_time = 0,
message_buffer = {},
recent_message_count = 0,
joined_players = {},
modpath = modpath,
lib = lib,
}
-- Compatibility
mt_irc = irc
dofile(modpath.."/config.lua")
dofile(modpath.."/messages.lua")
loadfile(modpath.."/hooks.lua")(ie)
dofile(modpath.."/callback.lua")
dofile(modpath.."/chatcmds.lua")
dofile(modpath.."/botcmds.lua")
-- Restore old (safe) functions
require = old_require
rawset(_G, "module", old_module)
if irc.config.enable_player_part then
dofile(modpath.."/player_part.lua")
else
setmetatable(irc.joined_players, {__index = function(index) return true end})
end
minetest.register_privilege("irc_admin", {
description = "Allow IRC administrative tasks to be performed.",
give_to_singleplayer = true
})
local stepnum = 0
minetest.register_globalstep(function(dtime) return irc:step(dtime) end)
function irc:step(dtime)
if stepnum == 3 then
if self.config.auto_connect then
self:connect()
end
end
stepnum = stepnum + 1
if not self.connected then return end
-- Hooks will manage incoming messages and errors
local good, err = xpcall(function() self.conn:think() end, debug.traceback)
if not good then
print(err)
return
end
end
function irc:connect()
if self.connected then
minetest.log("error", "IRC: Ignoring attempt to connect when already connected.")
return
end
self.conn = irc.lib.new({
nick = self.config.nick,
username = "Minetest",
realname = "Minetest",
})
self:doHook(self.conn)
-- We need to swap the `require` function again since
-- LuaIRC `require`s `ssl` if `irc.secure` is true.
local old_require = require
require = ie.require
local good, message = pcall(function()
self.conn:connect({
host = self.config.server,
port = self.config.port,
password = self.config.password,
timeout = self.config.timeout,
reconnect = self.config.reconnect,
secure = self.config.secure
})
end)
require = old_require
if not good then
minetest.log("error", ("IRC: Connection error: %s: %s -- Reconnecting in %d seconds...")
:format(self.config.server, message, self.config.reconnect))
minetest.after(self.config.reconnect, function() self:connect() end)
return
end
if self.config.NSPass then
self.conn:queue(irc.msgs.privmsg(
"NickServ", "IDENTIFY "..self.config.NSPass))
end
self.conn:join(self.config.channel, self.config.key)
self.connected = true
minetest.log("action", "IRC: Connected!")
minetest.chat_send_all("IRC: Connected!")
end
function irc:disconnect(message)
if self.connected then
--The OnDisconnect hook will clear self.connected and print a disconnect message
self.conn:disconnect(message)
end
end
function irc:say(to, message)
if not message then
message = to
to = self.config.channel
end
to = to or self.config.channel
self:queue(irc.msgs.privmsg(to, message))
end
function irc:reply(message)
if not self.last_from then
return
end
message = message:gsub("[\r\n%z]", " \\n ")
self:say(self.last_from, message)
end
function irc:send(msg)
if not self.connected then return end
self.conn:send(msg)
end
function irc:queue(msg)
if not self.connected then return end
self.conn:queue(msg)
end

1
mods/irc/irc Submodule

@ -0,0 +1 @@
Subproject commit e49a52ede1a58ddd94854657bc5098b0ac5d0677

17
mods/irc/messages.lua Normal file
View File

@ -0,0 +1,17 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
irc.msgs = irc.lib.msgs
function irc:logChat(message)
minetest.log("action", "IRC CHAT: "..message)
end
function irc:sendLocal(message)
minetest.chat_send_all(message)
irc:logChat(message)
end
function irc:playerMessage(name, message)
return ("<%s> %s"):format(name, message)
end

1
mods/irc/mod.conf Normal file
View File

@ -0,0 +1 @@
name = irc

69
mods/irc/player_part.lua Normal file
View File

@ -0,0 +1,69 @@
-- This file is licensed under the terms of the BSD 2-clause license.
-- See LICENSE.txt for details.
function irc:player_part(name)
if not self.joined_players[name] then
return false, "You are not in the channel"
end
self.joined_players[name] = nil
return true, "You left the channel"
end
function irc:player_join(name)
if self.joined_players[name] then
return false, "You are already in the channel"
end
self.joined_players[name] = true
return true, "You joined the channel"
end
minetest.register_chatcommand("join", {
description = "Join the IRC channel",
privs = {shout=true},
func = function(name, param)
return irc:player_join(name)
end
})
minetest.register_chatcommand("part", {
description = "Part the IRC channel",
privs = {shout=true},
func = function(name, param)
return irc:player_part(name)
end
})
minetest.register_chatcommand("who", {
description = "Tell who is currently on the channel",
privs = {},
func = function(name, param)
local out, n = { }, 0
for name in pairs(irc.joined_players) do
n = n + 1
out[n] = name
end
table.sort(out)
return true, "Players in channel: "..table.concat(out, ", ")
end
})
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
irc.joined_players[name] = irc.config.auto_join
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
irc.joined_players[name] = nil
end)
function irc:sendLocal(message)
for name, _ in pairs(self.joined_players) do
minetest.chat_send_player(name, message)
end
irc:logChat(message)
end

72
mods/irc/settingtypes.txt Normal file
View File

@ -0,0 +1,72 @@
[Basic]
# Whether to automatically connect to the server on mod load.
# If false, you must use /irc_connect to connect.
irc.auto_connect (Auto-connect on load) bool true
# Nickname for the bot. May only contain characters A-Z, 0-9
# '{', '}', '[', ']', '|', '^', '-', or '_'.
irc.nick (Bot nickname) string Minetest
# Server to connect to.
irc.server (IRC server) string irc.freenode.net
# Server port.
# The standard IRC protocol port is 6667 for regular servers,
# or 6697 for SSL-enabled servers.
# If unsure, leave at 6667.
irc.port (IRC server port) int 6667 1 65535
# Channel the bot joins after connecting.
irc.channel (Channel to join) string ##mt-irc-mod
[Authentication]
# Password for authenticating to NickServ.
# Leave empty to not authenticate with NickServ.
irc.NSPass (NickServ password) string
# IRC server password.
# Leave empty for no password.
irc.password (Server password) string
# Password for joining the channel.
# Leave empty if your channel is not protected.
irc.key (Channel key) string
# Enable TLS connection.
# Requires LuaSEC <https://github.com/brunoos/luasec>.
irc.secure (Use TLS) bool false
# Username for SASL authentication.
# Leave empty to use the nickname.
irc.sasl.user (SASL username) string
# Password for SASL authentication.
# Leave empty to not authenticate via SASL.
irc.sasl.pass (SASL password) string
[Advanced]
# Enable this to make the bot send messages when players join
# or leave the game server.
irc.send_join_part (Send join and part messages) bool true
# Underlying socket timeout in seconds.
irc.timeout (Timeout) int 60 1
# Time between reconnection attempts, in seconds.
irc.reconnect (Reconnect delay) int 600 1
# Prefix to use for bot commands.
irc.command_prefix (Command prefix) string
# Enable debug output.
irc.debug (Debug mode) bool false
# Whether to enable players joining and parting the channel.
irc.enable_player_part (Allow player join/part) bool true
# Whether to automatically show players in the channel when they join.
irc.auto_join (Auto join players) bool true