Docs


Scripts Tutorial

This document describes Rete’s Tcl scripting API: how to bind to events, how return values influence client behaviour, what globals are available, and which helper commands you can call from Tcl.

Event binding

bind EVENT handlerProc
unbind EVENT handlerProc
proc handlerProc {arg1 arg2 ...} {
    # return 0 to let Rete continue normal processing
    # return 1 to stop further processing of this event
}

Return convention

Event arguments

Each event passes a fixed list of string arguments to your handler. Timestamps are passed as ISO‑8601 strings when available, otherwise "". Optional values (like account, reason) are passed as "" when absent.

RAWIN

proc my_rawin {line} {
    # line: raw line from server before any parsing
    return 0
}

REGISTERED

proc my_registered {} {
    # No arguments
    return 0
}

SILENCE

proc my_silence {action hostmask} {
    # action: "add" or "rem"
    # hostmask: the hostmask being added/removed
    return 0
}

ERROR

proc my_error {text} {
    # text: server error message
    return 0
}

RPL (numeric replies)

proc my_rpl {code text buffer serverTime} {
    # code: numeric code as string (e.g., "001", "005"), or ""
    # text: reply text
    # buffer: target buffer name (channel/DM) if applicable, or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

SERVERNOTICE

proc my_servernotice {from text channel serverTime} {
    # from: sender prefix or ""
    # text: notice text
    # channel: associated channel if applicable, or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

JOIN

proc my_join {channel nick user host account realname serverTime} {
    # channel: channel name
    # nick: nickname of person who joined
    # user: ident/username
    # host: hostname
    # account: services account name or ""
    # realname: realname/GECOS
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

PART

proc my_part {channel nick reason serverTime} {
    # channel: channel name
    # nick: nickname of person who left
    # reason: part reason or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

QUIT

proc my_quit {nick message serverTime} {
    # nick: nickname of person who quit
    # message: quit message or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

CHANMSG (channel PRIVMSG)

proc my_chanmsg {from channel text serverTime} {
    # from: sender nickname
    # channel: channel name
    # text: message text
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

DIRECTMSG (direct PRIVMSG)

proc my_directmsg {from target text serverTime} {
    # from: sender nickname
    # target: recipient (your nick or theirs)
    # text: message text
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

CHANNOTICE (channel NOTICE)

proc my_channotice {from channel target text serverTime} {
    # from: sender nickname or server prefix
    # channel: channel name
    # target: target nickname or ""
    # text: notice text
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

DIRECTNOTICE (direct NOTICE)

proc my_directnotice {from target text serverTime} {
    # from: sender nickname or server prefix
    # target: recipient nickname
    # text: notice text
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

NICK (nick change)

proc my_nick {oldNick newNick serverTime} {
    # oldNick: previous nickname
    # newNick: new nickname
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

TOPIC (topic info/update)

proc my_topic {channel topic who serverTime} {
    # channel: channel name
    # topic: topic text or ""
    # who: nickname who set topic, or "" for topic info (RPL 332/333)
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

MODE (channel mode change)

proc my_mode {channel setter modeString params serverTime} {
    # channel: channel name
    # setter: nickname who set the mode
    # modeString: mode string (e.g., "+o", "+nt-lk"), or ""
    # params: space-separated mode parameters, or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

AWAY (away status change)

proc my_away {nick isAway message serverTime} {
    # nick: nickname
    # isAway: "1" if now away, "0" if back
    # message: away message or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

ACCOUNT (account change)

proc my_account {nick account serverTime} {
    # nick: nickname
    # account: services account name, or "" if logged out
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

CHGHOST (user/host change)

proc my_chghost {nick user host serverTime} {
    # nick: nickname
    # user: new ident/username
    # host: new hostname
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

OPER (oper status change)

proc my_oper {nick isOper} {
    # nick: nickname
    # isOper: "1" if now oper, "0" if no longer oper
    # NOTE: No serverTime for OPER events
    return 0
}

INVITE

proc my_invite {inviter target channel serverTime} {
    # inviter: nickname who sent invite
    # target: nickname who was invited (may be you)
    # channel: channel name
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

KICK

proc my_kick {channel kicker victim reason serverTime} {
    # channel: channel name
    # kicker: nickname who kicked, or ""
    # victim: nickname who was kicked
    # reason: kick reason or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

WALLOPS

proc my_wallops {from text serverTime} {
    # from: sender nickname or server prefix
    # text: wallops message text
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

ACTION (ACTION, /me)

proc my_action {from target text serverTime} {
    # from: sender nickname
    # target: channel name or nickname (where action was sent)
    # text: action text (without the `/me` prefix)
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

CTCPREQ (CTCP request)

proc my_ctcpreq {from target command params serverTime} {
    # from: sender nickname
    # target: recipient (channel or nickname)
    # command: CTCP command name (uppercased, e.g., "VERSION", "PING")
    # params: command parameters or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

CTCPRPL (CTCP reply)

proc my_ctcprpl {from target command params serverTime} {
    # from: sender nickname
    # target: recipient (channel or nickname)
    # command: CTCP command name (uppercased, e.g., "VERSION", "PING")
    # params: reply parameters or ""
    # serverTime: ISO-8601 timestamp or ""
    return 0
}

Globals

Identity variables:

Server variables:

Version variables:

Access them from Tcl as normal variables:

debug "I am $mynick!$myuser@$myhost (account=$myaccount)"
debug "Connected to $server at $serveraddress (daemon: $serverdaemon)"
debug "Uptime: $uptime, Server online: $server-online"

Helper commands (getters)

topic #channel

Returns the current topic for #channel as a string, or "" if none is known.

getchanmode #channel

Returns a human‑readable mode string for #channel if available, or "" if no cached information is available.

isupport_get KEY     ;# returns string or ""
isupport_isset KEY   ;# returns 1 if the key is present, 0 otherwise

These map directly onto the server’s ISUPPORT tokens as seen in RPL 005.

rfcequal string1 string2

Checks if two strings are equal using IRC case-insensitive comparison (RFC 1459 matching). This uses the same comparison logic as Rete’s internal IRCCompare function, which respects the server’s CASEMAPPING ISUPPORT token.

Returns 1 if the strings are equal (case-insensitive, with RFC 1459 character equivalences: {}|~ == []\^), 0 otherwise.

Example:

# These all return 1 (equal)
rfcequal "Nick" "nick"
rfcequal "Test" "test"
rfcequal "User[Name" "user{name"  ;# [ == { under RFC 1459
rfcequal "Chan^el" "chan~el"       ;# ^ == ~ under RFC 1459
theme_get <option>

Returns the value of a theme setting from the current theme. This allows scripts to access theme configuration values such as colors, badge icons, and theme information.

Options:

Returns a string value, or an empty string if the option is not found. Colors are returned in hex format (e.g., "#FF0000"). The command automatically uses the appropriate light/dark palette based on the current appearance mode.

Example:

# Get current theme name
set themeName [theme_get theme]
debug "Current theme: $themeName"

# Get accent color
set accentColor [theme_get accent]
debug "Accent color: $accentColor"

# Get bot badge icon
set botBadge [theme_get botBadge]
debug "Bot badge: $botBadge"

# Respond with theme information
proc on_chanmsg_info {from channel text serverTime} {
    if {$text eq "!theme"} {
        set themeName [theme_get theme]
        set highlightTheme [theme_get scriptEditorHighlightrTheme]
        msg $channel "Theme: $themeName, Highlight theme: $highlightTheme"
    }
    return 0
}
bind CHANMSG on_chanmsg_info

For complete documentation, see the “Accessing Theme Settings from TCL Scripts” section in Themes.md.

isop nick #channel
ishalfop nick #channel
isvoice nick #channel

Each returns 1 if the nick has the corresponding privilege on the channel, 0 otherwise. nick may be "" to mean “me”.

isaway nick
isbot nick
isoper nick
issecure nick
getaccount nick

These query Rete’s cached Person entry for nick and return:

issecure checks if the user has a secure (TLS/SSL) connection to the server.

ischanban ban #channel
ischanexempt exempt #channel
ischaninvite invite #channel

These commands check if a specific mask is present on the channel’s ban list, exception list, or invite-exempt list. Each returns 1 if found, 0 otherwise.

chanbans #channel
chanexempts #channel
chaninvites #channel

These commands return Tcl lists of entries for the respective channel lists. Each entry is a sublist of the form {mask bywho age}:

Example:

# Check if a ban exists
if {[ischanban *!*@*.example.com #channel]} {
    debug "User is banned"
}

# List all bans
set bans [chanbans #channel]
foreach entry $bans {
    set mask [lindex $entry 0]
    set bywho [lindex $entry 1]
    set age [lindex $entry 2]
    debug "Ban: $mask (set by $bywho, age: $age seconds)"
}

Helper commands (setters)

Scripts can also update certain fields on Person entries (Rete’s cached view of users on the current connection).

setcolor nick #RRGGBB

Sets color for nick to the given hex color (normalized by Rete). This affects how that nick is rendered in the UI (subject to theme rules).

setbot nick 0|1

Sets Person.isBot for nick to 1 (true) or 0 (false). This can be used by themes or scripts to treat known bots differently.

Helper commands (buffer manipulation)

appendmsg <buffer> <sender> <message> <category> ?tags...?

Appends a message to the specified buffer. This allows scripts to add messages to buffers programmatically, which can be useful for custom logging, notifications, or bot responses.

Valid categories:

Valid tags:

Returns nothing on success. If the buffer is not found, an error message is written to the Script Debug buffer.

createbuffer name type ?inputCallback? ?hidden?

Creates a new buffer with the specified name and type.

Buffer Types:

Script Buffers:

Script buffers are special buffers that can have an optional input callback function. When a callback function is provided and the user sends input in the buffer:

  1. The callback function is called with two arguments:
    • bufferName: The name of the buffer
    • inputText: The text that was entered
  2. If no callback is provided (or an empty string is passed), input is disabled for the buffer.

Notes:

Example (create/append):

# Append a normal message to a channel
appendmsg "#channel" "BotName" "Hello from script!" "normal" "channel"

# Append a server notice-style message (no sender)
appendmsg "#channel" "" "This is a system message" "serverNotice" "server"

# Append an error message
appendmsg "#channel" "" "Something went wrong!" "error" "error"

# Append a join event
appendmsg "#channel" "SomeUser" "has joined" "join" "channel"

# Append to a DM buffer
appendmsg "nickname" "BotName" "Private message" "normal" "dm"
# Create a script buffer with input callback
proc my_buffer_input {bufferName text} {
    msg #channel "Received: $text"
}
createbuffer "MyBuffer" script "my_buffer_input"

# Create a channel buffer
createbuffer "#test" channel

# Create a hidden script buffer without input
createbuffer "LogBuffer" script "" 1
getbuffer <name> <type>

Resolves a buffer by name and type and returns its UUID string. This is useful to avoid name collisions (e.g. a DM and a script buffer both called “nick”) when using commands like appendmsg, hidebuffer, or setbuffericon.

Returns the buffer UUID as a string on success, or an empty string if no matching buffer is found.

Example:

# Get the UUID for a script buffer called "test"
set bufId [getbuffer "test" script]

if {$bufId ne ""} {
    # Append a message using the UUID
    appendmsg $bufId "" "Hello from script buffer" "normal" "script"

    # Hide the buffer using the UUID
    hidebuffer $bufId 1

    # Set a custom icon using the UUID
    setbuffericon $bufId "star.fill"
}
setbuffericon <buffer> <iconName>

Sets a custom SF Symbol icon for a buffer. If the icon name is invalid (SF Symbol doesn’t exist) or empty, resets to the default icon based on buffer kind.

If the specified SF Symbol doesn’t exist, the command will log a warning and reset to the default icon for that buffer type.

Example:

# Set a custom icon for a script buffer
setbuffericon "MyBuffer" "star.fill"

# Set a different icon
setbuffericon "MyBuffer" "heart.circle"

# Reset to default icon
setbuffericon "MyBuffer" ""
hidebuffer <buffer> <hidden>

Hide or show a buffer in the sidebar. This works for any buffer type.

Example:

# Hide a buffer
hidebuffer "MyBuffer" 1

# Show a buffer
hidebuffer "MyBuffer" 0

# Hide a channel
hidebuffer "#test" true

# Show a channel
hidebuffer "#test" false
delbuffer <buffer>

Delete (close) a buffer. This will remove the buffer from the sidebar and discard its messages from the current session.

Notes:

setweburl <buffer> <url>

Sets the URL for a web buffer.

If the buffer is not found or is not a web buffer, an error is logged to the Script Debug buffer and the command is otherwise ignored.

Example:

proc open_rete_website {} {
    # Create or find a web buffer
    createbuffer "Rete Website" web

    # Point it to the desired URL
    setweburl "Rete Website" "https://rete.chat/"
}
addnickmenu <label> <callback>

Adds a custom menu item to the nicklist context menu (right-click on nickname).

The callback function will be called with three arguments:

proc my_nick_action {bufferId bufferName nick} {
    # bufferId   - UUID of the buffer where the nick was right-clicked
    # bufferName - Name of that buffer (e.g. #channel)
    # nick       - The nickname that was right-clicked
}

Notes:

Example:

# Define a callback function
proc my_nick_action {bufferId bufferName nick} {
    msg $bufferName "Hello $nick from script! (buffer $bufferName / $bufferId)"
}

# Add the menu item
addnickmenu "Say Hello" "my_nick_action"

Dynamic nicklist menu providers

For fully dynamic, per-nick context menus that are computed only when a nick is right-clicked, you can register a nickmenu provider:

setnickmenuprovider <callback>

The provider function will be called with three arguments:

proc my_nickmenu_provider {bufferId bufferName nick} {
    # bufferId   - UUID of the buffer where the nick was right-clicked
    # bufferName - Name of that buffer (e.g. #channel)
    # nick       - The nickname that was right-clicked

    # Return a Tcl list of label/callback pairs:
    #   {label1 callback1 label2 callback2 ...}
    set items {}
    lappend items "Whois $nick" my_whois_proc
    lappend items "Greet $nick" my_greet_proc

    return $items
}

proc my_whois_proc {bufferId bufferName nick} {
    putserv "WHOIS $nick"
}

proc my_greet_proc {bufferId bufferName nick} {
    msg $bufferName "Hello $nick from dynamic menu (buffer $bufferName / $bufferId)"
}

# Register the provider
setnickmenuprovider my_nickmenu_provider

Notes:

Dynamic hover-card (user info tooltip) buttons

You can also add buttons to the hover card that appears when hovering over nicknames (user info tooltip). These are computed dynamically when the tooltip is shown, similar to dynamic nicklist menu providers:

setnickhoverprovider <callback>

The provider function will be called with three arguments:

proc my_nickhover_provider {bufferId bufferName nick} {
    # bufferId   - UUID of the buffer context (channel or DM), or "" if unknown
    # bufferName - Name of that buffer (e.g. #channel or nick), or "" if unknown
    # nick       - The nickname being hovered

    # Return a Tcl list of label/callback pairs:
    #   {label1 callback1 label2 callback2 ...}
    set items {}
    lappend items "Whois" my_whois_proc
    lappend items "Greet" my_greet_proc

    return $items
}

proc my_whois_proc {bufferId bufferName nick} {
    putserv "WHOIS $nick"
}

proc my_greet_proc {bufferId bufferName nick} {
    # Send greeting to the current buffer if known, or fall back to using the nick
    if {$bufferName ne ""} {
        msg $bufferName "Hello $nick from hover card (buffer $bufferName / $bufferId)"
    } else {
        msg $nick "Hello from hover card"
    }
}

# Register the provider
setnickhoverprovider my_nickhover_provider

Notes:

Helper commands (sending commands)

In addition to getters, Tcl scripts can send commands to the IRC server via the following helpers. All of them operate on the current connection (IRCStore.transport).

putserv RAWLINE...

Example:

putserv PRIVMSG #channel :Hello from Tcl
join #channel ?key?
part #channel ?reason...?
msg target text...
notice target text...
ctcp target command ?params...?
action target text...
topic_set #channel ?newTopic...?
nick newNick
quit ?message...?
mode target modes ?params...?
kick #channel nick ?reason...?
silence ?input...?

These are thin wrappers around Rete’s internal command/transport layer and behave like the corresponding /join, /part, /msg, /notice, /ctcp, /me, /topic, /nick, /quit, /mode, /kick and /silence commands typed in the client, with putserv exposing raw access to the underlying connection.

cap ls
cap values
cap values <capability>
cap enabled
cap req "cap1 cap2 ..."
cap raw "CAP SUBCOMMAND ..."

Manages IRCv3 capabilities (CAP):

Examples:

# List server capabilities
set caps [cap ls]
debug "Server supports: $caps"

# Check if SASL is enabled
set enabled [cap enabled]
if {"sasl" in $enabled} {
    debug "SASL is enabled"
}

# Get SASL mechanisms
set mechs [cap values sasl]
debug "SASL mechanisms: $mechs"

# Request a capability
cap req "cap-notify"

# Send raw CAP command
cap raw "LS 302"

Timer commands

Rete provides timer functionality similar to Eggdrop’s timer and utimer commands, allowing scripts to schedule Tcl commands to execute at regular intervals.

timer minutes commandName ?count? ?timerName?

Creates a timer that fires every minutes minutes, aligned to the top of the minute. The first fire occurs at the next minute boundary plus minutes.

Returns the timer name (useful when auto-generated).

Example:

# Fire every 5 minutes, 10 times
timer 5 my_periodic_task 10 mytask

# Fire every 30 seconds, infinite
timer 0.5 check_status 0
utimer seconds commandName ?count? ?timerName?

Creates a timer that fires every seconds seconds, starting immediately (now + seconds).

Returns the timer name.

Example:

# Fire every 10 seconds, 5 times
utimer 10 check_connection 5 conncheck

# Fire once after 30 seconds
utimer 30 delayed_action 1
timers
utimers

Returns a space-separated list of active timers. Each entry contains: timerName intervalSeconds count nextFireAt command

killtimer timerName
killutimer timerName

Cancels and removes the specified timer. Returns nothing on success, or an error if the timer doesn’t exist.

Example:

set t [timer 5 my_task 0]
# ... later ...
killtimer $t

Notes:

The debug command

Scripts can write to the per‑store Script Debug buffer using:

debug ?level? text...

Rete maps debug verbosity levels with script as the theme category and error, warning, debug and infoas theme tags.