Cube Scripts

Server

Every server export, with examples.

All exports are server side.

screenshot#

Captures a single frame from a player's screen. The result stays on your server - nothing is uploaded anywhere.

lua
exports.cube_monitoring:screenshot(targetId, handler)
  • targetId: number
  • handler: function(result, error)
    • result: table or nil
      • base64: string
      • mimeType: string - always image/webp
    • error: string or nil

Returns the request id, or nil if the target is offline. The handler is always called exactly once - on success, on failure, or on timeout (Config.ScreenshotTimeout).

<u>Examples</u>

lua
exports.cube_monitoring:screenshot(source, function(result, err)
    if not result then
        print('capture failed: ' .. err)
        return
    end

    print('captured ' .. #result.base64 .. ' bytes of base64')
end)

screenshotToDiscord#

Captures a frame and posts it straight to a Discord webhook. The handler receives the hosted attachment URL.

lua
exports.cube_monitoring:screenshotToDiscord(targetId, handler, webhookUrl)
  • targetId: number
  • handler: function(url, error)
  • webhookUrl: string (optional)
    • Falls back to ServerConfig.Webhook
lua
exports.cube_monitoring:screenshotToDiscord(targetId, function(url, err)
    if not url then
        print('upload failed: ' .. err)
        return
    end

    print('uploaded: ' .. url)
end)

With an explicit webhook, to route different staff teams to different channels:

lua
exports.cube_monitoring:screenshotToDiscord(
    targetId,
    function(url) print(url) end,
    'https://discord.com/api/webhooks/...'
)

uploadToDiscord#

Posts an already-captured file. Retries on rate limits and 5xx responses.

lua
exports.cube_monitoring:uploadToDiscord(base64, filename, mimeType, content, handler, webhookUrl)
  • base64: string
  • filename: string
  • mimeType: string
  • content: string - message body, truncated to 2000 characters
  • handler: function(url, error)
  • webhookUrl: string (optional)
lua
exports.cube_monitoring:uploadToDiscord(
    clipBase64,
    'clip.webm',
    'video/webm',
    'Clip of **Player** (`12`)',
    function(url, err)
        if url then print(url) end
    end
)
Warning

Discord rejects files over 8 MB on a standard webhook.

watch#

Opens a live stream. The admin's interface opens on its own.

lua
exports.cube_monitoring:watch(viewerId, targetId)
  • viewerId: number - the admin who will see the window
  • targetId: number - the player being watched
  • returns: string or nil - the stream id

Returns nil when either player is offline or a limit in Config was hit. Calling it again for a pair that is already streaming returns the existing id rather than opening a second window.

lua
local streamId = exports.cube_monitoring:watch(adminId, suspectId)

if not streamId then
    print('could not start the stream')
end

stopWatch#

Closes one stream and notifies both sides.

lua
exports.cube_monitoring:stopWatch(streamId)
  • streamId: string
  • returns: boolean - whether the stream existed

stopWatchingAs#

Closes every stream one admin has open.

lua
local closed = exports.cube_monitoring:stopWatchingAs(adminId)
print(('closed %d streams'):format(closed))
  • viewerId: number
  • returns: number - how many were closed

Useful when an admin goes off duty:

lua
RegisterNetEvent('myduty:off', function()
    exports.cube_monitoring:stopWatchingAs(source)
end)

getSessions#

Every live stream on the server.

lua
for _, session in ipairs(exports.cube_monitoring:getSessions()) do
    print(('%s: %s watching %s since %s'):format(
        session.streamId, session.viewer, session.target, session.startedAt
    ))
end
  • returns: table[]
    • streamId: string
    • viewer: number
    • target: number
    • startedAt: number - Unix timestamp

isAllowed#

Runs the same permission check the resource uses, so your own menus stay in sync.

lua
if exports.cube_monitoring:isAllowed(source) then
    -- show the monitoring button
end
  • src: number
  • returns: boolean

openPanel#

Opens the player list for an admin, without going through a command.

lua
exports.cube_monitoring:openPanel(source)
  • src: number
  • returns: boolean

Handy for an admin menu button:

lua
RegisterNetEvent('myadmin:openMonitoring', function()
    local src = source

    if not exports.cube_monitoring:isAllowed(src) then return end

    exports.cube_monitoring:openPanel(src)
end)

Logging watch sessions#

Built-in Discord logging is a config switch:

server/config.lua
ServerConfig.Logs = {
    enabled = true,
    webhook = "https://discord.com/api/webhooks/...",
}

For a database log instead, wrap the export:

lua
local function watchAndLog(viewerId, targetId)
    local streamId = exports.cube_monitoring:watch(viewerId, targetId)
    if not streamId then return nil end

    MySQL.insert.await(
        'INSERT INTO monitoring_log (admin, target, at) VALUES (?, ?, NOW())',
        { GetPlayerIdentifier(viewerId, 0), GetPlayerIdentifier(targetId, 0) }
    )

    return streamId
end
Tip

To catch the in-game commands as well, add your logging inside HasPermission in server/function.lua - every entry point passes through it.