adv_chat/main.lua

570 lines
19 KiB
Lua

local const_irc, const_discord = bridges.irc, bridges.discord
local const_bridge = const_irc or const_discord
modlib.log.create_channel("adv_chat") -- Create log channel
local data_dir = minetest.get_worldpath() .. "/data"
minetest.mkdir(data_dir)
to_be_sent = modlib.persistence.lua_log_file.new(data_dir .. "/adv_chat.lua", {})
to_be_sent:init()
modlib.player.set_property_default("adv_chat.roles",{})
modlib.player.set_property_default("adv_chat.blocked",{chatters={}, roles={}})
registered_on_chat_messages = {}
function register_on_chat_message(func)
table.insert(registered_on_chat_messages, func)
end
register_on_chat_message(function(sendername, content, msg)
if not msg.targets then
modlib.log.write("adv_chat", "[MSG] "..sendername..": "..content)
end
end)
function unregister_on_chat_message(func)
for index, func_2 in modlib.table.rpairs(func) do
if func == func_2 then
table.remove(registered_on_chat_messages, index)
end
end
end
function call_registered_on_chat_messages(name, message, msg_info)
for _, func in ipairs(registered_on_chat_messages) do
if func(name, message, msg_info) then
return true
end
end
return false
end
registered_on_joinplayers = {}
function register_on_joinplayer(func)
table.insert(registered_on_joinplayers, func)
end
register_on_joinplayer(function(player)
if get_color(player:get_player_name()) == "#FFFFFF" then
chatters[player:get_player_name()].color = roles.minetest.color
end
end)
function call_registered_on_joinplayers(player)
for _, func in ipairs(registered_on_joinplayers) do
if func(player) then
return true
end
end
return false
end
channels={} --channelname -> definition : {hud_pos, mode, autoremove, max_messages, max_lines, wrap_chars, smartwrap}
roles={} -- Role -> players -> true
if roles_case_insensitive then
modlib.table.set_case_insensitive_index(roles)
end
chatters={} -- Chatter -> stuff
function is_blocked(target, source)
if not chatters[target] then return false end
local blocked=chatters[target].blocked
if not blocked then return false end
if blocked.chatters[source] then
return true
end
for role,_ in pairs(blocked.roles) do
if roles[role].affected[source] then
return true
end
end
end
function send_to_chatter(sendername, chattername, message)
if is_blocked(chattername, sendername) then return end
if chatters[chattername].minetest then
minetest.chat_send_player(chattername, message)
else
if const_discord then
if chatters[chattername].discord then
discord_bridge.write("[PMS]"..get_color(chattername).." "..chattername.." "..message)
end
end
if const_irc then
if chatters[chattername].irc then
irc_bridge.write("[PMS]"..chattername.." "..message)
end
end
end
end
function send_to_targets(msg)
message.mentionpart(msg)
if modlib.table.is_empty(msg.valid_targets) then
return
end
if message.handle_on_chat_messages(msg) then
return msg.handled_by_on_chat_messages
end
local irc_mentioned, discord_mentioned
if const_irc then
irc_mentioned = msg.targets.irc
end
if const_discord then
discord_mentioned = msg.targets.discord
end
for target, _ in pairs(msg.targets) do
if not chatters[target] then
if roles[target] then
modlib.table.add_all(msg.targets, roles[target].affected)
end
msg.targets[target]=nil
end
end
local discord_chatters={}
local irc_chatters={}
for chatter, _ in pairs(msg.targets) do
if not is_blocked(chatter, msg.chatter.name) then
if chatters[chatter].minetest then
minetest.chat_send_player(chatter, message.build(msg, "minetest"))
else
if const_discord then
if chatters[chatter].discord then
table.insert(discord_chatters, chatter:sub(1, chatter:len()-9))
end
end
if const_irc then
if chatters[chatter].irc then
table.insert(irc_chatters, chatter:sub(1, chatter:len()-5))
end
end
end
end
end
if const_discord then
if msg.sent_to ~= "discord" then
if discord_mentioned then
discord_bridge.write("[MSG]"..(msg.chatter.color).." "..message.build(msg, "discord"))
elseif #discord_chatters > 0 then
discord_bridge.write("[PMS]"..(msg.chatter.color).." "..table.concat(discord_chatters, ",").." "..message.build(msg, "discord"))
end
end
end
if const_irc then
if msg.sent_to ~= "irc" then
if irc_mentioned then
irc_bridge.write("[MSG]"..message.build(msg, "irc"))
elseif #irc_chatters > 0 then
irc_bridge.write("[PMS]"..table.concat(irc_chatters, ",").." "..message.build(msg, "irc"))
end
end
end
end
function join(name, def)
if not def.roles then
def.roles={}
end
if not def.name then
def.name=name
end
def.service = ((def.minetest and "minetest") or (def.irc and "irc")) or "discord"
chatters[name]=def
local to_be_received=to_be_sent.root[name]
if to_be_received then
local date=os.date("%Y-%m-%d")
for _, m in ipairs(to_be_received) do
local sender_color=get_color(m.sender)
local datepart=""
if date ~= m.date then
datepart=m.date.." "
end
local message
if def.minetest then
message="["..datepart..m.time.."] "..minetest.get_color_escape_sequence(sender_color)..
m.sender..schemes.minetest.content_prefix..m.message
else
message="["..datepart..m.time.."] "..m.sender..schemes.other.content_prefix..m.message
end
send_to_chatter(m.sender, name, message)
end
end
to_be_sent:set_root(name, nil)
end
function core.send_join_message(name) end
function send_join_message(name)
minetest.chat_send_all(mt_color(name)..name..minetest.get_color_escape_sequence("#FFFFFF").." joined.")
end
function rename(chattername, new_chattername)
chatters[new_chattername]=chatters[chattername]
transfer_roles(chattername, new_chattername)
chatters[chattername]=nil
minetest.chat_send_all(mt_color(new_chattername)..chattername..minetest.get_color_escape_sequence("#FFFFFF").." is now known as "..mt_color(new_chattername)..new_chattername)
end
function leave(name)
remove_roles(name)
chatters[name]=nil
end
function core.send_leave_message(name, timeout) end
function send_leave_message(name, timed_out)
local message = mt_color(name)..name..minetest.get_color_escape_sequence("#FFFFFF").." left"
if timed_out then
message = message .. " (timed out)"
end
minetest.chat_send_all(message .. ".")
end
minetest.register_on_joinplayer(function(player)
join(player:get_player_name(), {color=modlib.player.get_color(player), roles={}, blocked={chatters={}, roles={}}, minetest=true})
add_role(player:get_player_name(), "minetest")
if not call_registered_on_joinplayers(player) then
send_join_message(player:get_player_name())
end
end)
minetest.register_on_leaveplayer(function(player, timed_out)
send_leave_message(player:get_player_name(), timed_out)
leave(player:get_player_name())
end)
function register_role(rolename, roledef)
roles[rolename]={title=roledef.title, color=roledef.color or "#FFFFFF",affected={}}
modlib.player.register_forbidden_name(rolename)
end
if const_bridge then
minetest.original_chat_send_all=minetest.chat_send_all
minetest.chat_send_all=function(msg)
local adv_message=message.new(nil, nil, msg)
adv_message.internal=true
send_to_all(adv_message)
end
minetest.original_chat_send_player=minetest.chat_send_player
minetest.chat_send_player=function(name, msg)
local chatter=chatters[name]
if not chatter then
return
end
if chatter.minetest then
return minetest.original_chat_send_player(name, msg)
end
local adv_message=message.new(nil, nil, msg)
adv_message.internal=true
local to_be_sent=message.build(adv_message, chatter.service)
if const_irc then
if chatter.irc then
irc_bridge.write("[PMS]"..chatter.name.." "..to_be_sent)
end
end
if const_discord then
if chatter.discord then
discord_bridge.write("[PMS]#FFFFFF "..chatter.name.." "..to_be_sent)
end
end
end
end
register_role("minetest",{color="#66FF66"})
function unregister_role(rolename)
roles[rolename]=nil
modlib.player.unregister_forbidden_name(rolename)
end
function add_role(player, role, value)
if not roles[role] or not chatters[player] then return false end
if not roles[role].affected[player] then
roles[role].affected[player]=value or true
end
if not chatters[player].roles[role] then
chatters[player].roles[role]=value or true
end
return true
end
function remove_role(player, role, expected_value)
if expected_value and roles[role].affected[player] == expected_value then
roles[role].affected[player]=nil
chatters[player].roles[role]=nil
end
end
function remove_roles(chatter)
for role, _ in pairs(chatters[chatter].roles) do
roles[role].affected[chatter] = nil
chatters[chatter].roles[role] = nil
end
end
function transfer_roles(chatter, new_chatter)
for role, _ in pairs(chatters[chatter].roles) do
roles[role].affected[new_chatter] = roles[role].affected[chatter]
roles[role].affected[chatter] = nil
end
end
function get_color(chatter)
if chatters[chatter] then
return chatters[chatter].color or "#FFFFFF"
end
return "#FFFFFF"
end
function mt_color(chattername)
return minetest.get_color_escape_sequence(get_color(chattername))
end
function send_to_all(msg)
if message.handle_on_chat_messages(msg) then
return msg.handled_by_on_chat_messages
end
if const_irc then
if msg.sent_to ~= "irc" then
irc_bridge.write("[MSG]"..message.build(msg, "irc"))
end
end
if const_discord then
if msg.sent_to ~= "discord" then
discord_bridge.write("[MSG]"..((msg.chatter and msg.chatter.color) or "#FFFFFF").." "..message.build(msg, "discord"))
end
end
if msg.sent_to ~= "minetest" then
local mt_msg
for _,player in pairs(minetest.get_connected_players()) do
local playername=player:get_player_name()
if not msg.chatter or not is_blocked(playername, msg.chatter) then
mt_msg=mt_msg or message.build(msg, "minetest")
minetest.chat_send_player(playername, mt_msg)
end
end
end
end
function send_to_players(msg, players, origin)
for playername,_ in pairs(players) do
local blocked=modlib.player.get_property(playername, "chatroles.blocked")
if not blocked.players[origin] then
local send = true
for role,_ in ipairs(blocked.roles) do
if roles[role].affected[origin] then
send = false
break
end
end
if send then
minetest.chat_send_player(playername, msg)
end
end
end
end
function get_affected_by_mentions(mentions)
local affected={}
for _, mention in pairs(mentions) do
if roles[mention] then
modlib.table.add_all(affected, roles[mention].affected)
elseif minetest.get_player_by_name(mention) then
affected[mention]=true
end
end
return affected
end
function parse_message(message)
return colorize_message(parse_unicode(message))
end
function on_chat_message(sender, msg)
local mentions={}
local msg_content=msg
if msg:sub(1,1)=="@" then
local delim_space=false
local last_non_delim_char=false
for i=1,msg:len() do
local c=msg:sub(i,i)
if c == "," then
last_non_delim_char=false
elseif c ~=" " then
if last_non_delim_char and i-last_non_delim_char > 1 then
delim_space=i-1
break
end
last_non_delim_char=i
end
end
if not delim_space then
minetest.chat_send_player(sender, "No message given. Use '@mentions message'.")
return true
end
msg_content=msg:sub(delim_space+1)
local msg_header=msg:sub(2, delim_space-1)
local parts=modlib.text.split_without_limit(msg_header,",")
for _, part in pairs(parts) do
table.insert(mentions, modlib.text.trim(part, " "))
end
local adv_msg=message.new(chatters[sender], mentions, msg_content)
message.mentionpart(adv_msg)
table.insert(mentions, sender)
send_to_targets(adv_msg)
if #adv_msg.invalid_mentions == 1 then
minetest.chat_send_player(sender, "The target "..adv_msg.invalid_mentions[1].." is inexistant.")
elseif #adv_msg.invalid_mentions > 1 then
minetest.chat_send_player(sender, "The targets "..table.concat(adv_msg.invalid_mentions, ", ").." are inexistant.")
end
else
local sender_color=get_color(sender)
players={}
for _,player in pairs(minetest.get_connected_players()) do
players[player:get_player_name()]=true
end
local adv_msg=message.new(chatters[sender], mentions, msg_content)
send_to_all(adv_msg)
end
return true
end
minetest.register_on_chat_message(on_chat_message)
local prefix = (cmdlib and "chat ") or "chat_"
minetest.register_chatcommand(prefix.."msg",{
params = "<name> <message>",
description = "Send a message to a chatter as soon as they join",
privs={},
func = function(sendername, param)
local delim=param:find(" ")
if not delim or delim==string.len(param) then
return false, "No message specified"
else
local playername=param:sub(1,delim-1)
if minetest.player_exists(playername) or modlib.text.ends_with(playername, "[irc]") or modlib.text.ends_with(playername, "[discord]") then
local message=colorize_message(param:sub(delim+1))
if not to_be_sent.root[playername] then
to_be_sent:set_root(playername, {})
end
local list = to_be_sent.root[playername]
to_be_sent:set(list, #list+1, {sender=sendername, message=message, date=os.date("%Y-%m-%d"), time=os.date("%H:%M:%S")})
to_be_sent:flush()
return true, "Your message '"..message..minetest.get_color_escape_sequence("#FFFFFF").."' will be sent to chatter '"..playername.."' as soon as they join."
else
return false, "No chatter called '"..playername.."'"
end
end
end
})
local formspec=[[size[9,0.5,false]
field[0.2,0.2;7,1;text;;]
button_exit[7,0;2,0.75;send;Send]
no_prepend[]
]]
minetest.register_chatcommand(prefix.."say", {
params="",
description="Send chat message using entry field.",
privs={discord_user=false, irc_user=false},
func=function(sendername)
minetest.show_formspec(sendername, "chatroles:chatbox", formspec)
end
})
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname=="chatroles:chatbox" and fields.text then
on_chat_message(player:get_player_name(), fields.text)
end
end)
minetest.register_chatcommand(prefix.."block", {
params = "<name> | <role>",
description = "Block messages from chatter or role",
privs={},
func = function(sendername, param)
param=modlib.text.trim(param)
if param:len() == 0 or (not chatters[param] and not roles[param]) then
return false, "No valid chatter name or role given."
end
if not chatters[sendername] then
return false, "No valid sender name."
end
local blocked=chatters[sendername].blocked.chatters
if roles[param] then
blocked=chatters[sendername].blocked.roles
end
if blocked[param] then
return false, type..param.." is already blocked"
end
blocked[param]=true
return true, type..param.." was blocked"
end
})
minetest.register_chatcommand(prefix.."unblock", {
params = "<name> | <role>",
description = "Unblock messages from chatter or role",
privs={},
func = function(sendername, param)
param=modlib.text.trim(param)
if param:len() == 0 or (not chatters[param] and not roles[param]) then
return false, "No valid chatter name or role given."
end
if not chatters[sendername] then
return false, "No valid sender name."
end
local blocked=chatters[sendername].blocked.chatters
if roles[param] then
blocked=chatters[sendername].blocked.roles
end
if not blocked[param] then
return false, type..param.." is not blocked"
end
blocked[param]=nil
return true, type..param.." was unblocked"
end
})
minetest.register_chatcommand(prefix.."login", {
params = "<name> <password>",
description = "Log in as (fake) player to execute chatcommands as them",
privs = {chatter=true},
func = function(sendername, param)
param=modlib.text.trim(param)
if param:len() == 0 then
return false, "No arguments given - missing name and password."
end
local name, password = unpack(modlib.text.split(param, " ", 2))
password = password or ""
local auth = minetest.get_auth_handler().get_auth(name)
if auth and minetest.check_password_entry(name, auth.password, password) then
chatters[sendername].login = name
return true, 'Logged in as "'..name..'"'
end
return false, "Wrong playername/password. : "..name..", "..password.."!="..auth.password
end
})
minetest.register_chatcommand(prefix.."logout", {
params = "",
description = "Log out from your (fake) player account",
privs = {chatter=true},
func = function(sendername, param)
if not chatters[sendername].login then
return false, "Not logged in."
end
local login = chatters[sendername].login
chatters[sendername].login = nil
return true, 'Logged out from "'..login..'"'
end
})