From ca88374fbdea0bc6aa8cbaa0906cab74a588ca91 Mon Sep 17 00:00:00 2001 From: NatureFreshMilk Date: Mon, 16 Sep 2019 08:06:54 +0200 Subject: [PATCH] separated from webmail mod --- .luacheckrc | 23 ++++ LICENSE | 13 ++ README.md | 38 ++++++ api.lua | 67 +++++++++++ api.md | 60 ++++++++++ attachment.lua | 27 +++++ chatcommands.lua | 6 + depends.txt | 3 + gui.lua | 250 +++++++++++++++++++++++++++++++++++++++ hud.lua | 59 +++++++++ init.lua | 55 +++++++++ migrate.lua | 24 ++++ onjoin.lua | 19 +++ storage.lua | 32 +++++ tan.lua | 16 +++ textures/email_mail.png | Bin 0 -> 270 bytes textures/mail_button.png | Bin 0 -> 2916 bytes util/channel.lua | 93 +++++++++++++++ webmail.lua | 156 ++++++++++++++++++++++++ 19 files changed, 941 insertions(+) create mode 100644 .luacheckrc create mode 100644 LICENSE create mode 100644 README.md create mode 100644 api.lua create mode 100644 api.md create mode 100644 attachment.lua create mode 100644 chatcommands.lua create mode 100644 depends.txt create mode 100644 gui.lua create mode 100644 hud.lua create mode 100644 init.lua create mode 100644 migrate.lua create mode 100644 onjoin.lua create mode 100644 storage.lua create mode 100644 tan.lua create mode 100644 textures/email_mail.png create mode 100644 textures/mail_button.png create mode 100644 util/channel.lua create mode 100644 webmail.lua diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..ec6a1d2 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,23 @@ +unused_args = false +allow_defined_top = true + +globals = { + "mail", +} + +read_globals = { + -- Stdlib + string = {fields = {"split"}}, + table = {fields = {"copy", "getn"}}, + + -- Minetest + "minetest", + "vector", "ItemStack", + "dump", + + -- Deps + "unified_inventory", "default", + + -- optional mods + "xban" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..caa7028 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + +The file textures/mail_button.png was created by bas080 and is licensed under the WTFPL. + +Webmail component: +WTFPL + +All other files: + +Copyright (c) 2016 Carter Kolwey ("Cheapie Systems") +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and/or any associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..37ce2b0 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +Mail mod for Minetest +====== + +This is a fork of cheapies mail mod + +It adds a mail-system that allows players to send each other messages in-game and via webmail (optional) + +# Screenshots + +Ingame mail +![](pics/ingame.png?raw=true) + +# Installation + +## In-game mail mod + +Install it like any other mod: copy the directory `mail_mod` to your "worldmods" folder + +## Webmail + +See: https://github.com/thomasrudin-mt/mail + +# Commands/Howto + +To access your mail click on the inventory mail button or use the "/mail" command +Mails can be deleted, marked as read or unread, replied to and forwarded to another player + +# Dependencies +* None + + +# License + +See the "LICENSE" file + +# Old/Historic stuff +* Old forum topic: https://forum.minetest.net/viewtopic.php?t=14464 +* Old mod: https://cheapiesystems.com/git/mail/ diff --git a/api.lua b/api.lua new file mode 100644 index 0000000..1156ea3 --- /dev/null +++ b/api.lua @@ -0,0 +1,67 @@ +-- see: mail.md + +mail.registered_on_receives = {} +function mail.register_on_receive(func) + mail.registered_on_receives[#mail.registered_on_receives + 1] = func +end + +mail.receive_mail_message = "You have a new message from %s! Subject: %s\nTo view it, type /mail" +mail.read_later_message = "You can read your messages later by using the /mail command" + +--[[ +mail sending function, can be invoked with one object argument (new api) or +all 4 parameters (old compat version) +see: "Mail format" api.md +--]] +function mail.send(sender, receiver, subject, body) + local m + if receiver == nil and subject == nil and body == nil then + -- new format (one object param) + m = sender + + else + -- old format + -- create mail from params + + m = {} + m.sender = sender + m.receiver = receiver + m.subject = subject + m.body = body + + end + + m.unread = true + + if not m.time then + -- add timestamp + m.time = os.time() + end + + + minetest.log("action", "[mail] '" .. m.sender .. "' sends mail to '" .. m.receiver .. + "' with subject '" .. m.subject .. "' and body: '" .. m.body .. "'") + + local messages = mail.getMessages(m.receiver) + + table.insert(messages, 1, m) + mail.setMessages(m.receiver, messages) + + for _, player in ipairs(minetest.get_connected_players()) do + local name = player:get_player_name() + if name == m.receiver 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(m.receiver, + string.format(mail.receive_mail_message, m.sender, m.subject)) + end + end + + for i=1, #mail.registered_on_receives do + if mail.registered_on_receives[i](m) then + break + end + end +end diff --git a/api.md b/api.md new file mode 100644 index 0000000..dec0a70 --- /dev/null +++ b/api.md @@ -0,0 +1,60 @@ + +# Mail format +The mail format in the api hooks + +```lua +mail = { + sender = "source name", + receiver = "destination name", + subject = "subject line", + body = "mail body", + -- 8 attachments max + attachments = {"default:stone 99", "default:gold_ingot 99"} +} +``` + +## Sending mail +Old variant (pre-1.1) +```lua +mail.send("source name", "destination name", "subject line", "mail body") +``` + +New variant (1.1+) +```lua +mail.send({ + sender = "source name", + receiver = "destination name", + subject = "subject line", + body = "mail body" +}) +``` + +# Hooks +On-receive mail hook: + +```lua +mail.register_on_receive(function(m) + -- "m" is an object in the form: "Mail format" +end) +``` + +# internal mail format (on-disk) +The mail format on-disk + +> (worldfolder)/mails/(playername).json + +```json +[{ + "unread": true, + "sender": "sender name", + "receiver": "receiver name", + "subject": "subject name", + "body": "main\nmultiline\nbody", + "time": 1551258349, + "attachments": [ + "default:stone 99", + "default:gold_ingot 99" + ] +}] + +``` diff --git a/attachment.lua b/attachment.lua new file mode 100644 index 0000000..efb76d9 --- /dev/null +++ b/attachment.lua @@ -0,0 +1,27 @@ + +local invmap = {} + + +mail.getAttachmentInventory = function(playername) + return invmap[playername] +end + +mail.getAttachmentInventoryName = function(playername) + return "mail:" .. playername +end + + +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + local inv = minetest.create_detached_inventory(mail.getAttachmentInventoryName(name), {}) + + invmap[name] = inv +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + invmap[name] = nil + if minetest.remove_detached_inventory then + minetest.remove_detached_inventory(mail.getAttachmentInventoryName(name)) + end +end) diff --git a/chatcommands.lua b/chatcommands.lua new file mode 100644 index 0000000..044d246 --- /dev/null +++ b/chatcommands.lua @@ -0,0 +1,6 @@ +minetest.register_chatcommand("mail",{ + description = "Open the mail interface", + func = function(name) + mail.show_inbox(name) + end +}) diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..709b3a7 --- /dev/null +++ b/depends.txt @@ -0,0 +1,3 @@ +unified_inventory? +default? +xban2? \ No newline at end of file diff --git a/gui.lua b/gui.lua new file mode 100644 index 0000000..e165517 --- /dev/null +++ b/gui.lua @@ -0,0 +1,250 @@ +selected_message_idxs = {} + +local theme +if minetest.get_modpath("default") then + theme = default.gui_bg .. default.gui_bg_img +else + theme = "" +end + +mail.inbox_formspec = "size[8,9;]" .. theme .. [[ + button_exit[7.5,0;0.5,0.5;quit;X] + button[6,1;2,0.5;new;New Message] + button[6,2;2,0.5;read;Read] + button[6,3;2,0.5;reply;Reply] + button[6,4;2,0.5;forward;Forward] + button[6,5;2,0.5;delete;Delete] + button[6,6;2,0.5;markread;Mark Read] + button[6,7;2,0.5;markunread;Mark Unread] + button[6,8;2,0.5;about;About] + tablecolumns[color;text;text] + table[0,0;5.75,9;messages;#999,From,Subject]] + + +function mail.show_about(name) + local formspec = [[ + size[8,5;] + button[7.5,0;0.5,0.5;back;X] + label[0,0;Mail] + label[0,0.5;By cheapie] + label[0,1;http://github.com/cheapie/mail] + label[0,1.5;See LICENSE file for license information] + label[0,2.5;NOTE: Communication using this system] + label[0,3;is NOT guaranteed to be private!] + label[0,3.5;Admins are able to view the messages] + label[0,4;of any player.] + ]] .. theme + + minetest.show_formspec(name, "mail:about", formspec) +end + +function mail.show_inbox(name) + local formspec = { mail.inbox_formspec } + local messages = mail.getMessages(name) + + if messages[1] then + for idx, message in ipairs(messages) do + if message.unread then + formspec[#formspec + 1] = ",#FFD700" + else + formspec[#formspec + 1] = "," + end + formspec[#formspec + 1] = "," + formspec[#formspec + 1] = minetest.formspec_escape(message.sender) + formspec[#formspec + 1] = "," + if message.subject ~= "" then + if string.len(message.subject) > 30 then + formspec[#formspec + 1] = + minetest.formspec_escape(string.sub(message.subject, 1, 27)) + formspec[#formspec + 1] = "..." + else + formspec[#formspec + 1] = minetest.formspec_escape(message.subject) + end + else + formspec[#formspec + 1] = "(No subject)" + end + end + if selected_message_idxs[name] then + formspec[#formspec + 1] = ";" + formspec[#formspec + 1] = tostring(selected_message_idxs[name] + 1) + end + formspec[#formspec + 1] = "]" + else + formspec[#formspec + 1] = "]label[2,4.5;No mail]" + end + minetest.show_formspec(name, "mail:inbox", table.concat(formspec, "")) +end + +function mail.show_message(name, msgnumber) + local messages = mail.getMessages(name) + local message = messages[msgnumber] + local formspec = [[ + size[8,7.2] + button[7,0;1,0.5;back;X] + label[0,0;From: %s] + label[0,0.5;Subject: %s] + textarea[0.25,1.25;8,6.25;body;;%s] + button[1,6.7;2,1;reply;Reply] + button[3,6.7;2,1;forward;Forward] + button[5,6.7;2,1;delete;Delete] + ]] .. theme + + local sender = minetest.formspec_escape(message.sender) + local subject = minetest.formspec_escape(message.subject) + local body = minetest.formspec_escape(message.body) + formspec = string.format(formspec, sender, subject, body) + + minetest.show_formspec(name,"mail:message",formspec) +end + +function mail.show_compose(name, defaulttgt, defaultsubj, defaultbody) + local formspec = [[ + size[8,7.2] + field[0.25,0.5;4,1;to;To:;%s] + field[0.25,1.7;8,1;subject;Subject:;%s] + textarea[0.25,2.4;8,5;body;;%s] + button[0.5,6.7;3,1;cancel;Cancel] + button[7,0;1,0.5;cancel;X] + button[4.5,6.7;3,1;send;Send] + ]] .. theme + + formspec = string.format(formspec, + minetest.formspec_escape(defaulttgt), + minetest.formspec_escape(defaultsubj), + minetest.formspec_escape(defaultbody)) + + minetest.show_formspec(name, "mail:compose", formspec) +end + +function mail.handle_receivefields(player, formname, fields) + if formname == "" and fields and fields.quit and minetest.get_modpath("unified_inventory") then + unified_inventory.set_inventory_formspec(player, "craft") + end + + if formname == "mail:about" then + minetest.after(0.5, function() + mail.show_inbox(player:get_player_name()) + end) + + elseif formname == "mail:inbox" then + local name = player:get_player_name() + local messages = mail.getMessages(name) + + if fields.messages then + local evt = minetest.explode_table_event(fields.messages) + selected_message_idxs[name] = evt.row - 1 + if evt.type == "DCL" and messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = false + mail.show_message(name, selected_message_idxs[name]) + end + mail.setMessages(name, messages) + return true + end + if fields.read then + if messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = false + mail.show_message(name, selected_message_idxs[name]) + end + + elseif fields.delete then + if messages[selected_message_idxs[name]] then + table.remove(messages, selected_message_idxs[name]) + end + + mail.show_inbox(name) + elseif fields.reply and messages[selected_message_idxs[name]] then + local message = messages[selected_message_idxs[name]] + local replyfooter = "Type your reply here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, message.sender, "Re: "..message.subject,replyfooter) + + elseif fields.forward and messages[selected_message_idxs[name]] then + local message = messages[selected_message_idxs[name]] + local fwfooter = "Type your message here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, "", "Fw: "..message.subject, fwfooter) + + elseif fields.markread then + if messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = false + end + mail.show_inbox(name) + + elseif fields.markunread then + if messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = true + end + mail.show_inbox(name) + + elseif fields.new then + mail.show_compose(name,"","","Type your message here.") + + elseif fields.quit then + if minetest.get_modpath("unified_inventory") then + unified_inventory.set_inventory_formspec(player, "craft") + end + + elseif fields.about then + mail.show_about(name) + + end + + mail.setMessages(name, messages) + return true + elseif formname == "mail:message" then + local name = player:get_player_name() + local messages = mail.getMessages(name) + + if fields.back then + mail.show_inbox(name) + elseif fields.reply then + local message = messages[selected_message_idxs[name]] + local replyfooter = "Type your reply here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, message.sender, "Re: "..message.subject, replyfooter) + elseif fields.forward then + local message = messages[selected_message_idxs[name]] + local fwfooter = "Type your message here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, "", "Fw: "..message.subject, fwfooter) + elseif fields.delete then + if messages[selected_message_idxs[name]] then + table.remove(messages,selected_message_idxs[name]) + end + mail.show_inbox(name) + end + + mail.setMessages(name, messages) + return true + elseif formname == "mail:compose" then + if fields.send then + mail.send({ + src = player:get_player_name(), + dst = fields.to, + subject = fields.subject, + body = fields.body + }) + end + minetest.after(0.5, function() + mail.show_inbox(player:get_player_name()) + end) + return true + + elseif fields.mail then + mail.show_inbox(player:get_player_name()) + else + return false + end +end + +minetest.register_on_player_receive_fields(mail.handle_receivefields) + + +if minetest.get_modpath("unified_inventory") then + mail.receive_mail_message = mail.receive_mail_message .. + " or use the mail button in the inventory" + mail.read_later_message = mail.read_later_message .. + " or by using the mail button in the inventory" + + unified_inventory.register_button("mail", { + type = "image", + image = "mail_button.png", + tooltip = "Mail" + }) +end diff --git a/hud.lua b/hud.lua new file mode 100644 index 0000000..bb07ca2 --- /dev/null +++ b/hud.lua @@ -0,0 +1,59 @@ + +local huddata = {} + +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + local data = {} + + data.imageid = player:hud_add({ + hud_elem_type = "image", + name = "MailIcon", + position = {x=0.52, y=0.52}, + text="", + scale = {x=1,y=1}, + alignment = {x=0.5, y=0.5}, + }) + + data.textid = player:hud_add({ + hud_elem_type = "text", + name = "MailText", + position = {x=0.55, y=0.52}, + text= "", + scale = {x=1,y=1}, + alignment = {x=0.5, y=0.5}, + }) + + + huddata[name] = data +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + huddata[name] = nil +end) + + +mail.hud_update = function(playername, messages) + local data = huddata[playername] + local player = minetest.get_player_by_name(playername) + + if not data or not player then + return + end + + local unreadcount = 0 + for _, message in ipairs(messages) do + if message.unread then + unreadcount = unreadcount + 1 + end + end + + if unreadcount == 0 then + player:hud_change(data.imageid, "text", "") + player:hud_change(data.textid, "text", "") + else + player:hud_change(data.imageid, "text", "email_mail.png") + player:hud_change(data.textid, "text", unreadcount .. " /mail") + end + +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..c5c08e2 --- /dev/null +++ b/init.lua @@ -0,0 +1,55 @@ +mail = { + -- mail directory + maildir = minetest.get_worldpath().."/mails", + + -- allow item/node attachments + allow_attachments = minetest.settings:get("mail.allow_attachments") == "true", + + webmail = { + -- disallow banned players in the webmail interface + disallow_banned_players = minetest.settings:get("webmail.disallow_banned_players") == "true", + + -- url and key to the webmail server + url = minetest.settings:get("webmail.url"), + key = minetest.settings:get("webmail.key") + }, + + tan = {} +} + + +local MP = minetest.get_modpath(minetest.get_current_modname()) +dofile(MP .. "/chatcommands.lua") +dofile(MP .. "/migrate.lua") +dofile(MP .. "/attachment.lua") +dofile(MP .. "/hud.lua") +dofile(MP .. "/storage.lua") +dofile(MP .. "/api.lua") +dofile(MP .. "/gui.lua") +dofile(MP .. "/onjoin.lua") + +-- optional webmail stuff below + +--[[ minetest.conf +secure.http_mods = mail +webmail.url = http://127.0.0.1:8080 +webmail.key = myserverkey +--]] + +local http = minetest.request_http_api() + +if http then + local webmail_url = mail.webmail.url + local webmail_key = mail.webmail.key + + if not webmail_url then error("webmail.url is not defined") end + if not webmail_key then error("webmail.key is not defined") end + + print("[mail] loading webmail-component with endpoint: " .. webmail_url) + dofile(MP .. "/tan.lua") + dofile(MP .. "/webmail.lua") + mail.webmail_init(http, webmail_url, webmail_key) +end + +-- migrate storage +mail.migrate() diff --git a/migrate.lua b/migrate.lua new file mode 100644 index 0000000..5a14478 --- /dev/null +++ b/migrate.lua @@ -0,0 +1,24 @@ + +-- migrate from mail.db to player-file-based mailbox + +mail.migrate = function() + + local file = io.open(minetest.get_worldpath().."/mail.db", "r") + if file then + print("[mail] migrating to new per-player storage") + minetest.mkdir(mail.maildir) + + local data = file:read("*a") + local oldmails = minetest.deserialize(data) + file:close() + + for name, oldmessages in pairs(oldmails) do + mail.setMessages(name, oldmessages) + end + + -- rename file + print("[mail] migration done, renaming old mail.db") + os.rename(minetest.get_worldpath().."/mail.db", minetest.get_worldpath().."/mail.db.old") + end + +end diff --git a/onjoin.lua b/onjoin.lua new file mode 100644 index 0000000..79ad287 --- /dev/null +++ b/onjoin.lua @@ -0,0 +1,19 @@ +minetest.register_on_joinplayer(function(player) + minetest.after(2, function(name) + local messages = mail.getMessages(name) + + local unreadcount = 0 + + for _, message in pairs(messages) do + if message.unread then + unreadcount = unreadcount + 1 + end + end + + if unreadcount > 0 then + minetest.chat_send_player(name, + "(" .. unreadcount .. ") You have mail! Type /mail to read") + + end + end, player:get_player_name()) +end) diff --git a/storage.lua b/storage.lua new file mode 100644 index 0000000..8f50a74 --- /dev/null +++ b/storage.lua @@ -0,0 +1,32 @@ + +-- TODO: maybe local cache? + +function getMailFile(playername) + local saneplayername = string.gsub(playername, "[.|/]", "") + return mail.maildir .. "/" .. saneplayername .. ".json" +end + +mail.getMessages = function(playername) + local file = io.open(getMailFile(playername), "r") + local messages = {} + if file then + local json = file:read("*a") + messages = minetest.parse_json(json or "[]") or {} + mail.hud_update(playername, messages) + file:close() + end + + return messages +end + +mail.setMessages = function(playername, messages) + local file = io.open(getMailFile(playername),"w") + local json = minetest.write_json(messages) + if file and file:write(json) and file:close() then + mail.hud_update(playername, messages) + return true + else + minetest.log("error","[mail] Save failed - messages may be lost!") + return false + end +end diff --git a/tan.lua b/tan.lua new file mode 100644 index 0000000..95d116c --- /dev/null +++ b/tan.lua @@ -0,0 +1,16 @@ + + +minetest.register_chatcommand("webmail_tan", { + description = "generates a tan (temporary access number) for the webmail access", + func = function(name) + local tan = "" .. math.random(1000, 9999) + mail.tan[name] = tan + + return true, "Your tan is " .. tan .. ", it will expire upon leaving the game" + end +}) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + mail.tan[name] = nil +end) diff --git a/textures/email_mail.png b/textures/email_mail.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e5553de1bd6ce724fffdde42e8fbeb4a8067c1 GIT binary patch literal 270 zcmeAS@N?(olHy`uVBq!ia0vp^qChOj!3HFa+MYK6DYhhUcNd2LAh=-f^2tCE&H|6f zVg?4j!ywFfJby(BP>{XE)7O>#E*n3uByWTrTOd$qwx^3@h{frvQylqP6?j}EAK!BI zfAH7;C}%`PV__((%+_P?ADJsEvc@fAW>vYoKG{S5i28+#>I`we9fWvGq9f)VHi(^a z{^<@yrZ=rTUYR0m8kUQTGR|jQy4*HG_RgoHt8zMRHJ3&(%kK$hPq@Xt`EHc4o6YN~ zhS7&&gf<_YY8EFpd8t*zyzl%=->tAdFXh1ct#8tW?YUk1fBp({>G$D`DHDzWI+MZE L)z4*}Q$iB}$xB~d literal 0 HcmV?d00001 diff --git a/textures/mail_button.png b/textures/mail_button.png new file mode 100644 index 0000000000000000000000000000000000000000..8adf3eaa60ae226fc91bb3b11ec77f5e53c66d78 GIT binary patch literal 2916 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 O0000 " .. data.params.receiver) + + mail.send(data.params) + + channel.send({ + method = data.method, + id = data.id, + result = { + success = true + } + }) +end + +-- get player messages request from webmail +local function get_player_messages_handler(data) + local messages = mail.getMessages(data.params.playername) + channel.send({ + method = data.method, + id = data.id, + result = messages + }) +end + +-- remove mail +local function delete_mail_handler(data) + local index = data.params.index + local playername = data.params.playername + + local messages = mail.getMessages(playername) + if messages[index] then + table.remove(messages, index) + end + mail.setMessages(playername, messages) + -- TODO: check subject + + channel.send({ + method = data.method, + id = data.id, + result = { success = true } + }) +end + +-- mark mail as read +local function mark_mail_read_handler(data) + local index = data.params.index + local playername = data.params.playername + local read = data.params.read + + local messages = mail.getMessages(playername) + + if messages[index] then + messages[index].unread = not read + end + mail.setMessages(playername, messages) + -- TODO: check subject + + channel.send({ + method = data.method, + id = data.id, + result = { success = true } + }) +end + +function mail.webmail_send_hook(m) + channel.send({ + type = "new-message", + data = m + }) +end +mail.register_on_receive(mail.webmail_send_hook) + +function mail.webmail_init(http, url, key) + channel = Channel(http, url .. "/api/minetest/channel", { + extra_headers = { "webmailkey: " .. key } + }) + + channel.receive(function(data) + if data.method == "auth" then + auth_handler(data) + + elseif data.method == "get-mails" then + get_player_messages_handler(data) + + elseif data.method == "mark-mail-read" then + mark_mail_read_handler(data) + + elseif data.method == "delete-mail" then + delete_mail_handler(data) + + elseif data.method == "send" then + send_handler(data) + + + end + end) +end