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(),
}
-- insert in global storage
mail.addMessage(msg)
-- add in senders outbox
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
local mail_alert = f(mail.receive_mail_message, m.from, m.subject)

View file

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

View file

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

View file

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

View file

@ -59,156 +59,64 @@ function mail.set_storage_entry(playername, entry)
mail.storage:get_string(playername, minetest.write_json(entry))
end
function mail.getMessage(msg_id)
local messages = mail.getMessages()
if messages then
for _, msg in ipairs(messages) do
-- get a mail by id from the players in- or outbox
function mail.get_message(playername, msg_id)
local entry = mail.get_storage_entry(playername)
for _, msg in ipairs(entry.inbox) do
if msg.id == msg_id then
return msg
end
end
for _, msg in ipairs(entry.outbox) do
if msg.id == msg_id then
return msg
end
end
end
end
-- api in use by the `fancyvend` mod
function mail.getMessages(playername)
local messages = mail.getMessages()
local playerMessages = {}
if messages then
for _, msg in ipairs(messages) do
local cc = ""
local bcc = ""
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
-- marks a mail read by its id
function mail.mark_read(playername, msg_id)
local entry = mail.get_storage_entry(playername)
for _, msg in ipairs(entry.inbox) do
if msg.id == msg_id then
msg.read = true
mail.set_storage_entry(playername, entry)
return
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
-- marks a mail unread by its id
function mail.mark_unread(playername, msg_id)
local entry = mail.get_storage_entry(playername)
for _, msg in ipairs(entry.inbox) do
if msg.id == msg_id then
msg.read = false
mail.set_storage_entry(playername, entry)
return
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
-- deletes a mail by its id
function mail.delete_mail(playername, msg_id)
local entry = mail.get_storage_entry(playername)
for i, msg in ipairs(entry.inbox) do
if msg.id == msg_id then
table.remove(entry.outbox, i)
mail.set_storage_entry(playername, entry)
return
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
for i, msg in ipairs(entry.outbox) do
if msg.id == msg_id then
table.remove(entry.outbox, i)
mail.set_storage_entry(playername, entry)
return
end
end
end
function mail.addStatus(player, msg_id, status)
local messagesStatus = mail.getStatus()
local msg_status = {id = msg_id, player = player, status = status}
table.insert(messagesStatus, msg_status)
if mail.write_json_file(mail.maildir .. "/mail.status.json", messagesStatus) then
return true
else
minetest.log("error","[mail] Save failed - messages status may be lost!")
return false
end
end
function mail.setStatus(player, msg_id, status)
local messagesStatus = mail.getStatus()
for _, msg_status in ipairs(messagesStatus) do
if msg_status.id == msg_id and msg_status.player == player then
messagesStatus[_] = {id = msg_id, player = player, status = status}
end
end
if mail.write_json_file(mail.maildir .. "/mail.status.json", messagesStatus) then
return true
else
minetest.log("error","[mail] Save failed - messages status may be lost!")
return false
end
end
function mail.getContactsFile()
return mail.maildir .. "/mail.contacts.json"

View file

@ -28,7 +28,7 @@ function mail.show_inbox(name)
if messages[1] then
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
formspec[#formspec + 1] = ",#FFD788"
else
@ -114,9 +114,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.delete then
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
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "deleted")
mail.delete_mail(name, messagesSent[mail.selected_idxs.sent[name]].id)
end
mail.show_mail_menu(name)
@ -150,18 +150,18 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.markread 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
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "read")
mail.mark_read(name, messagesSent[mail.selected_idxs.sent[name]].id)
end
mail.show_mail_menu(name)
elseif fields.markunread 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
mail.setStatus(name, messagesSent[mail.selected_idxs.sent[name]].id, "unread")
mail.mark_unread(name, messagesSent[mail.selected_idxs.sent[name]].id)
end
mail.show_mail_menu(name)

View file

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

View file

@ -19,7 +19,8 @@ local sent_formspec = "size[8,10;]" .. mail.theme .. [[
function mail.show_sent(name)
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

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