536 lines
19 KiB
Lua
536 lines
19 KiB
Lua
local nk = require("nakama")
|
|
local utils = require("lua.utils")
|
|
|
|
local inbox = {}
|
|
|
|
function inbox.rpc_admin_send_mail(context, payload)
|
|
utils.require_admin(context)
|
|
local request = nk.json_decode(payload or "{}")
|
|
|
|
local nowStr = os.date("!%Y-%m-%dT%H:%M:%S.000Z") -- approximate ISO8601
|
|
local startDate = request.start_date or nowStr
|
|
local endDate = request.end_date or ""
|
|
|
|
-- 30 days from now in seconds for expiry_date fallback if not specified
|
|
local expiryDate = os.date("!%Y-%m-%dT%H:%M:%S.000Z", os.time() + 30 * 24 * 60 * 60)
|
|
|
|
local mailObj = {
|
|
id = nk.uuid_v4(),
|
|
title = request.title or "Announcement",
|
|
content = request.content or "",
|
|
sender = "TEKTON DEV TEAM",
|
|
date = startDate,
|
|
start_date = startDate,
|
|
end_date = endDate,
|
|
expiry_date = expiryDate,
|
|
rewards = request.rewards or {}
|
|
}
|
|
|
|
if request.target_user_id and request.target_user_id ~= "" then
|
|
mailObj.type = "personal"
|
|
local invObjs = nk.storage_read({{ collection = "inbox", key = "personal", user_id = request.target_user_id }})
|
|
local personalMails = {}
|
|
if #invObjs > 0 then
|
|
personalMails = invObjs[1].value.mails or {}
|
|
end
|
|
table.insert(personalMails, mailObj)
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "personal",
|
|
user_id = request.target_user_id,
|
|
value = { mails = personalMails },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
nk.logger_info("Personal mail sent to " .. request.target_user_id)
|
|
else
|
|
mailObj.type = "global"
|
|
local globalObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local globalMails = {}
|
|
if #globalObjs > 0 then
|
|
globalMails = globalObjs[1].value.mails or {}
|
|
end
|
|
table.insert(globalMails, mailObj)
|
|
|
|
nk.storage_write({{
|
|
collection = "config",
|
|
key = "global_mail",
|
|
user_id = "00000000-0000-0000-0000-000000000000",
|
|
value = { mails = globalMails },
|
|
permission_read = 2,
|
|
permission_write = 0
|
|
}})
|
|
nk.logger_info("Global mail sent")
|
|
end
|
|
|
|
return nk.json_encode({ success = true, mail = mailObj })
|
|
end
|
|
|
|
function inbox.rpc_get_mail(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
|
|
local personalObjs = nk.storage_read({{ collection = "inbox", key = "personal", user_id = context.user_id }})
|
|
local globalObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local stateObjs = nk.storage_read({{ collection = "inbox", key = "state", user_id = context.user_id }})
|
|
|
|
local personalMails = (#personalObjs > 0) and (personalObjs[1].value.mails or {}) or {}
|
|
local globalMails = (#globalObjs > 0) and (globalObjs[1].value.mails or {}) or {}
|
|
|
|
local state = { claimed_ids = {}, deleted_ids = {}, read_ids = {} }
|
|
if #stateObjs > 0 then
|
|
local val = stateObjs[1].value
|
|
state.claimed_ids = val.claimed_ids or {}
|
|
state.deleted_ids = val.deleted_ids or {}
|
|
state.read_ids = val.read_ids or {}
|
|
end
|
|
|
|
local function array_contains(arr, val)
|
|
for _, v in ipairs(arr) do
|
|
if v == val then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local allMails = {}
|
|
for _, m in ipairs(personalMails) do table.insert(allMails, m) end
|
|
for _, m in ipairs(globalMails) do table.insert(allMails, m) end
|
|
|
|
local filteredMails = {}
|
|
local nowStr = os.date("!%Y-%m-%dT%H:%M:%S.000Z")
|
|
|
|
for _, mail in ipairs(allMails) do
|
|
if not array_contains(state.deleted_ids, mail.id) then
|
|
local skip = false
|
|
if mail.expiry_date and mail.expiry_date ~= "" and nowStr > mail.expiry_date then
|
|
skip = true
|
|
end
|
|
if not skip and mail.start_date and mail.start_date ~= "" and nowStr < mail.start_date then
|
|
skip = true
|
|
end
|
|
if not skip and mail.type == "global" and mail.end_date and mail.end_date ~= "" and nowStr > mail.end_date then
|
|
skip = true
|
|
end
|
|
|
|
if not skip then
|
|
table.insert(filteredMails, mail)
|
|
end
|
|
end
|
|
end
|
|
|
|
return nk.json_encode({ mails = filteredMails, state = state })
|
|
end
|
|
|
|
function inbox.rpc_claim_mail_reward(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
local request = nk.json_decode(payload or "{}")
|
|
local mailId = request.mail_id
|
|
if not mailId then error("mail_id required") end
|
|
|
|
local personalObjs = nk.storage_read({{ collection = "inbox", key = "personal", user_id = context.user_id }})
|
|
local globalObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local stateObjs = nk.storage_read({{ collection = "inbox", key = "state", user_id = context.user_id }})
|
|
|
|
local state = { claimed_ids = {}, deleted_ids = {}, read_ids = {} }
|
|
if #stateObjs > 0 then
|
|
local val = stateObjs[1].value
|
|
state.claimed_ids = val.claimed_ids or {}
|
|
state.deleted_ids = val.deleted_ids or {}
|
|
state.read_ids = val.read_ids or {}
|
|
end
|
|
|
|
local function array_contains(arr, val)
|
|
for _, v in ipairs(arr) do
|
|
if v == val then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
if array_contains(state.claimed_ids, mailId) then
|
|
error("Reward already claimed")
|
|
end
|
|
|
|
local personalMails = (#personalObjs > 0) and (personalObjs[1].value.mails or {}) or {}
|
|
local globalMails = (#globalObjs > 0) and (globalObjs[1].value.mails or {}) or {}
|
|
local allMails = {}
|
|
for _, m in ipairs(personalMails) do table.insert(allMails, m) end
|
|
for _, m in ipairs(globalMails) do table.insert(allMails, m) end
|
|
|
|
local targetMail = nil
|
|
for _, mail in ipairs(allMails) do
|
|
if mail.id == mailId then
|
|
targetMail = mail
|
|
break
|
|
end
|
|
end
|
|
|
|
if not targetMail then error("Mail not found") end
|
|
|
|
local rewards = targetMail.rewards or {}
|
|
local starTotal = 0
|
|
local goldTotal = 0
|
|
local fragsToUpdate = {}
|
|
local skinsToAdd = {}
|
|
|
|
if type(rewards) == "table" and not rewards[1] and (rewards.star or rewards.gold) then
|
|
-- Handle legacy dictionary format
|
|
starTotal = rewards.star or 0
|
|
goldTotal = rewards.gold or 0
|
|
rewards = {}
|
|
end
|
|
|
|
for _, r in ipairs(rewards) do
|
|
local rType = r.type or "star"
|
|
local amount = r.amount or 0
|
|
|
|
if rType == "star" then
|
|
starTotal = starTotal + amount
|
|
elseif rType == "gold" then
|
|
goldTotal = goldTotal + amount
|
|
elseif string.sub(rType, 1, 5) == "frag_" or rType == "item" then
|
|
local fragId = r.id or rType
|
|
fragsToUpdate[fragId] = (fragsToUpdate[fragId] or 0) + amount
|
|
elseif rType == "skin" then
|
|
if r.id then table.insert(skinsToAdd, r.id) end
|
|
end
|
|
end
|
|
|
|
if starTotal > 0 or goldTotal > 0 then
|
|
local changes = {}
|
|
if starTotal > 0 then changes.star = starTotal end
|
|
if goldTotal > 0 then changes.gold = goldTotal end
|
|
nk.wallet_update(context.user_id, changes, {}, true)
|
|
end
|
|
|
|
local fragKeysCount = 0
|
|
for _ in pairs(fragsToUpdate) do fragKeysCount = fragKeysCount + 1 end
|
|
|
|
if fragKeysCount > 0 then
|
|
local invObjs = nk.storage_read({{ collection = "inventory", key = "fragments", user_id = context.user_id }})
|
|
local frags = (#invObjs > 0) and invObjs[1].value or {}
|
|
for fId, count in pairs(fragsToUpdate) do
|
|
frags[fId] = (frags[fId] or 0) + count
|
|
end
|
|
nk.storage_write({{
|
|
collection = "inventory",
|
|
key = "fragments",
|
|
user_id = context.user_id,
|
|
value = frags,
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
end
|
|
|
|
if #skinsToAdd > 0 then
|
|
local skinWrites = {}
|
|
for _, sId in ipairs(skinsToAdd) do
|
|
table.insert(skinWrites, {
|
|
collection = "inventory",
|
|
key = sId,
|
|
user_id = context.user_id,
|
|
value = { acquired_via = "mail", purchased_at = os.date("!%Y-%m-%dT%H:%M:%S.000Z") },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
})
|
|
end
|
|
nk.storage_write(skinWrites)
|
|
end
|
|
|
|
table.insert(state.claimed_ids, mailId)
|
|
if not array_contains(state.read_ids, mailId) then
|
|
table.insert(state.read_ids, mailId)
|
|
end
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "state",
|
|
user_id = context.user_id,
|
|
value = state,
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
|
|
return nk.json_encode({ success = true, claimed_ids = state.claimed_ids })
|
|
end
|
|
|
|
function inbox.rpc_delete_mail(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
local request = nk.json_decode(payload or "{}")
|
|
local mailId = request.mail_id
|
|
if not mailId then error("mail_id required") end
|
|
|
|
local stateObjs = nk.storage_read({{ collection = "inbox", key = "state", user_id = context.user_id }})
|
|
local state = { claimed_ids = {}, deleted_ids = {}, read_ids = {} }
|
|
if #stateObjs > 0 then
|
|
local val = stateObjs[1].value
|
|
state.claimed_ids = val.claimed_ids or {}
|
|
state.deleted_ids = val.deleted_ids or {}
|
|
state.read_ids = val.read_ids or {}
|
|
end
|
|
|
|
local function array_contains(arr, val)
|
|
for _, v in ipairs(arr) do if v == val then return true end end
|
|
return false
|
|
end
|
|
|
|
if not array_contains(state.deleted_ids, mailId) then table.insert(state.deleted_ids, mailId) end
|
|
if not array_contains(state.read_ids, mailId) then table.insert(state.read_ids, mailId) end
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "state",
|
|
user_id = context.user_id,
|
|
value = state,
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
|
|
return nk.json_encode({ success = true, deleted_ids = state.deleted_ids })
|
|
end
|
|
|
|
function inbox.rpc_save_mail_state(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
local request = nk.json_decode(payload or "{}")
|
|
|
|
local stateObjs = nk.storage_read({{ collection = "inbox", key = "state", user_id = context.user_id }})
|
|
local state = { claimed_ids = {}, deleted_ids = {}, read_ids = {} }
|
|
if #stateObjs > 0 then
|
|
local val = stateObjs[1].value
|
|
state.claimed_ids = val.claimed_ids or {}
|
|
state.deleted_ids = val.deleted_ids or {}
|
|
state.read_ids = val.read_ids or {}
|
|
end
|
|
|
|
local function array_contains(arr, val)
|
|
for _, v in ipairs(arr) do if v == val then return true end end
|
|
return false
|
|
end
|
|
|
|
local newReadIds = request.read_ids or {}
|
|
for _, rid in ipairs(newReadIds) do
|
|
if not array_contains(state.read_ids, rid) then
|
|
table.insert(state.read_ids, rid)
|
|
end
|
|
end
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "state",
|
|
user_id = context.user_id,
|
|
value = state,
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
|
|
return nk.json_encode({ success = true })
|
|
end
|
|
|
|
function inbox.rpc_admin_list_mail(context, payload)
|
|
utils.require_admin(context)
|
|
|
|
local globalObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local globalMails = (#globalObjs > 0) and (globalObjs[1].value.mails or {}) or {}
|
|
for _, m in ipairs(globalMails) do m.type = "global" end
|
|
|
|
local personalMails = {}
|
|
local cursor = nil
|
|
|
|
repeat
|
|
local status, listResult = pcall(nk.storage_list, "", "inbox", 100, cursor)
|
|
if status and listResult then
|
|
local objects = listResult.objects or {}
|
|
for _, obj in ipairs(objects) do
|
|
if obj.key == "personal" then
|
|
local ownerUserId = obj.user_id
|
|
local mails = obj.value.mails or {}
|
|
for _, m in ipairs(mails) do
|
|
m.type = "personal"
|
|
m.target_user_id = ownerUserId
|
|
table.insert(personalMails, m)
|
|
end
|
|
end
|
|
end
|
|
cursor = listResult.cursor
|
|
else
|
|
cursor = nil
|
|
end
|
|
until not cursor or cursor == ""
|
|
|
|
local allMails = {}
|
|
for _, m in ipairs(globalMails) do table.insert(allMails, m) end
|
|
for _, m in ipairs(personalMails) do table.insert(allMails, m) end
|
|
|
|
table.sort(allMails, function(a, b)
|
|
local d1 = a.date or ""
|
|
local d2 = b.date or ""
|
|
return d1 > d2
|
|
end)
|
|
|
|
return nk.json_encode({ mails = allMails })
|
|
end
|
|
|
|
function inbox.rpc_admin_update_mail(context, payload)
|
|
utils.require_admin(context)
|
|
local request = nk.json_decode(payload or "{}")
|
|
local mailId = request.mail_id
|
|
if not mailId then error("mail_id required") end
|
|
|
|
local isGlobal = request.type ~= "personal"
|
|
local targetUserId = request.target_user_id or ""
|
|
local newTargetUserId = request.new_target_user_id
|
|
local hasNewTarget = (newTargetUserId ~= nil)
|
|
|
|
local mailObj = nil
|
|
|
|
if isGlobal then
|
|
local globalObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local globalMails = (#globalObjs > 0) and (globalObjs[1].value.mails or {}) or {}
|
|
|
|
for i, m in ipairs(globalMails) do
|
|
if m.id == mailId then
|
|
mailObj = table.remove(globalMails, i)
|
|
break
|
|
end
|
|
end
|
|
if not mailObj then error("Mail not found in global") end
|
|
|
|
nk.storage_write({{
|
|
collection = "config",
|
|
key = "global_mail",
|
|
user_id = "00000000-0000-0000-0000-000000000000",
|
|
value = { mails = globalMails },
|
|
permission_read = 2,
|
|
permission_write = 0
|
|
}})
|
|
else
|
|
if targetUserId == "" then error("target_user_id required for personal mail") end
|
|
local pObjs = nk.storage_read({{ collection = "inbox", key = "personal", user_id = targetUserId }})
|
|
local personalMails = (#pObjs > 0) and (pObjs[1].value.mails or {}) or {}
|
|
|
|
for i, m in ipairs(personalMails) do
|
|
if m.id == mailId then
|
|
mailObj = table.remove(personalMails, i)
|
|
break
|
|
end
|
|
end
|
|
if not mailObj then error("Mail not found in personal inbox") end
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "personal",
|
|
user_id = targetUserId,
|
|
value = { mails = personalMails },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
end
|
|
|
|
if request.title ~= nil then mailObj.title = request.title end
|
|
if request.content ~= nil then mailObj.content = request.content end
|
|
if request.end_date ~= nil then mailObj.end_date = request.end_date end
|
|
if request.expiry_date ~= nil then mailObj.expiry_date = request.expiry_date end
|
|
|
|
local destUserId = ""
|
|
if hasNewTarget then destUserId = newTargetUserId else
|
|
if not isGlobal then destUserId = targetUserId end
|
|
end
|
|
|
|
if destUserId == "" then
|
|
mailObj.type = "global"
|
|
local gObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local gMails = (#gObjs > 0) and (gObjs[1].value.mails or {}) or {}
|
|
table.insert(gMails, mailObj)
|
|
|
|
nk.storage_write({{
|
|
collection = "config",
|
|
key = "global_mail",
|
|
user_id = "00000000-0000-0000-0000-000000000000",
|
|
value = { mails = gMails },
|
|
permission_read = 2,
|
|
permission_write = 0
|
|
}})
|
|
else
|
|
mailObj.type = "personal"
|
|
local dObjs = nk.storage_read({{ collection = "inbox", key = "personal", user_id = destUserId }})
|
|
local dMails = (#dObjs > 0) and (dObjs[1].value.mails or {}) or {}
|
|
table.insert(dMails, mailObj)
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "personal",
|
|
user_id = destUserId,
|
|
value = { mails = dMails },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
end
|
|
|
|
nk.logger_info("Admin updated mail " .. mailId .. " by " .. context.user_id)
|
|
return nk.json_encode({ success = true })
|
|
end
|
|
|
|
function inbox.rpc_admin_delete_mail_server(context, payload)
|
|
utils.require_admin(context)
|
|
local request = nk.json_decode(payload or "{}")
|
|
local mailId = request.mail_id
|
|
if not mailId then error("mail_id required") end
|
|
|
|
local isGlobal = request.type ~= "personal"
|
|
local targetUserId = request.target_user_id or ""
|
|
|
|
if isGlobal then
|
|
local globalObjs = nk.storage_read({{ collection = "config", key = "global_mail", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
local globalMails = (#globalObjs > 0) and (globalObjs[1].value.mails or {}) or {}
|
|
local before = #globalMails
|
|
local filtered = {}
|
|
for _, m in ipairs(globalMails) do
|
|
if m.id ~= mailId then table.insert(filtered, m) end
|
|
end
|
|
if #filtered == before then error("Mail not found") end
|
|
|
|
nk.storage_write({{
|
|
collection = "config",
|
|
key = "global_mail",
|
|
user_id = "00000000-0000-0000-0000-000000000000",
|
|
value = { mails = filtered },
|
|
permission_read = 2,
|
|
permission_write = 0
|
|
}})
|
|
else
|
|
if targetUserId == "" then error("target_user_id required for personal mail") end
|
|
local pObjs = nk.storage_read({{ collection = "inbox", key = "personal", user_id = targetUserId }})
|
|
local personalMails = (#pObjs > 0) and (pObjs[1].value.mails or {}) or {}
|
|
local before = #personalMails
|
|
local filtered = {}
|
|
for _, m in ipairs(personalMails) do
|
|
if m.id ~= mailId then table.insert(filtered, m) end
|
|
end
|
|
if #filtered == before then error("Mail not found") end
|
|
|
|
nk.storage_write({{
|
|
collection = "inbox",
|
|
key = "personal",
|
|
user_id = targetUserId,
|
|
value = { mails = filtered },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
end
|
|
|
|
nk.logger_info("Admin deleted mail " .. mailId .. " from server by " .. context.user_id)
|
|
return nk.json_encode({ success = true })
|
|
end
|
|
|
|
nk.register_rpc(inbox.rpc_admin_send_mail, "admin_send_mail")
|
|
nk.register_rpc(inbox.rpc_get_mail, "get_mail")
|
|
nk.register_rpc(inbox.rpc_claim_mail_reward, "claim_mail_reward")
|
|
nk.register_rpc(inbox.rpc_delete_mail, "delete_mail")
|
|
nk.register_rpc(inbox.rpc_save_mail_state, "save_mail_state")
|
|
nk.register_rpc(inbox.rpc_admin_list_mail, "admin_list_mail")
|
|
nk.register_rpc(inbox.rpc_admin_update_mail, "admin_update_mail")
|
|
nk.register_rpc(inbox.rpc_admin_delete_mail_server, "admin_delete_mail_server")
|
|
|
|
nk.logger_info("LUA TEST: inbox module loaded")
|
|
|
|
return inbox
|