6 Copyright (C) 2019 Auke Kok <sofar@foo-projects.org>
8 Permission to use, copy, modify, and/or distribute this software for
9 any purpose with or without fee is hereby granted, provided that the
10 above copyright notice and this permission notice appear in all copies.
12 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
13 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
15 SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
17 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
18 OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 - messages are sent from player to another player.
27 - messages have "from", "to", "subject", and "content", and "unread" properties
28 - When a player sends a message, it is put in the global spool
29 - the global spool is in mod_storage and persistent
30 - periodically, the spool will attempt to deliver all messages to receivers
31 - if the receiver does not exist or is not online, the message stays in the spool
32 - if the message stays in the spool longer than <config>, the message will be
33 deleted. A new spool message, containing the old message in quoted form, will be
34 returned to the sender. These messages will not expire from the spool.
35 - if a player comes online, the spool attempts to deliver queued messages to the player
36 - if the player is online, the spool immediately delivers the message
37 - the player has their own mailbox, in player attributes/storage
49 "content" = <array of strings>,
66 Spool/mbox format: array of messages
70 local S
= minetest
.get_mod_storage()
73 -- helper functions for handling msgid
75 -- allocate a new msgid str
76 function telex
.msgid()
77 local msgid
= S
:get_int("msgid") + 1
78 S
:set_int("msgid", msgid
)
79 return "m" .. tostring(msgid
)
83 function telex
.get_msg(msgid
)
85 minetest
.log("action", "telex: get_msg() called with nil")
88 local msg
= S
:get_string(msgid
)
90 return telex
.decode(msg
)
92 minetest
.log("action", "telex: get_msg() unable to find " .. msgid
)
98 function telex
.save_msg(msgid
, msg
)
99 S
:set_string(msgid
, telex
.encode(msg
))
103 function telex
.remove_msg(msgid
)
104 S
:set_string(msgid
, "")
107 -- returns an array of strings
108 function telex
.list(player
)
109 local pmeta
= player
:get_meta()
110 local mbox
= telex
.decode(pmeta
:get_string("telex_mbox"))
112 for n
, msgid
in pairs(mbox
) do
113 local msg
= telex
.get_msg(msgid
)
115 if msg
.read == 1 then
118 list
[#list
+ 1] = unread
.. n
.. ": <" .. msg
.from
.. "> \"" .. msg
.subject
.. "\""
124 function telex
.get(player
, no
)
125 local pmeta
= player
:get_meta()
126 local mbox
= telex
.decode(pmeta
:get_string("telex_mbox"))
128 return { "You have no messages." }
131 local msgid
= mbox
[no
]
132 local msg
= telex
.get_msg(msgid
)
135 minetest
.log("action", "telex: get() unable to find msg no " .. no
)
136 return { "No such message exists." }
142 -- returns an array of strings
143 function telex
.read(player
, no
)
144 local pmeta
= player
:get_meta()
145 local mbox
= telex
.decode(pmeta
:get_string("telex_mbox"))
147 return { "You have no messages." }
149 local msgid
= mbox
[no
]
150 local msg
= telex
.get_msg(msgid
)
152 return { "No such message exists." }
156 "From: <" .. msg
.from
.. ">",
157 "Subject: " .. msg
.subject
,
160 for _
, v
in pairs(msg
.content
) do
161 table.insert(list
, v
)
164 -- mark as read and store
166 telex
.save_msg(msgid
, msg
)
168 pmeta
:set_string("telex_mbox", telex
.encode(mbox
))
173 -- returns array of strings
174 function telex
.delete(player
, no
)
175 local pmeta
= player
:get_meta()
176 local mbox
= telex
.decode(pmeta
:get_string("telex_mbox"))
178 return { "You have no messages." }
180 local msgid
= mbox
[no
]
181 local msg
= telex
.get_msg(msgid
)
183 return { "No such message exists." }
186 table.remove(mbox
, no
)
187 telex
.remove_msg(msgid
)
188 pmeta
:set_string("telex_mbox", telex
.encode(mbox
))
190 return { "Message deleted." }
193 -- external should use `send`, internal uses `deliver`
194 function telex
.send(msg
)
200 local msgid
= telex
.msgid()
201 telex
.save_msg(msgid
, msg
)
202 telex
.deliver(msgid
, msg
)
206 function telex
.deliver(msgid
, msg
)
209 msg
= telex
.get_msg(msgid
)
211 local player
= minetest
.get_player_by_name(msg
.to
)
213 -- remove age, no longer needed
215 telex
.save_msg(msgid
, msg
)
217 local pmeta
= player
:get_meta()
218 local mbox
= telex
.decode(pmeta
:get_string("telex_mbox"))
219 table.insert(mbox
, msgid
)
220 pmeta
:set_string("telex_mbox", telex
.encode(mbox
))
221 minetest
.chat_send_player(msg
.to
, "You have a new message from <" .. msg
.from
.. ">")
222 minetest
.log("action", "telex: delivered " .. msgid
.. " from <" .. msg
.from
.. "> to <" .. msg
.to
.. ">")
225 msg
.age
= 7 * 86400 -- 7 days max in spool
226 telex
.save_msg(msgid
, msg
)
227 local spool
= telex
.decode(S
:get_string("telex_spool"))
228 table.insert(spool
, msgid
)
229 S
:set_string("telex_spool", telex
.encode(spool
))
230 minetest
.log("action", "telex: spooled " .. msgid
.. " from <" .. msg
.from
.. "> to <" .. msg
.to
.. ">")
231 announce
.admins("spooled " .. msgid
.. " from <" .. msg
.from
.. "> to <" .. msg
.to
.. ">")
236 function telex
.retour(msgid
)
237 local msg
= telex
.get_msg(msgid
)
238 if msg
.from
== "MAILER-DEAMON" then
239 -- discard, we tried hard enough!
240 minetest
.log("action", "telex: discarded " .. msgid
.. " from <" .. msg
.from
.. "> to <" .. msg
.to
.. ">")
241 announce
.admins("discarded " .. msgid
.. "from <" .. msg
.from
.. "> to <" .. msg
.to
.. ">")
246 local from
= msg
.from
248 msg
.from
= "MAILER-DAEMON"
249 msg
.subject
= "UNDELIVERABLE: " .. msg
.subject
250 msg
.age
= 86400 * 30 -- return mail for 30 days max, then discard.
251 table.insert(msg
.content
, 1, "Your message to <" .. to
.. "> was unable to be delivered.")
252 table.insert(msg
.content
, 2, "================== ORIGINAL MESSAGE BELOW ================")
254 -- remove the old msg, it's no longer in spool
255 telex
.remove_msg(msgid
)
257 -- allocate a new ID for the return msg
258 local msgid2
= telex
.msgid()
259 telex
.save_msg(msgid2
, msg
)
260 telex
.deliver(msgid2
, msg
)
262 minetest
.log("action", "telex: returned " .. msgid
.. " -> " .. msgid2
.. " from <" .. from
.. "> back to <" .. to
.. ">")
263 announce
.admins("returned " .. msgid
.. " -> " .. msgid2
.. " from <" .. from
.. "> back to <" .. to
.. ">")
266 -- returns message/mbox --FIXME compress/decompress
267 function telex
.decode(digest
)
268 if not digest
or digest
== "" then
272 local tbl
= minetest
.parse_json(digest
)
280 function telex
.encode(message_or_mbox
)
281 return minetest
.write_json(message_or_mbox
)
284 minetest
.register_on_joinplayer(function(player
)
286 local pmeta
= player
:get_meta()
287 local mbox
= telex
.decode(pmeta
:get_string("telex_mbox"))
289 for k
, v
in pairs(mbox
) do
290 if type(v
) == "table" then
291 local msgid
= telex
.msgid()
292 telex
.save_msg(msgid
, v
)
298 pmeta
:set_string("telex_mbox", telex
.encode(mbox
))
301 -- check the spool for messages for `player`
302 local spool
= telex
.decode(S
:get_string("telex_spool"))
303 local name
= player
:get_player_name()
305 -- deliver items for this player
308 for k
, msgid
in pairs(spool
) do
309 local msg
= telex
.get_msg(msgid
)
310 if msg
.to
== name
then
312 del
[#del
+ 1] = msgid
316 -- remove items from spool in reverse order
317 for i
= #old
, 1, -1 do
318 table.remove(spool
, old
[i
])
320 S
:set_string("telex_spool", telex
.encode(spool
))
322 -- now deliver them to player
323 for _
, msgid
in pairs(del
) do
327 -- notify if there wasn't anything new but the player still had some unread
330 for _
, msgid
in pairs(mbox
) do
331 local msg
= telex
.get_msg(msgid
)
333 minetest
.chat_send_player(name
, "You have unread messages")
340 function telex
.process_spool()
341 local spool
= telex
.decode(S
:get_string("telex_spool"))
343 minetest
.log("action", "telex: processing spool: " .. #spool
.. " messages" )
346 for k
, msgid
in pairs(spool
) do
347 local msg
= telex
.get_msg(msgid
)
348 msg
.age
= msg
.age
- 3600
351 del
[#del
+ 1] = msgid
353 telex
.save_msg(msgid
, msg
)
356 -- remove old msgs from spool
357 for i
= #old
, 1, -1 do
358 table.remove(spool
, old
[i
])
360 S
:set_string("telex_spool", telex
.encode(spool
))
362 -- return the actual messages
363 for _
, msgid
in pairs(del
) do
367 minetest
.after(3600, telex
.process_spool
)
370 minetest
.after(5, telex
.process_spool
)