From f7a7ef361a51f8850669ab5dfa5f08d8beb5c3b6 Mon Sep 17 00:00:00 2001 From: flux <25628292+fluxionary@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:41:41 -0800 Subject: [PATCH] use canonical_name if it is available; report error when trying to send mail to non-existent players --- api.lua | 109 ++++++++++++++++++++++----------------------- api.md | 6 ++- gui.lua | 7 ++- mod.conf | 2 +- util/normalize.lua | 31 ++++++++----- 5 files changed, 83 insertions(+), 72 deletions(-) diff --git a/api.lua b/api.lua index e9fbbf0..1559954 100644 --- a/api.lua +++ b/api.lua @@ -1,5 +1,7 @@ -- see: mail.md +local f = string.format + mail.registered_on_receives = {} function mail.register_on_receive(func) mail.registered_on_receives[#mail.registered_on_receives + 1] = func @@ -15,101 +17,96 @@ see: "Mail format" api.md TODO: refactor this garbage code! --]] -function mail.send(src, dst, subject, body) +function mail.send(...) -- figure out format local m - if dst == nil and subject == nil and body == nil then - -- new format (one object param) - m = src + if #{...} == 1 then + -- new format (one table param) + m = ... + -- populate "to" field + m.to = m.to or m.dst + -- populate "from" field + m.from = m.from or m.src else -- old format m = {} - m.from = src - m.to = dst - m.subject = subject - m.body = body - end - - if m.dst and not m.to then - -- populate "to" field - m.to = m.dst - end - - if m.src and not m.from then - -- populate "from" field - m.from = m.src + m.from, m.to, m.subject, m.body = ... end -- sane default values m.subject = m.subject or "" m.body = m.body or "" - local cc - local bcc - local extra - -- log mail send action - if m.cc or m.bcc then - if m.cc then - cc = "CC: " .. m.cc - if m.bcc then - cc = cc .. " - " - end - else - cc = "" - end - if m.bcc then - bcc = "BCC: " .. m.bcc - else - bcc = "" - end - extra = " (" .. cc .. bcc .. ")" - else - extra = "" + if m.subject == "" then + m.subject = "(No subject)" + end + if string.len(m.subject) > 30 then + m.subject = string.sub(m.subject,1,27) .. "..." end - minetest.log("action", "[mail] '" .. m.from .. "' sends mail to '" .. m.to .. "'" .. - extra .. "' with subject '" .. m.subject .. "' and body: '" .. m.body .. "'") - -- normalize to, cc and bcc while compiling a list of all recipients local recipients = {} - m.to = mail.normalize_players_and_add_recipients(m.to, recipients) + local undeliverable = {} + m.to = mail.normalize_players_and_add_recipients(m.to, recipients, undeliverable) if m.cc then - m.cc = mail.normalize_players_and_add_recipients(m.cc, recipients) + m.cc = mail.normalize_players_and_add_recipients(m.cc, recipients, undeliverable) end if m.bcc then - m.bcc = mail.normalize_players_and_add_recipients(m.bcc, recipients) + m.bcc = mail.normalize_players_and_add_recipients(m.bcc, recipients, undeliverable) end + if next(undeliverable) then -- table is not empty + local undeliverable_names = {} + for name in pairs(undeliverable) do + undeliverable_names[#undeliverable_names + 1] = '"' .. name .. '"' + end + return f("recipients %s don't exist; cannot send mail.", + table.concat(undeliverable_names, ", ") + ) + end + + local extra = {} + local extra_log + if m.cc then + table.insert(extra, "CC: " .. m.cc) + end + if m.bcc then + table.insert(extra, "BCC: " .. m.bcc) + end + if #extra > 0 then + extra_log = f(" (%s)", table.concat(extra, " - ")) + else + extra_log = "" + end + + minetest.log("action", f("[mail] %q send mail to %q%s with subject %q and body %q", + m.from, m.to, extra_log, m.subject, m.body + )) + -- form the actual mail local msg = { unread = true, sender = m.from, to = m.to, + cc = m.cc, subject = m.subject, body = m.body, time = os.time(), } - if m.cc then - msg.cc = m.cc - end -- send the mail to all recipients - for _, recipient in pairs(recipients) do + for recipient in pairs(recipients) do local messages = mail.getMessages(recipient) table.insert(messages, 1, msg) mail.setMessages(recipient, messages) end -- notify recipients that happen to be online + local mail_alert = f(mail.receive_mail_message, m.from, m.subject) for _, player in ipairs(minetest.get_connected_players()) do local name = player:get_player_name() - if recipients[string.lower(name)] ~= nil then - if m.subject == "" then m.subject = "(No subject)" end - if string.len(m.subject) > 30 then - m.subject = string.sub(m.subject,1,27) .. "..." - end - minetest.chat_send_player(name, - string.format(mail.receive_mail_message, m.from, m.subject)) + if recipients[name] then + minetest.chat_send_player(name, mail_alert) end end diff --git a/api.md b/api.md index 984cd28..b0800e2 100644 --- a/api.md +++ b/api.md @@ -35,12 +35,13 @@ mail = { ## Sending mail Old variant (pre-1.1) ```lua -mail.send("source name", "destination name", "subject line", "mail body") +local error = mail.send("source name", "destination name", "subject line", "mail body") +-- error will contain an error message if mail couldn't be delivered, otherwise nil ``` New variant (1.1+) ```lua -mail.send({ +local error = mail.send({ from = "sender name", to = "destination name", cc = "carbon copy", @@ -48,6 +49,7 @@ mail.send({ subject = "subject line", body = "mail body" }) +-- error will contain an error message if mail couldn't be delivered, otherwise nil ``` # Hooks diff --git a/gui.lua b/gui.lua index 69e4837..5e32c68 100644 --- a/gui.lua +++ b/gui.lua @@ -464,7 +464,7 @@ function mail.handle_receivefields(player, formname, fields) elseif formname == "mail:compose" then local name = player:get_player_name() if fields.send then - mail.send({ + local error = mail.send({ from = name, to = fields.to, cc = fields.cc, @@ -472,6 +472,11 @@ function mail.handle_receivefields(player, formname, fields) subject = fields.subject, body = fields.body, }) + if error then + minetest.chat_send_player(name, error) + return + end + local contacts = mail.getContacts(name) local recipients = mail.parse_player_list(fields.to) local changed = false diff --git a/mod.conf b/mod.conf index 64f3889..31761be 100644 --- a/mod.conf +++ b/mod.conf @@ -1,3 +1,3 @@ name = mail description = ingame mail-system -optional_depends = unified_inventory,default,mtt +optional_depends = canonical_name,default,mtt,unified_inventory diff --git a/util/normalize.lua b/util/normalize.lua index 7dcec4a..3064bf3 100644 --- a/util/normalize.lua +++ b/util/normalize.lua @@ -1,18 +1,21 @@ +local has_canonical_name = minetest.get_modpath("canonical_name") + --[[ return the field normalized (comma separated, single space) and add individual player names to recipient list --]] -function mail.normalize_players_and_add_recipients(field, recipients) +function mail.normalize_players_and_add_recipients(field, recipients, undeliverable) local order = mail.parse_player_list(field) - for _, c in ipairs(order) do - if recipients[string.lower(c)] == nil then - recipients[string.lower(c)] = c + for _, player_name in ipairs(order) do + if not minetest.player_exists(player_name) then + undeliverable[player_name] = true + else + recipients[player_name] = true end end return mail.concat_player_list(order) end - function mail.parse_player_list(field) if not field then return {} @@ -24,10 +27,15 @@ function mail.parse_player_list(field) -- get individual players local player_set = {} local order = {} - field:gsub(pattern, function(c) - if player_set[string.lower(c)] == nil then - player_set[string.lower(c)] = c - order[#order+1] = c + field:gsub(pattern, function(player_name) + local lower = string.lower(player_name) + if not player_set[lower] then + if has_canonical_name then + player_name = canonical_name.get(player_name) or player_name + end + + player_set[lower] = player_name + order[#order+1] = player_name end end) @@ -44,15 +52,14 @@ function mail.player_in_list(name, list) if type(list) == "string" then list = mail.parse_player_list(list) end - for _, c in pairs(list) do - if name == c then + for _, player_name in pairs(list) do + if name == player_name then return true end end return false end - function mail.ensure_new_format(message, name) if message.to == nil then message.to = name