use canonical_name if it is available; report error when trying to send mail to non-existent players

This commit is contained in:
flux 2023-02-23 13:41:41 -08:00
parent 43bf5b9a6f
commit f7a7ef361a
No known key found for this signature in database
GPG Key ID: 9333B27816848A15
5 changed files with 83 additions and 72 deletions

107
api.lua
View File

@ -1,5 +1,7 @@
-- see: mail.md -- see: mail.md
local f = string.format
mail.registered_on_receives = {} mail.registered_on_receives = {}
function mail.register_on_receive(func) function mail.register_on_receive(func)
mail.registered_on_receives[#mail.registered_on_receives + 1] = 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! TODO: refactor this garbage code!
--]] --]]
function mail.send(src, dst, subject, body) function mail.send(...)
-- figure out format -- figure out format
local m local m
if dst == nil and subject == nil and body == nil then if #{...} == 1 then
-- new format (one object param) -- new format (one table param)
m = src m = ...
-- populate "to" field
m.to = m.to or m.dst
-- populate "from" field
m.from = m.from or m.src
else else
-- old format -- old format
m = {} m = {}
m.from = src m.from, m.to, m.subject, m.body = ...
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
end end
-- sane default values -- sane default values
m.subject = m.subject or "" m.subject = m.subject or ""
m.body = m.body or "" m.body = m.body or ""
local cc if m.subject == "" then
local bcc m.subject = "(No subject)"
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 end
else if string.len(m.subject) > 30 then
cc = "" m.subject = string.sub(m.subject,1,27) .. "..."
end end
if m.bcc then
bcc = "BCC: " .. m.bcc
else
bcc = ""
end
extra = " (" .. cc .. bcc .. ")"
else
extra = ""
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 -- normalize to, cc and bcc while compiling a list of all recipients
local 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 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 end
if m.bcc then 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 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 -- form the actual mail
local msg = { local msg = {
unread = true, unread = true,
sender = m.from, sender = m.from,
to = m.to, to = m.to,
cc = m.cc,
subject = m.subject, subject = m.subject,
body = m.body, body = m.body,
time = os.time(), time = os.time(),
} }
if m.cc then
msg.cc = m.cc
end
-- send the mail to all recipients -- send the mail to all recipients
for _, recipient in pairs(recipients) do for recipient in pairs(recipients) do
local messages = mail.getMessages(recipient) local messages = mail.getMessages(recipient)
table.insert(messages, 1, msg) table.insert(messages, 1, msg)
mail.setMessages(recipient, messages) mail.setMessages(recipient, messages)
end 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)
for _, player in ipairs(minetest.get_connected_players()) do for _, player in ipairs(minetest.get_connected_players()) do
local name = player:get_player_name() local name = player:get_player_name()
if recipients[string.lower(name)] ~= nil then if recipients[name] then
if m.subject == "" then m.subject = "(No subject)" end minetest.chat_send_player(name, mail_alert)
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))
end end
end end

6
api.md
View File

@ -35,12 +35,13 @@ mail = {
## Sending mail ## Sending mail
Old variant (pre-1.1) Old variant (pre-1.1)
```lua ```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+) New variant (1.1+)
```lua ```lua
mail.send({ local error = mail.send({
from = "sender name", from = "sender name",
to = "destination name", to = "destination name",
cc = "carbon copy", cc = "carbon copy",
@ -48,6 +49,7 @@ mail.send({
subject = "subject line", subject = "subject line",
body = "mail body" body = "mail body"
}) })
-- error will contain an error message if mail couldn't be delivered, otherwise nil
``` ```
# Hooks # Hooks

View File

@ -464,7 +464,7 @@ function mail.handle_receivefields(player, formname, fields)
elseif formname == "mail:compose" then elseif formname == "mail:compose" then
local name = player:get_player_name() local name = player:get_player_name()
if fields.send then if fields.send then
mail.send({ local error = mail.send({
from = name, from = name,
to = fields.to, to = fields.to,
cc = fields.cc, cc = fields.cc,
@ -472,6 +472,11 @@ function mail.handle_receivefields(player, formname, fields)
subject = fields.subject, subject = fields.subject,
body = fields.body, body = fields.body,
}) })
if error then
minetest.chat_send_player(name, error)
return
end
local contacts = mail.getContacts(name) local contacts = mail.getContacts(name)
local recipients = mail.parse_player_list(fields.to) local recipients = mail.parse_player_list(fields.to)
local changed = false local changed = false

View File

@ -1,3 +1,3 @@
name = mail name = mail
description = ingame mail-system description = ingame mail-system
optional_depends = unified_inventory,default,mtt optional_depends = canonical_name,default,mtt,unified_inventory

View File

@ -1,18 +1,21 @@
local has_canonical_name = minetest.get_modpath("canonical_name")
--[[ --[[
return the field normalized (comma separated, single space) return the field normalized (comma separated, single space)
and add individual player names to recipient list 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) local order = mail.parse_player_list(field)
for _, c in ipairs(order) do for _, player_name in ipairs(order) do
if recipients[string.lower(c)] == nil then if not minetest.player_exists(player_name) then
recipients[string.lower(c)] = c undeliverable[player_name] = true
else
recipients[player_name] = true
end end
end end
return mail.concat_player_list(order) return mail.concat_player_list(order)
end end
function mail.parse_player_list(field) function mail.parse_player_list(field)
if not field then if not field then
return {} return {}
@ -24,10 +27,15 @@ function mail.parse_player_list(field)
-- get individual players -- get individual players
local player_set = {} local player_set = {}
local order = {} local order = {}
field:gsub(pattern, function(c) field:gsub(pattern, function(player_name)
if player_set[string.lower(c)] == nil then local lower = string.lower(player_name)
player_set[string.lower(c)] = c if not player_set[lower] then
order[#order+1] = c 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
end) end)
@ -44,15 +52,14 @@ function mail.player_in_list(name, list)
if type(list) == "string" then if type(list) == "string" then
list = mail.parse_player_list(list) list = mail.parse_player_list(list)
end end
for _, c in pairs(list) do for _, player_name in pairs(list) do
if name == c then if name == player_name then
return true return true
end end
end end
return false return false
end end
function mail.ensure_new_format(message, name) function mail.ensure_new_format(message, name)
if message.to == nil then if message.to == nil then
message.to = name message.to = name