webchat/init.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)
--------------------------------------------------------------------------------