init.lua 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. -- Mod is reloadable.
  2. email = email or {}
  3. email.inboxes = email.inboxes or {}
  4. email.modpath = minetest.get_modpath("email")
  5. email.worldpath = minetest.get_worldpath()
  6. email.database = email.worldpath .. "/email.sqlite"
  7. email.maxsize = 100
  8. email.max_subject_length = 128
  9. email.max_message_length = 1024*2
  10. -- Localize for performance.
  11. local math_random = math.random
  12. dofile(email.modpath .. "/database.lua")
  13. function email.on_startup()
  14. assert(not email.db)
  15. email.db = email.sql.open(email.database)
  16. assert(email.db)
  17. -- Ensure table exists.
  18. email.db_exec([[ CREATE TABLE IF NOT EXISTS email (
  19. name TEXT,
  20. sender TEXT,
  21. date TEXT,
  22. subject TEXT,
  23. message TEXT,
  24. number INTEGER
  25. ); ]])
  26. end
  27. function email.on_shutdown()
  28. assert(email.db)
  29. email.db:close()
  30. email.db = nil
  31. end
  32. function email.get_inbox(name)
  33. name = rename.grn(name)
  34. if not email.inboxes[name] then
  35. assert(email.db)
  36. email.inboxes[name] = email.load_inbox(email.db, name) or {}
  37. end
  38. return email.inboxes[name]
  39. end
  40. function email.get_inbox_size(name)
  41. name = rename.grn(name)
  42. local tb = email.get_inbox(name)
  43. return #tb
  44. end
  45. function email.clear_inbox(name, mails)
  46. name = rename.grn(name)
  47. local inbox = email.get_inbox(name)
  48. -- `mails` is a list of emails to delete.
  49. -- If an email has a duplicate in the `mails` table, then we delete it.
  50. for k, v in ipairs(mails) do
  51. local rng = v.rng
  52. assert(type(rng) == "number")
  53. for i, j in ipairs(inbox) do
  54. local rng2 = j.rng
  55. assert(type(rng2) == "number")
  56. if rng2 == rng then
  57. table.remove(inbox, i)
  58. break
  59. end
  60. end
  61. end
  62. email.delete_mails(email.db, name, mails)
  63. if #inbox == 0 then
  64. -- This will cause a refetch from database.
  65. email.inboxes[name] = nil
  66. end
  67. end
  68. function email.send_mail_multi(from, multi_target, subject, message)
  69. from = rename.grn(from)
  70. local success = {}
  71. local failure = {}
  72. email.db_exec([[ BEGIN TRANSACTION; ]])
  73. -- Assume multi-target is a table of playernames to send mail to.
  74. for k, v in ipairs(multi_target) do
  75. v = rename.grn(v)
  76. local bresult, sresult = email.send_mail_ex(from, v, subject, message)
  77. if bresult == true then
  78. table.insert(success, {name=v, error=sresult})
  79. elseif bresult == false then
  80. table.insert(failure, {name=v, error=sresult})
  81. end
  82. end
  83. email.db_exec([[ COMMIT; ]])
  84. return success, failure -- Tables with success and failure results.
  85. end
  86. -- API function. Shall send mail to a player, first checking if
  87. -- sending is possible and permitted.
  88. -- Returns a success boolean and a string key.
  89. function email.send_mail_single(from, to, subject, message)
  90. from = rename.grn(from)
  91. to = rename.grn(to)
  92. local bresult, sresult = email.send_mail_ex(from, to, subject, message)
  93. return bresult, sresult -- Boolean, error key.
  94. end
  95. -- This should only be called internally to this mod.
  96. function email.send_mail_ex(from, to, subject, message)
  97. from = rename.grn(from)
  98. to = rename.grn(to)
  99. -- Cannot send email if recipient does not exist.
  100. if not minetest.player_exists(to) then
  101. return false, "badplayer"
  102. end
  103. if string.len(subject) > email.max_subject_length or string.len(message) > email.max_message_length then
  104. return false, "toobig"
  105. end
  106. local inboxes = email.get_inbox(to)
  107. if #inboxes >= email.maxsize then return false, "boxfull" end -- Inbox full.
  108. -- Find a unique ID for this new email.
  109. ::tryagain::
  110. local rng = math_random(1, 32000) -- 0 is not a valid ID. Important!
  111. for k, v in ipairs(inboxes) do
  112. if v.rng == rng then goto tryagain end
  113. end
  114. local mail = {
  115. date = os.date("%Y-%m-%d"),
  116. from = from,
  117. msg = message,
  118. sub = subject,
  119. rng = rng, -- Random ID unique from all other emails in this inbox.
  120. }
  121. -- Keep in-memory cache sychronized with database.
  122. -- Only if cache already loaded for this target player.
  123. if email.inboxes[to] then
  124. table.insert(email.inboxes[to], mail)
  125. end
  126. email.store_mail(email.db, to, mail)
  127. return true, "success" -- Mail successfully sent!
  128. end
  129. function email.on_joinplayer(player)
  130. local pname = player:get_player_name()
  131. minetest.after(10, function()
  132. if passport.player_registered(pname) then
  133. local inbox = email.get_inbox(pname)
  134. if #inbox > 0 then
  135. minetest.chat_send_player(pname,
  136. "# Server: You have mail (" .. #inbox .. ")! Use a Key of Citizenship to view it.")
  137. end
  138. end
  139. end)
  140. end
  141. if not email.registered then
  142. email.sql = require("lsqlite3")
  143. -- Don't allow other mods to use this global library!
  144. if sqlite3 then sqlite3 = nil end
  145. minetest.register_on_shutdown(function(...)
  146. return email.on_shutdown(...)
  147. end)
  148. -- Depreciated command.
  149. minetest.register_chatcommand("inbox", {
  150. params = "",
  151. description = "Depreciated command; use Key of Citizenship instead.",
  152. func = function(name, param)
  153. minetest.chat_send_player(name, "# Server: This command is unavailable; the functionality is accessed from the Key.")
  154. easyvend.sound_error(name)
  155. end
  156. })
  157. -- Depreciated command.
  158. minetest.register_chatcommand("mail", {
  159. params = "",
  160. description = "Depreciated command; use KoC instead.",
  161. func = function(name, param)
  162. minetest.chat_send_player(name, "# Server: This command is unavailable; the functionality is accessed from the Key.")
  163. easyvend.sound_error(name)
  164. end,
  165. })
  166. minetest.register_on_joinplayer(function(...)
  167. return email.on_joinplayer(...)
  168. end)
  169. email.on_startup()
  170. dofile(email.modpath .. "/hud.lua")
  171. local c = "email:core"
  172. local f = email.modpath .. "/init.lua"
  173. reload.register_file(c, f, false)
  174. email.registered = true
  175. end