Integration events

Raven tells your scripts the moment it bans, kicks, or warns someone, and can hold the drop back while you show your own screen.

When Raven acts on a player, your server usually wants to know. Raven fires a server event for every action it records, with the detail already assembled, so you never have to poll the dashboard for it. This page lists the four events, what each payload carries, and how to hold the drop back while you show your own screen.

The four events

EventWhen it firesCan delay the drop
rac:onPlayerBannedEvery ban, from a detection or from your own script, once the dashboard has issued the ban id and before the player is dropped.Yes
rac:onPlayerGlobalBannedIn addition to onPlayerBanned, and with the same payload, when that ban was global.Yes
rac:onPlayerKickedEvery kick, after the kick has been sent to the dashboard and before the player is dropped. It also fires for a ban the dashboard refused, because the player is kicked instead.Yes
rac:onPlayerWarnedA detection warning. Nothing is recorded and nobody is dropped.No
A global ban fires two events, so a handler registered on both runs twice for the same ban. Match on banId if that matters.

Only a ban is confirmed before its event fires: Raven waits for the dashboard to issue the ban id and passes it to you, so the id in the payload is real and already reviewable. A kick is sent to the dashboard without waiting, so rac:onPlayerKicked tells you the player is being dropped rather than that the record landed. A warning records nothing at all, which is why there is nothing to delay.

Handler arguments

ArgumentTypeWhat it is
srcnumberThe player server id.
tokenstringCompare it against the rac_token convar before you trust the rest.
datatableThe detail, laid out below.
delayDropfunction or nilOnly passed on the ban and kick events. It is nil on a warning.

The payload table

Every field below is on the data table. A field the event does not carry is not there at all, so data.banId reads nil on a kick and data.name reads nil on a warning. Test for a field before you format it.

FieldEventsWhat it holds
banIdBanned, Global bannedThe id your dashboard now lists the ban under, as a string.
globalBanned, Global bannedtrue when the ban was global, false when it was only yours.
warnIdWarnedThe detection name and the warning number joined with a colon, such as AntiNoclip:3.
count and maxWarnedWhich warning this is and how many it takes to act.
reasonAll fourWhat the player was shown, or the detection name on a warning.
descriptionAll fourThe description you passed to the export, or the detection that fired. On a warning it is "Warning 3 of 5".
screenshotUrlBanned, Global banned, KickedThe capture taken with the action, or nil.
nameBanned, Global banned, KickedThe player name as Raven had it when it acted.
A warning carries no name and no screenshot, so build your message from reason and description there.

Usage

server.lua
AddEventHandler('rac:onPlayerBanned', function(src, token, data)
    if token ~= GetConvar('rac_token', '') then return end

    print(('%s was banned: %s (ban %s)'):format(data.name, data.reason, data.banId))
end)

Checking the token

Any resource can trigger an event by name. The token is how your handler tells a real Raven event from one something else fired. Raven generates a fresh 32-character random value the first time it fires an integration event in a run and publishes it as the rac_token server convar, so a client can never read it.

Delaying the drop

Raven drops the player as soon as the ban or kick is recorded. Call delayDrop with a number of milliseconds to hold that back while you show your own screen.

server.lua
AddEventHandler('rac:onPlayerBanned', function(src, token, data, delayDrop)
    if token ~= GetConvar('rac_token', '') then return end

    delayDrop(4000)
    TriggerClientEvent('myserver:banScreen', src, data.reason, data.banId)
end)
RuleDetail
The largest ask winsHandlers asking for 2000 and 5000 give you 5000, not 7000.
The ceiling is 20000 msExactly 20000 is accepted. Anything higher, anything negative, and anything that is not a number are ignored.
0 is acceptedIt asks for no hold, which is the same as not calling it at all.
Call it before you yieldAfter a Wait or inside a callback it arrives too late to count.
A numeric string worksThe value is read with tonumber, so "4000" is the same as 4000.

What waits and what does not

The hold sits between the event and the drop, so everything the dashboard had already done is finished, and everything Raven does on the way out is pushed behind it.

WhatDoes it wait
The dashboard recordAlready written. The ban event only fires once the id exists.
The cloud Discord logAlready sent, because the dashboard writes it when it takes the ban.
The drop itselfWaits for the whole window you asked for.
The in-game admin notificationWaits with the drop. It goes out after the hold, not during it.
The menu ban list refreshWaits with the drop, for the same reason.
Your admins see the notification when the player actually goes, so a long hold on a busy server delays the warning they get about it.