Skip to content

Commit

Permalink
Implement non-player recipients
Browse files Browse the repository at this point in the history
  • Loading branch information
y5nw committed Jan 13, 2024
1 parent 721d882 commit 3046f46
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 57 deletions.
47 changes: 14 additions & 33 deletions api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ function mail.register_on_receive(func)
mail.registered_on_receives[#mail.registered_on_receives + 1] = func
end

mail.registered_recipient_handlers = {}
function mail.register_recipient_handler(func)
table.insert(mail.registered_recipient_handlers, func)
end

function mail.send(m)
if type(m.from) ~= "string" then return false, "'from' is not a string" end
if type(m.to or "") ~= "string" then return false, "'to' is not a string" end
Expand All @@ -25,22 +30,22 @@ function mail.send(m)
local recipients = {}
local undeliverable = {}
m.to = mail.concat_player_list(mail.extractMaillists(m.to, m.from))
m.to = mail.normalize_players_and_add_recipients(m.to, recipients, undeliverable)
m.to = mail.normalize_players_and_add_recipients(m.from, m.to, recipients, undeliverable)
if m.cc then
m.cc = mail.concat_player_list(mail.extractMaillists(m.cc, m.from))
m.cc = mail.normalize_players_and_add_recipients(m.cc, recipients, undeliverable)
m.cc = mail.normalize_players_and_add_recipients(mail.from, m.cc, recipients, undeliverable)
end
if m.bcc then
m.bcc = mail.concat_player_list(mail.extractMaillists(m.bcc, m.from))
m.bcc = mail.normalize_players_and_add_recipients(m.bcc, recipients, undeliverable)
m.bcc = mail.normalize_players_and_add_recipients(m.from, 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 .. '"'
local undeliverable_reason = {S("The mail could not be sent:")}
for _, reason in pairs(undeliverable) do
table.insert(undeliverable_reason, reason)
end
return false, f("recipients %s don't exist; cannot send mail.", table.concat(undeliverable_names, ", "))
return false, table.concat(undeliverable_reason, "\n")
end

local extra = {}
Expand Down Expand Up @@ -85,32 +90,8 @@ function mail.send(m)
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 = S("You have a new message from @1! Subject: @2", m.from, m.subject) ..
"\n" .. S("To view it, type /mail")
local inventory_alert = S("You could also use the button in your inventory.")
for _, player in ipairs(minetest.get_connected_players()) do
local name = player:get_player_name()
if recipients[name] then
if mail.get_setting(name, "chat_notifications") == true then
minetest.chat_send_player(name, mail_alert)
if minetest.get_modpath("unified_inventory") or minetest.get_modpath("sfinv_buttons") then
minetest.chat_send_player(name, inventory_alert)
end
end
if mail.get_setting(name, "sound_notifications") == true then
minetest.sound_play("mail_notif", {to_player=name})
end
local receiver_entry = mail.get_storage_entry(name)
local receiver_messages = receiver_entry.inbox
mail.hud_update(name, receiver_messages)
end
for _, deliver in pairs(recipients) do
deliver(msg)
end

for i=1, #mail.registered_on_receives do
Expand Down
16 changes: 16 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ mail.register_on_receive(function(m)
end)
```

# Recipient handler
Recipient handlers are registered using

```lua
mail.register_recipient_handler(function(sender, name)
end)
```

where `name` is the name of a single recipient.

The recipient handler should return
* `nil` if the handler does not handle messages sent to the particular recipient,
* `true, player` (where `player` is a string or a list of strings) if the mail should be redirected to `player`,
* `true, deliver` if the mail should be delivered by calling `deliver` with the message, or
* `false, reason` (where `reason` is optional or, if provided, a string) if the recipient explicitly rejects the mail.

# Internals

mod-storage entry for a player (indexed by playername and serialized with json):
Expand Down
1 change: 1 addition & 0 deletions init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ dofile(MP .. "/storage.lua")
dofile(MP .. "/api.lua")
dofile(MP .. "/gui.lua")
dofile(MP .. "/onjoin.lua")
dofile(MP .. "/player_recipients.lua")
-- sub directories
dofile(MP .. "/ui/init.lua")

Expand Down
47 changes: 47 additions & 0 deletions player_recipients.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
local S = minetest.get_translator("mail")
local has_canonical_name = minetest.get_modpath("canonical_name")

local function deliver_mail_to_player(name, msg)
-- add to inbox
local entry = mail.get_storage_entry(name)
table.insert(entry.inbox, msg)
mail.set_storage_entry(name, entry)

-- notify recipients that happen to be online
local mail_alert = S("You have a new message from @1! Subject: @2", msg.from, msg.subject) ..
"\n" .. S("To view it, type /mail")
local inventory_alert = S("You could also use the button in your inventory.")
local player = minetest.get_player_by_name(name)
if player then
if mail.get_setting(name, "chat_notifications") == true then
minetest.chat_send_player(name, mail_alert)
if minetest.get_modpath("unified_inventory") or minetest.get_modpath("sfinv_buttons") then
minetest.chat_send_player(name, inventory_alert)
end
end
if mail.get_setting(name, "sound_notifications") == true then
minetest.sound_play("mail_notif", {to_player=name})
end
local receiver_entry = mail.get_storage_entry(name)
local receiver_messages = receiver_entry.inbox
mail.hud_update(name, receiver_messages)
end
end

mail.register_recipient_handler(function(_, pname)
if not minetest.player_exists(pname) then
return nil
end
return true, function(mail)
deliver_mail_to_player(pname, mail)
end
end)

if has_canonical_name then
mail.register_recipient_handler(function(_, name)
local realname = canonical_name.get(name)
if realname then
return true, realname
end
end)
end
60 changes: 38 additions & 22 deletions util/normalize.lua
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
local has_canonical_name = minetest.get_modpath("canonical_name")
local S = minetest.get_translator("mail")

local function recursive_expand_recipient_names(sender, list, recipients, undeliverable)
for _, name in ipairs(list) do
if not (recipients[name] or undeliverable[name]) then
local succ, value
for _, handler in ipairs(mail.registered_recipient_handlers) do
succ, value = handler(sender, name)
if succ ~= nil then
break
end
end
local vtp = type(value)
if succ then
if vtp == "string" then
recursive_expand_recipient_names(sender, {value}, recipients, undeliverable)
elseif vtp == "table" then
recursive_expand_recipient_names(sender, value, recipients, undeliverable)
elseif vtp == "function" then
recipients[name] = value
else
undeliverable[name] = S("The method of delivery to @1 is invalid.", name)
end
elseif succ == nil then
undeliverable[name] = S("The recipient @1 could not be identified.", name)
else
local reason = tostring(value) or S("@1 rejected your mail.", name)
undeliverable[name] = reason
end
end
end
end

--[[
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, undeliverable)
function mail.normalize_players_and_add_recipients(sender, field, recipients, undeliverable)
local order = mail.parse_player_list(field)
for _, recipient_name in ipairs(order) do
if not minetest.player_exists(recipient_name) then
undeliverable[recipient_name] = true
else
recipients[recipient_name] = true
end
end
recursive_expand_recipient_names(sender, order, recipients, undeliverable)
return mail.concat_player_list(order)
end

Expand All @@ -21,23 +46,14 @@ function mail.parse_player_list(field)
return {}
end

local separator = ", "
local separator = ",%s"
local pattern = "([^" .. separator .. "]+)"

-- get individual players
local player_set = {}
local order = {}
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)
for name in field:gmatch(pattern) do
table.insert(order, name)
end

return order
end
Expand Down
4 changes: 2 additions & 2 deletions util/normalize.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
mtt.register("util/normalize_players_and_add_recipients", function(callback)
local recipients = {}
local undeliverable = {}
local to = mail.normalize_players_and_add_recipients("player1,player2", recipients, undeliverable)
local to = mail.normalize_players_and_add_recipients("sender", "player1,player2", recipients, undeliverable)

assert(to == "player1, player2")
assert(not next(undeliverable))
assert(recipients["player1"])
assert(recipients["player2"])
callback()
end)
end)

0 comments on commit 3046f46

Please sign in to comment.