From 3046f46414d68b350dcd7311509fc4ac819ab95a Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:07:30 +0100 Subject: [PATCH] Implement non-player recipients --- api.lua | 47 ++++++++++---------------------- api.md | 16 +++++++++++ init.lua | 1 + player_recipients.lua | 47 ++++++++++++++++++++++++++++++++ util/normalize.lua | 60 ++++++++++++++++++++++++++--------------- util/normalize.spec.lua | 4 +-- 6 files changed, 118 insertions(+), 57 deletions(-) create mode 100644 player_recipients.lua diff --git a/api.lua b/api.lua index 9533084..53bed29 100644 --- a/api.lua +++ b/api.lua @@ -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 @@ -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 = {} @@ -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 diff --git a/api.md b/api.md index 7ef2ea9..d2a6bc7 100644 --- a/api.md +++ b/api.md @@ -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): diff --git a/init.lua b/init.lua index 889031c..b921ac4 100644 --- a/init.lua +++ b/init.lua @@ -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") diff --git a/player_recipients.lua b/player_recipients.lua new file mode 100644 index 0000000..604ca6b --- /dev/null +++ b/player_recipients.lua @@ -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 diff --git a/util/normalize.lua b/util/normalize.lua index b817068..0be3934 100644 --- a/util/normalize.lua +++ b/util/normalize.lua @@ -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 @@ -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 diff --git a/util/normalize.spec.lua b/util/normalize.spec.lua index 88628ad..b9caa0f 100644 --- a/util/normalize.spec.lua +++ b/util/normalize.spec.lua @@ -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) \ No newline at end of file +end)