status refactoring

This commit is contained in:
BuckarooBanzay 2023-03-28 14:23:48 +02:00 committed by Athozus
parent 706b870b7d
commit b414ace620
No known key found for this signature in database
GPG key ID: B50895022E8484BF
9 changed files with 91 additions and 176 deletions

13
api.lua
View file

@ -97,8 +97,17 @@ function mail.send(...)
time = os.time(), time = os.time(),
} }
-- insert in global storage -- add in senders outbox
mail.addMessage(msg) local entry = mail.get_storage_entry(m.from)
table.insert(entry.outbox, msg)
mail.set_storage_entry(m.from, entry)
-- add in every receivers inbox
for recipient in pairs(recipients) do
entry = mail.get_storage_entry(recipient)
table.insert(entry.inbox, msg)
mail.set_storage_entry(recipient, entry)
end
-- notify recipients that happen to be online -- notify recipients that happen to be online
local mail_alert = f(mail.receive_mail_message, m.from, m.subject) local mail_alert = f(mail.receive_mail_message, m.from, m.subject)

View file

@ -1,9 +1,6 @@
mail = { mail = {
-- api version -- version
apiversion = 1.1, version = 3,
-- database version
dbversion = 3.0,
-- mail directory -- mail directory
maildir = minetest.get_worldpath().."/mails", maildir = minetest.get_worldpath().."/mails",
@ -39,6 +36,7 @@ end
local MP = minetest.get_modpath(minetest.get_current_modname()) local MP = minetest.get_modpath(minetest.get_current_modname())
dofile(MP .. "/util/normalize.lua") dofile(MP .. "/util/normalize.lua")
dofile(MP .. "/util/uuid.lua")
dofile(MP .. "/chatcommands.lua") dofile(MP .. "/chatcommands.lua")
dofile(MP .. "/migrate.lua") dofile(MP .. "/migrate.lua")
dofile(MP .. "/hud.lua") dofile(MP .. "/hud.lua")

View file

@ -13,12 +13,12 @@ end
function mail.migrate_v2_to_v3() function mail.migrate_v2_to_v3()
minetest.mkdir(mail.maildir) -- if necessary (eg. first login) minetest.mkdir(mail.maildir) -- if necessary (eg. first login)
print("[mail] Migration from v2 to v3 database") print("[mail] Migration from v2 to v3 database")
local already_processed = {} -- store messages that are already process to avoid duplicates
minetest.after(0,function() minetest.after(0,function()
for playername, _ in minetest.get_auth_handler().iterate() do for playername, _ in minetest.get_auth_handler().iterate() do
local player_contacts = mail.read_json_file(mail.maildir .. "/contacts/" .. playername .. ".json")
local entry = mail.get_storage_entry(playername) local entry = mail.get_storage_entry(playername)
local player_contacts = mail.read_json_file(mail.maildir .. "/contacts/" .. playername .. ".json")
for _, c in pairs(player_contacts) do for _, c in pairs(player_contacts) do
table.insert(entry.contacts, { name = c.name, note = c.note }) table.insert(entry.contacts, { name = c.name, note = c.note })
end end
@ -26,27 +26,16 @@ function mail.migrate_v2_to_v3()
local saneplayername = string.gsub(playername, "[.|/]", "") local saneplayername = string.gsub(playername, "[.|/]", "")
local player_inbox = mail.read_json_file(mail.maildir .. "/" .. saneplayername .. ".json") local player_inbox = mail.read_json_file(mail.maildir .. "/" .. saneplayername .. ".json")
for _, msg in ipairs(player_inbox) do for _, msg in ipairs(player_inbox) do
-- id like "123456789.0singleplayer" -- it presumes that a same sender cannot send two mails within a second if msg.to then
local msg_id = tostring(msg.time) .. msg.sender table.insert(entry.inbox, {
local new_msg = true -- check if that mail was already processed with another player id = mail.new_uuid(),
for _, cur_id in ipairs(already_processed) do
if cur_id == msg_id then
new_msg = false
break
end
end
-- add if valid and "to" field populated (missing in ancient storage formats)
if new_msg and msg.to then
local msg_table = {
sender = msg.sender, sender = msg.sender,
to = msg.to, to = msg.to,
cc = msg.cc, cc = msg.cc,
subject = msg.subject, subject = msg.subject,
body = msg.body, body = msg.body,
time = msg.time, time = msg.time,
} })
mail.addMessage(msg_table)
table.insert(already_processed, msg_id)
end end
end end

View file

@ -1,6 +1,7 @@
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
minetest.after(2, function(name) minetest.after(2, function(name)
local messages = mail.getMessages(name) local entry = mail.get_storage_entry(name)
local messages = entry.inbox
local unreadcount = 0 local unreadcount = 0

View file

@ -59,157 +59,65 @@ function mail.set_storage_entry(playername, entry)
mail.storage:get_string(playername, minetest.write_json(entry)) mail.storage:get_string(playername, minetest.write_json(entry))
end end
function mail.getMessage(msg_id) -- get a mail by id from the players in- or outbox
local messages = mail.getMessages() function mail.get_message(playername, msg_id)
if messages then local entry = mail.get_storage_entry(playername)
for _, msg in ipairs(messages) do for _, msg in ipairs(entry.inbox) do
if msg.id == msg_id then if msg.id == msg_id then
return msg return msg
end end
end
for _, msg in ipairs(entry.outbox) do
if msg.id == msg_id then
return msg
end end
end end
end end
-- api in use by the `fancyvend` mod -- marks a mail read by its id
function mail.getMessages(playername) function mail.mark_read(playername, msg_id)
local messages = mail.getMessages() local entry = mail.get_storage_entry(playername)
local playerMessages = {} for _, msg in ipairs(entry.inbox) do
if messages then if msg.id == msg_id then
for _, msg in ipairs(messages) do msg.read = true
local cc = "" mail.set_storage_entry(playername, entry)
local bcc = "" return
if msg.cc then
cc = msg.cc
end
if msg.bcc then
bcc = msg.bcc
end
local receivers = (msg.to .. "," .. cc .. "," .. bcc):split(",")
for _, receiver in ipairs(receivers) do
receiver = string.gsub(receiver, " ", "") -- avoid blank spaces (ex : " singleplayer" instead of "singleplayer")
if receiver == playername then -- check if player is a receiver
if mail.getMessageStatus(receiver, msg.id) ~= "deleted" then -- do not return if the message was deleted by player
table.insert(playerMessages, msg)
break
end
elseif msg.sender == playername then
if mail.getMessageStatus(receiver, msg.id) ~= "deleted" then -- do not return if the message was deleted by player
table.insert(playerMessages, msg)
break
end
end
end
end
end
return playerMessages
end
function mail.getPlayerSentMessages(playername)
local messages = mail.getMessages()
local playerSentMessages = {}
if messages[1] then
for _, msg in ipairs(messages) do
if msg.sender == playername then -- check if player is the sender
-- do not return if the message was deleted from player
if mail.getMessageStatus(playername, msg.id) ~= "deleted" then
table.insert(playerSentMessages, msg)
end
end
end
end
return playerSentMessages
end
function mail.setMessages(playername, messages)
if mail.write_json_file(mail.getMailFile(playername), messages) then
mail.hud_update(playername, messages)
return true
else
minetest.log("error","[mail] Save failed - messages may be lost! ("..playername..")")
return false
end
end
function mail.addMessage(message)
local messages = mail.getMessages()
if messages[1] then
local previousMsg = messages[1]
message.id = previousMsg.id + 1
table.insert(messages, message)
else
message.id = 1
messages = {message}
end
if mail.write_json_file(mail.maildir .. "/mail.messages.json", messages) then
-- add default status (unread for receivers) of this message
local isSenderAReceiver = false
-- extracted maillists from all receivers
local receivers = mail.extractMaillists((message.to .. "," .. (message.cc or "")
.. "," .. (message.bcc or "")), message.sender)
for _, receiver in ipairs(receivers) do
if minetest.player_exists(receiver) then -- avoid blank names
mail.addStatus(receiver, message.id, "unread")
if message.sender == receiver then
isSenderAReceiver = true
end
end
end
if isSenderAReceiver == false then
mail.addStatus(message.sender, message.id, "read")
end
return true
else
minetest.log("error","[mail] Save failed - messages may be lost!")
return false
end
end
function mail.getStatus()
local messagesStatus = mail.read_json_file(mail.maildir .. "/mail.status.json")
return messagesStatus
end
function mail.getMessageStatus(player, msg_id)
local messagesStatus = mail.getStatus()
for _, msg in ipairs(messagesStatus) do
if msg.id == msg_id and msg.player == player then
return msg.status
end end
end end
end end
function mail.addStatus(player, msg_id, status) -- marks a mail unread by its id
local messagesStatus = mail.getStatus() function mail.mark_unread(playername, msg_id)
local msg_status = {id = msg_id, player = player, status = status} local entry = mail.get_storage_entry(playername)
table.insert(messagesStatus, msg_status) for _, msg in ipairs(entry.inbox) do
if mail.write_json_file(mail.maildir .. "/mail.status.json", messagesStatus) then if msg.id == msg_id then
return true msg.read = false
else mail.set_storage_entry(playername, entry)
minetest.log("error","[mail] Save failed - messages status may be lost!") return
return false end
end end
end end
function mail.setStatus(player, msg_id, status) -- deletes a mail by its id
local messagesStatus = mail.getStatus() function mail.delete_mail(playername, msg_id)
for _, msg_status in ipairs(messagesStatus) do local entry = mail.get_storage_entry(playername)
if msg_status.id == msg_id and msg_status.player == player then for i, msg in ipairs(entry.inbox) do
messagesStatus[_] = {id = msg_id, player = player, status = status} if msg.id == msg_id then
table.remove(entry.outbox, i)
mail.set_storage_entry(playername, entry)
return
end end
end end
if mail.write_json_file(mail.maildir .. "/mail.status.json", messagesStatus) then for i, msg in ipairs(entry.outbox) do
return true if msg.id == msg_id then
else table.remove(entry.outbox, i)
minetest.log("error","[mail] Save failed - messages status may be lost!") mail.set_storage_entry(playername, entry)
return false return
end
end end
end end
function mail.getContactsFile() function mail.getContactsFile()
return mail.maildir .. "/mail.contacts.json" return mail.maildir .. "/mail.contacts.json"
end end

View file

@ -28,7 +28,7 @@ function mail.show_inbox(name)
if messages[1] then if messages[1] then
for _, message in ipairs(messages) do for _, message in ipairs(messages) do
if mail.getMessageStatus(name, message.id) == "unread" then if not message.read then
if not mail.player_in_list(name, message.to) then if not mail.player_in_list(name, message.to) then
formspec[#formspec + 1] = ",#FFD788" formspec[#formspec + 1] = ",#FFD788"
else else
@ -114,9 +114,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.delete then elseif fields.delete then
if formname == "mail:inbox" and messagesInbox[mail.selected_idxs.inbox[name]] then -- inbox table if formname == "mail:inbox" and messagesInbox[mail.selected_idxs.inbox[name]] then -- inbox table
mail.setStatus(name, messagesInbox[mail.selected_idxs.inbox[name]].id, "deleted") mail.delete_mail(name, messagesInbox[mail.selected_idxs.inbox[name]].id)
elseif formname == "mail:sent" and messagesSent[mail.selected_idxs.sent[name]] then -- sent table elseif formname == "mail:sent" and messagesSent[mail.selected_idxs.sent[name]] then -- sent table
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "deleted") mail.delete_mail(name, messagesSent[mail.selected_idxs.sent[name]].id)
end end
mail.show_mail_menu(name) mail.show_mail_menu(name)
@ -150,18 +150,18 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.markread then elseif fields.markread then
if formname == "mail:inbox" and messagesInbox[mail.selected_idxs.inbox[name]] then if formname == "mail:inbox" and messagesInbox[mail.selected_idxs.inbox[name]] then
mail.setStatus(name, messagesInbox[mail.selected_idxs.inbox[name]].id, "read") mail.mark_read(name, messagesInbox[mail.selected_idxs.inbox[name]].id)
elseif formname == "mail:sent" and messagesSent[mail.selected_idxs.sent[name]] then elseif formname == "mail:sent" and messagesSent[mail.selected_idxs.sent[name]] then
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "read") mail.mark_read(name, messagesSent[mail.selected_idxs.sent[name]].id)
end end
mail.show_mail_menu(name) mail.show_mail_menu(name)
elseif fields.markunread then elseif fields.markunread then
if formname == "mail:inbox" and messagesInbox[mail.selected_idxs.inbox[name]] then if formname == "mail:inbox" and messagesInbox[mail.selected_idxs.inbox[name]] then
mail.setStatus(name, messagesInbox[mail.selected_idxs.inbox[name]].id, "unread") mail.mark_unread(name, messagesInbox[mail.selected_idxs.inbox[name]].id)
elseif formname == "mail:sent" and messagesSent[mail.selected_idxs.sent[name]] then elseif formname == "mail:sent" and messagesSent[mail.selected_idxs.sent[name]] then
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "unread") mail.mark_unread(name, messagesSent[mail.selected_idxs.sent[name]].id)
end end
mail.show_mail_menu(name) mail.show_mail_menu(name)

View file

@ -1,7 +1,8 @@
local FORMNAME = "mail:message" local FORMNAME = "mail:message"
function mail.show_message(name, msgnumber) function mail.show_message(name, id)
local message = mail.getMessage(msgnumber) local message = mail.get_message(name, id)
local formspec = [[ local formspec = [[
size[8,9] size[8,9]
@ -32,10 +33,9 @@ function mail.show_message(name, msgnumber)
local body = minetest.formspec_escape(message.body) or "" local body = minetest.formspec_escape(message.body) or ""
formspec = string.format(formspec, from, to, cc, date, subject, body) formspec = string.format(formspec, from, to, cc, date, subject, body)
local message_status = mail.getMessageStatus(name, message.id) if not message.read then
-- mark as read
if message_status == "unread" then mail.mark_read(name, id)
mail.setStatus(name, message.id, "read")
end end
minetest.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
@ -125,9 +125,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.delete then elseif fields.delete then
if messagesInbox[mail.selected_idxs.inbox[name]] then if messagesInbox[mail.selected_idxs.inbox[name]] then
mail.setStatus(name, messagesInbox[mail.selected_idxs.inbox[name]].id, "deleted") mail.delete_mail(name, messagesInbox[mail.selected_idxs.inbox[name]].id)
elseif messagesSent[mail.selected_idxs.sent[name]] then elseif messagesSent[mail.selected_idxs.sent[name]] then
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "deleted") mail.delete_mail(name, messagesSent[mail.selected_idxs.sent[name]].id)
end end
mail.show_mail_menu(name) mail.show_mail_menu(name)
end end

View file

@ -19,7 +19,8 @@ local sent_formspec = "size[8,10;]" .. mail.theme .. [[
function mail.show_sent(name) function mail.show_sent(name)
local formspec = { sent_formspec } local formspec = { sent_formspec }
local messages = mail.getPlayerSentMessages(name) local entry = mail.get_storage_entry(name)
local messages = entry.outbox
mail.message_drafts[name] = nil mail.message_drafts[name] = nil

9
util/uuid.lua Normal file
View file

@ -0,0 +1,9 @@
-- source: https://gist.github.com/jrus/3197011
local random = math.random
function mail.new_uuid()
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
return string.format('%x', v)
end)
end