363 lines
12 KiB
Lua
363 lines
12 KiB
Lua
|
--
|
||
|
-- Webchat-Mod
|
||
|
-- © 2022-03-12 secklsurvival@gerloni.net
|
||
|
-- GPL-3.0-or-later
|
||
|
--
|
||
|
|
||
|
-- To use the functions outside this Mod
|
||
|
webchat={}
|
||
|
|
||
|
-- Mod dir
|
||
|
local webchatdir=minetest.get_worldpath().."/"..minetest.get_current_modname()
|
||
|
|
||
|
-- Player data
|
||
|
local pldatadir=webchatdir.."/playerdata"
|
||
|
|
||
|
-- Files in playerdata/<plname>/ directories:
|
||
|
-- lastseen 1611343962 game Contains timestamp (Unix epoch) and type of last activity
|
||
|
-- game_is_online webchat_is_online 1609018688 File exists only while player is online and contains the timestamp of his last activity
|
||
|
-- game_player.log website_player.log 1611349823 join Log of player game/website events
|
||
|
|
||
|
minetest.mkdir(webchatdir)
|
||
|
|
||
|
-- Append new message to webchat log
|
||
|
function webchat_send_msg(plname,msg)
|
||
|
|
||
|
local clog_file=io.open(webchatdir.."/chat.log","a")
|
||
|
clog_file:write(os.time().."\t"..plname.."\t"..msg.."\n")
|
||
|
clog_file:close()
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Append new message to DM log of sender and receiver
|
||
|
function dmlog_send_msg(frpl,topl,msg)
|
||
|
|
||
|
local frpldat_dir=pldatadir.."/"..frpl
|
||
|
minetest.mkdir(frpldat_dir)
|
||
|
local dmlog_frpl_file=io.open(frpldat_dir.."/dm.log","a")
|
||
|
dmlog_frpl_file:write(os.time().."\tto\t"..topl.."\t"..msg.."\n")
|
||
|
dmlog_frpl_file:close()
|
||
|
|
||
|
if topl ~= "+error+" then
|
||
|
local topldat_dir=pldatadir.."/"..topl
|
||
|
minetest.mkdir(topldat_dir)
|
||
|
local dmlog_topl_file=io.open(topldat_dir.."/dm.log","a")
|
||
|
dmlog_topl_file:write(os.time().."\tfrom\t"..frpl.."\t"..msg.."\n")
|
||
|
dmlog_topl_file:close()
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Append new message to server log
|
||
|
function server_log(msg)
|
||
|
|
||
|
local slog_file=io.open(webchatdir.."/server.log","a")
|
||
|
slog_file:write(os.time().."\t"..msg.."\n")
|
||
|
slog_file:close()
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Update playerdata when player tries to join the game
|
||
|
local function game_prejoin(plname,plip)
|
||
|
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
minetest.mkdir(pldat_dir)
|
||
|
|
||
|
local game_plog_file=io.open(pldat_dir.."/game_player.log","a")
|
||
|
game_plog_file:write(os.time().."\tprejoin\t"..plip.."\n")
|
||
|
game_plog_file:close()
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Update playerdata when player joined the game
|
||
|
local function game_join(plname)
|
||
|
|
||
|
server_log(plname.." joined the game.")
|
||
|
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
minetest.mkdir(pldat_dir)
|
||
|
|
||
|
local game_plog_file=io.open(pldat_dir.."/game_player.log","a")
|
||
|
game_plog_file:write(os.time().."\tjoin\t"..minetest.get_player_ip(plname).."\n")
|
||
|
game_plog_file:close()
|
||
|
|
||
|
local lastseen_file=io.open(pldat_dir.."/lastseen","w")
|
||
|
lastseen_file:write(os.time().."\tgame\n")
|
||
|
lastseen_file:close()
|
||
|
|
||
|
local game_online_file=io.open(pldat_dir.."/game_is_online","w")
|
||
|
game_online_file:write(os.time().."\n")
|
||
|
game_online_file:close()
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Update playerdata when player left the game
|
||
|
local function game_leave(action,plname)
|
||
|
|
||
|
server_log(plname.." left the game.")
|
||
|
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
minetest.mkdir(pldat_dir)
|
||
|
|
||
|
local game_plog_file=io.open(pldat_dir.."/game_player.log","a")
|
||
|
game_plog_file:write(os.time().."\t"..action.."\n")
|
||
|
game_plog_file:close()
|
||
|
|
||
|
local lastseen_file=io.open(pldat_dir.."/lastseen","w")
|
||
|
lastseen_file:write(os.time().."\tgame\n")
|
||
|
lastseen_file:close()
|
||
|
|
||
|
os.remove(pldat_dir.."/game_is_online")
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Remove *_is_online files on startup/shutdown in case they were left behind
|
||
|
local function cleanup()
|
||
|
|
||
|
for _,plname in ipairs(minetest.get_dir_list(pldatadir,true)) do
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
os.remove(pldat_dir.."/game_is_online")
|
||
|
os.remove(pldat_dir.."/webchat_is_online")
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Player plname is logged in on the website and has the chat page open in his browser
|
||
|
function webchat.ping(plname)
|
||
|
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
minetest.mkdir(pldat_dir)
|
||
|
|
||
|
local lastseen_file=io.open(pldat_dir.."/lastseen","w")
|
||
|
lastseen_file:write(os.time().."\twebchat\n")
|
||
|
lastseen_file:close()
|
||
|
|
||
|
local webchat_online_file=io.open(pldat_dir.."/webchat_is_online","r")
|
||
|
if webchat_online_file then
|
||
|
|
||
|
webchat_online_file:close()
|
||
|
|
||
|
else
|
||
|
|
||
|
minetest.chat_send_all("*** "..plname.." joined the web chat.")
|
||
|
server_log(plname.." joined the web chat.")
|
||
|
|
||
|
local website_plog_file=io.open(pldat_dir.."/website_player.log","a")
|
||
|
website_plog_file:write(os.time().."\tchat_join\n")
|
||
|
website_plog_file:close()
|
||
|
minetest.log("action",plname.." joined the webchat.")
|
||
|
|
||
|
end
|
||
|
|
||
|
webchat_online_file=io.open(pldat_dir.."/webchat_is_online","w")
|
||
|
webchat_online_file:write(os.time().."\n")
|
||
|
webchat_online_file:close()
|
||
|
|
||
|
-- If 600 sec later the file webchat_is_online is older than 540 sec we suppose that
|
||
|
-- the user lost the connection or closed the browser without logging out
|
||
|
minetest.after(600,function()
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
local webchat_online_file=io.open(pldat_dir.."/webchat_is_online","r")
|
||
|
if webchat_online_file and os.time()-webchat_online_file:read() > 540 then
|
||
|
|
||
|
webchat_online_file:close()
|
||
|
|
||
|
minetest.chat_send_all("*** "..plname.." left the web chat (timeout).")
|
||
|
server_log(plname.." left the web chat (timeout).")
|
||
|
|
||
|
os.remove(pldat_dir.."/webchat_is_online")
|
||
|
|
||
|
local website_plog_file=io.open(pldat_dir.."/website_player.log","a")
|
||
|
website_plog_file:write(os.time().."\tchat_timeout\n")
|
||
|
website_plog_file:close()
|
||
|
minetest.log("action",plname.." left the webchat (timeout).")
|
||
|
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Modify /me & /msg chat commands so that they also write to the webchat log (original Minetest code: builtin/game/chat.lua)
|
||
|
minetest.register_chatcommand("me",{
|
||
|
params="<action>",
|
||
|
description="Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')",
|
||
|
privs={shout=true},
|
||
|
func=function(plname,param)
|
||
|
minetest.chat_send_all("* "..plname.." "..param)
|
||
|
webchat_send_msg(plname,"* "..plname.." "..param)
|
||
|
return true
|
||
|
end
|
||
|
})
|
||
|
minetest.register_chatcommand("msg",{
|
||
|
params="<sender> <message>",
|
||
|
description="Send a direct message to a player (game and web chat)",
|
||
|
privs={shout=true},
|
||
|
func=function(sender,param)
|
||
|
local sendto,message=param:match("^(%S+)%s(.+)$")
|
||
|
if not sendto then
|
||
|
return false,"Invalid usage, see /help msg."
|
||
|
end
|
||
|
if not minetest.player_exists(sendto) then
|
||
|
return false,"The player "..sendto.." does not exist."
|
||
|
end
|
||
|
-- Always send DM to webchat dm log of sender and receiver
|
||
|
dmlog_send_msg(sender,sendto,message)
|
||
|
-- Send DM to game chat only if the receiver is online
|
||
|
if minetest.get_player_by_name(sendto) then
|
||
|
minetest.chat_send_player(sendto,"DM from "..sender..": "..message)
|
||
|
end
|
||
|
minetest.log("action","DM from "..sender.." to "..sendto.." (in game): "..message)
|
||
|
return true,"Message sent."
|
||
|
end
|
||
|
})
|
||
|
|
||
|
-- Login to website with Minetest credentials
|
||
|
function webchat.login(plname,passwd,plip)
|
||
|
|
||
|
local pl_ah=minetest.get_auth_handler().get_auth(plname)
|
||
|
if pl_ah and minetest.check_password_entry(plname,pl_ah['password'],passwd) then
|
||
|
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
minetest.mkdir(pldat_dir)
|
||
|
|
||
|
local website_plog_file=io.open(pldat_dir.."/website_player.log","a")
|
||
|
website_plog_file:write(os.time().."\tlogin\t"..plip.."\n")
|
||
|
website_plog_file:close()
|
||
|
|
||
|
minetest.log("action",plname.." ["..plip.."] logged in to the website.")
|
||
|
return "auth-ok"
|
||
|
|
||
|
else
|
||
|
|
||
|
minetest.log("action","Server: User "..plname.." at "..plip.." supplied wrong password on the website.")
|
||
|
return "auth-failed"
|
||
|
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Logout from website
|
||
|
function webchat.logout(plname)
|
||
|
|
||
|
local pldat_dir=pldatadir.."/"..plname
|
||
|
|
||
|
local website_plog_file=io.open(pldat_dir.."/website_player.log","a")
|
||
|
|
||
|
if os.remove(pldat_dir.."/webchat_is_online") then
|
||
|
minetest.chat_send_all("*** "..plname.." left the web chat.")
|
||
|
server_log(plname.." left the web chat.")
|
||
|
website_plog_file:write(os.time().."\tchat_leave\n")
|
||
|
end
|
||
|
|
||
|
website_plog_file:write(os.time().."\tlogout\n")
|
||
|
website_plog_file:close()
|
||
|
minetest.log("action",plname.." logged out from the website.")
|
||
|
|
||
|
end
|
||
|
|
||
|
-- Receive message from webchat - Partly taken from builtin/game/chat.lua
|
||
|
function webchat.receive_msg(sender,msg)
|
||
|
|
||
|
if msg:sub(1,1) ~= "/" then
|
||
|
-- msg does not start with / => chat message
|
||
|
|
||
|
if minetest.check_player_privs(sender,"shout") then
|
||
|
minetest.chat_send_all("<"..sender.."> "..msg)
|
||
|
webchat_send_msg(sender,msg)
|
||
|
minetest.log("action","Chat message from "..sender.." (in webchat): "..msg)
|
||
|
else
|
||
|
dmlog_send_msg(sender,"+error+","You don't have permission to shout.")
|
||
|
end
|
||
|
|
||
|
else
|
||
|
-- msg starts with a / => command
|
||
|
|
||
|
local cmd,param=string.match(msg,"^/([^ ]+) *(.*)")
|
||
|
param=param or ""
|
||
|
|
||
|
if not cmd then
|
||
|
dmlog_send_msg(sender,"+error+","Empty command.")
|
||
|
return false
|
||
|
|
||
|
elseif cmd=="me" then
|
||
|
if minetest.check_player_privs(sender,"shout") then
|
||
|
minetest.chat_send_all("* "..sender.." "..param)
|
||
|
webchat_send_msg(sender,"* "..sender.." "..param)
|
||
|
minetest.log("action","/me message from "..sender.." (in webchat): ".."* "..sender.." "..param)
|
||
|
else
|
||
|
dmlog_send_msg(sender,"+error+","/me - You don't have permission to run this command (missing privilege: shout).")
|
||
|
end
|
||
|
|
||
|
elseif cmd=="msg" then
|
||
|
if minetest.check_player_privs(sender,"shout") then
|
||
|
local sendto,message=param:match("^(%S+)%s(.+)$")
|
||
|
if not sendto then
|
||
|
dmlog_send_msg(sender,"+error+","/msg - Invalid usage.")
|
||
|
minetest.log("action","Invalid usage of command /msg by "..sender.." (in webchat).")
|
||
|
return false
|
||
|
end
|
||
|
if not minetest.player_exists(sendto) then
|
||
|
dmlog_send_msg(sender,"+error+","/msg - The player "..sendto.." does not exist.")
|
||
|
minetest.log("action","DM from "..sender.." to non-existent player "..sendto.." (in webchat).")
|
||
|
return false
|
||
|
end
|
||
|
-- Always send DM to webchat dm log of sender and receiver
|
||
|
dmlog_send_msg(sender,sendto,message)
|
||
|
-- Send DM to game chat only if the receiver/sender is online
|
||
|
if minetest.get_player_by_name(sendto) then
|
||
|
minetest.chat_send_player(sendto,"DM from "..sender..": "..message)
|
||
|
end
|
||
|
if minetest.get_player_by_name(sender) then
|
||
|
minetest.chat_send_player(sender,"DM to "..sendto..": "..message)
|
||
|
end
|
||
|
minetest.log("action","DM from "..sender.." to "..sendto.." (in webchat): "..message)
|
||
|
else
|
||
|
dmlog_send_msg(sender,"+error+","/msg - You don't have permission to run this command (missing privilege: shout).")
|
||
|
end
|
||
|
|
||
|
else
|
||
|
dmlog_send_msg(sender,"+error+","/"..cmd.." - Unknown command.")
|
||
|
minetest.log("action","Unknown command "..cmd.." from "..sender.." (in webchat).")
|
||
|
return false
|
||
|
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
webchat.ping(sender)
|
||
|
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Game event registration
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
-- Update playerdata/ on startup
|
||
|
cleanup()
|
||
|
server_log("Server startup")
|
||
|
|
||
|
-- Update chatlog on every chat message
|
||
|
minetest.register_on_chat_message(webchat_send_msg)
|
||
|
|
||
|
-- Update playerdata/ on (pre)join and leave
|
||
|
minetest.register_on_prejoinplayer(function(plname,plip)
|
||
|
game_prejoin(plname,plip)
|
||
|
end)
|
||
|
minetest.register_on_joinplayer(function(player)
|
||
|
game_join(player:get_player_name())
|
||
|
end)
|
||
|
minetest.register_on_leaveplayer(function(player)
|
||
|
game_leave("leave",player:get_player_name())
|
||
|
end)
|
||
|
|
||
|
-- Update playerdata/ on shutdown
|
||
|
minetest.register_on_shutdown(function()
|
||
|
for _,player in ipairs(minetest.get_connected_players()) do
|
||
|
game_leave("shutdown",player:get_player_name())
|
||
|
end
|
||
|
cleanup()
|
||
|
server_log("Server shutdown")
|
||
|
end)
|
||
|
|
||
|
--------------------------------------------------------------------------------
|