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 = " ", 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 = " | ", 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 = " | ", 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 = " ", 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 })