Files
sky_phone-sky-systems/sky_phone/source/client/notifications.lua
T
2026-08-21 21:49:05 +02:00

301 lines
10 KiB
Lua

local NOTIFICATION_TEXT_MAX_LENGTH = 2000
local NOTIFICATION_TITLE_MAX_LENGTH = 160
local DEVICE_NAME_MAX_LENGTH = 128
SkyPhoneNotifications = {}
local function trim(value)
return value:match("^%s*(.-)%s*$")
end
local function normalize_text(value, error_code, maximum_length)
if type(value) ~= "string" then
return nil, error_code
end
local normalized = trim(value)
if normalized == "" or #normalized > maximum_length then
return nil, error_code
end
return normalized
end
local function resolve_alias(data, primary_key, alternate_key, error_code)
local primary = data[primary_key]
local alternate = data[alternate_key]
if primary ~= nil and alternate ~= nil and primary ~= alternate then
return nil, error_code
end
return primary ~= nil and primary or alternate
end
local function normalize_notification(notification)
if type(notification) ~= "table" then
return nil, "invalid_notification"
end
local app_id = resolve_alias(notification, "appId", "app_id", "invalid_app_id")
local valid_app_id = SkyPhoneApps.ValidateAppId(app_id)
if not valid_app_id or not SkyPhoneApps.CanReceiveNotification(app_id) then
return nil, "invalid_app_id"
end
local title, title_error = normalize_text(
notification.title,
"invalid_title",
NOTIFICATION_TITLE_MAX_LENGTH
)
if not title then
return nil, title_error
end
local raw_text = resolve_alias(notification, "text", "body", "invalid_text")
local notification_text, text_error = normalize_text(
raw_text,
"invalid_text",
NOTIFICATION_TEXT_MAX_LENGTH
)
if not notification_text then
return nil, text_error
end
return {
appId = app_id,
route = "/apps/" .. app_id,
text = notification_text,
title = title,
}
end
local function normalize_server_device(device)
if type(device) ~= "table"
or not SkyPhoneImei.IsValid(device.imei)
then
return nil, "invalid_device"
end
local name, name_error = normalize_text(
device.name,
"invalid_device",
DEVICE_NAME_MAX_LENGTH
)
if not name then
return nil, name_error
end
if device.settings ~= nil
and type(device.settings) ~= "string"
and type(device.settings) ~= "table"
then
return nil, "invalid_device"
end
return {
imei = device.imei,
name = name,
settings = device.settings,
}
end
local function show_normalized(notification)
SendNUIMessage({
type = "notification:show",
data = notification,
})
end
function SkyPhoneNotifications.Show(notification)
local normalized, error_code = normalize_notification(notification)
if not normalized then
return false, error_code
end
show_normalized(normalized)
return true
end
function SkyPhoneNotifications.Send(notification)
local normalized, error_code = normalize_notification(notification)
if not normalized then
return false, error_code
end
local result = Bridge.Callbacks.Trigger("sky_phone:notifications:self", normalized)
if type(result) ~= "table" or result.success ~= true then
return false, type(result) == "table" and result.error or "request_failed"
end
return true, result.data
end
RegisterNetEvent("sky_phone:notifications:show", function(notification)
if source ~= 65535 then
Bridge.Debug("warn", "[sky_phone] Rejected a client-triggered notification delivery event.")
return
end
local normalized, notification_error = normalize_notification(notification)
local device
local device_error = "invalid_device"
if type(notification) == "table" then
device, device_error = normalize_server_device(notification.device)
end
if not normalized or not device then
Bridge.Debug(
"warn",
"[sky_phone] Rejected an invalid server notification: %s.",
tostring(notification_error or device_error)
)
return
end
normalized.device = device
show_normalized(normalized)
TriggerEvent("sky_phone:client:pushNotification", {
appId = normalized.appId,
text = normalized.text,
title = normalized.title,
})
end)
local phone_locale = SkyPhoneLocales.Resolve(Config.Bridge.Locale)
local app_locales = phone_locale.Nui.Apps
RegisterNetEvent("sky_phone:mail:new", function(data)
local locale = app_locales.mail
data.title = locale.name
data.text = locale.newMessage:gsub("{sender}", tostring(data.sender))
SendNUIMessage({ type = "mail:new", data = data })
end)
RegisterNetEvent("sky_phone:citywarn:changed", function(data)
if type(data) ~= "table" then
return
end
local locale = app_locales.citywarn
data.title = locale.name
data.text = locale.notifications[data.kind] or locale.notifications.update
SendNUIMessage({ type = "citywarn:changed", data = data })
end)
RegisterNetEvent("sky_phone:companies:notification", function(data)
local locale = app_locales.companies
data.title = locale.name
data.text = locale.notifications[data.kind] or locale.notifications.requestUpdated
SendNUIMessage({ type = "companies:notification", data = data })
end)
RegisterNetEvent("sky_phone:fliptok:command-feedback", function(data)
Bridge.Framework.Notify("FlipTok", data.message, data.notificationType, 5000)
end)
RegisterNetEvent("sky_phone:fliptok:new", function(data)
local locale = app_locales.fliptok
local notification_text = locale.notifications[data.kind] or locale.notifications.default
data.title = locale.name
data.text = notification_text:gsub("{actor}", tostring(data.actor or ""))
SendNUIMessage({ type = "fliptok:new", data = data })
end)
RegisterNetEvent("sky_phone:picstagram:command-feedback", function(data)
Bridge.Framework.Notify("Picstagram", data.message, data.notificationType, 5000)
end)
RegisterNetEvent("sky_phone:picstagram:new", function(data)
local locale = app_locales.picstagram
local notification_text = locale.notifications[data.kind] or locale.notifications.default
data.title = locale.name
data.text = notification_text:gsub("{actor}", tostring(data.actor or ""))
SendNUIMessage({ type = "picstagram:new", data = data })
end)
RegisterNetEvent("sky_phone:feather:new", function(data)
local locale = app_locales.feather
local notification_text = locale.notifications[data.kind] or locale.notifications.default
data.title = locale.name
data.text = notification_text:gsub("{actor}", tostring(data.actor or ""))
SendNUIMessage({ type = "feather:new", data = data })
end)
RegisterNetEvent("sky_phone:marketplace:new-message", function(data)
local locale = app_locales.citymarkt
data.title = locale.name
if data.kind == "offer" then
data.text = locale.newOffer
:gsub("{sender}", tostring(data.sender))
:gsub("{price}", tostring(data.amount))
elseif data.kind == "offer-response" and data.action == "accepted" then
data.text = locale.offerAcceptedNotification
:gsub("{sender}", tostring(data.sender))
:gsub("{price}", tostring(data.amount))
elseif data.kind == "offer-response" and data.action == "rejected" then
data.text = locale.offerRejectedNotification
:gsub("{sender}", tostring(data.sender))
:gsub("{price}", tostring(data.amount))
else
data.text = locale.newMessage:gsub("{sender}", tostring(data.sender))
end
SendNUIMessage({ type = "marketplace:new-message", data = data })
end)
RegisterNetEvent("sky_phone:calendar:reminder", function(data)
local locale = app_locales.calendar
data.title = locale.name
data.text = locale.reminder:gsub("{title}", tostring(data.eventTitle))
SendNUIMessage({ type = "calendar:reminder", data = data })
end)
RegisterNetEvent("sky_phone:flare:match", function(data)
local locale = app_locales.flare
data.title = locale.name
data.text = locale.newMatchNotification:gsub("{sender}", tostring(data.sender))
SendNUIMessage({ type = "flare:new-match", data = data })
end)
RegisterNetEvent("sky_phone:flare:message", function(data)
local locale = app_locales.flare
data.title = locale.name
data.text = locale.newMessageNotification:gsub("{sender}", tostring(data.sender))
SendNUIMessage({ type = "flare:new-message", data = data })
end)
RegisterNetEvent("sky_phone:billing:new", function(data)
local locale = app_locales.billing
data.title = locale.name
data.text = locale.notifications.newInvoice
:gsub("{issuer}", tostring(data.issuer))
:gsub("{amount}", ("%s %s"):format(tostring(data.amount), Config.Billing.Currency))
SendNUIMessage({ type = "billing:new", data = data })
end)
RegisterNetEvent("sky_phone:crewlink:notification", function(data)
local locale = app_locales.crewlink
local notification_text = locale.notifications[data.kind] or locale.notifications.default
data.title = locale.name
data.text = notification_text
:gsub("{actor}", tostring(data.actor or ""))
:gsub("{group}", tostring(data.groupName or ""))
:gsub("{ping}", tostring(data.pingLabel or ""))
SendNUIMessage({ type = "crewlink:notification", data = data })
end)
RegisterNetEvent("sky_phone:messages:new", function(data)
local locale = app_locales.messages
data.title = locale.name
data.text = locale.newMessage:gsub("{sender}", tostring(data.sender))
SendNUIMessage({ type = "messages:new", data = data })
end)
RegisterNetEvent("sky_phone:darkchat:new", function(data)
local locale = app_locales.darkchat
data.title = locale.name
if data.notificationMode == "private" then
data.sender = nil
data.text = locale.privateNotification
elseif data.notificationMode == "hidden" then
data.sender = nil
data.text = ""
else
data.text = locale.newMessage:gsub("{sender}", tostring(data.sender))
end
SendNUIMessage({ type = "darkchat:new", data = data })
end)