sigildocs

(sigil irc message)

(sigil irc message) - IRC Message Parsing and Construction

Parses and constructs IRC protocol messages, including IRCv3 message tags. The wire format is symmetric — the same parser handles client→server lines and server→client lines — so there is one parser for both flow directions.

IRCv3 message format: [@tags] [:prefix] command [params...] [:trailing]

A tags field is an alist of (tag-key . tag-value-or-#f). Keys may be vendor-prefixed (e.g. +example.com/feature) and values may be absent for valueless flags. Tag value escaping per IRCv3: ; -> : space -> s -> \ CR -> r LF -> n

Examples: :nick!user@host PRIVMSG #channel :Hello world @time=2026-04-27T12:00:00.123Z;account=alice :alice!a@host PRIVMSG #foo :hi PING :server.name :server 001 nick :Welcome to the IRC Network

Exports

irc-messageprocedure

Construct a irc-message struct.

irc-message?procedure

Test if a value is a irc-message struct.

Get the raw field of a irc-message struct.

Get the tags field of a irc-message struct.

Get the prefix field of a irc-message struct.

Get the nick field of a irc-message struct.

Get the user field of a irc-message struct.

Get the host field of a irc-message struct.

Get the command field of a irc-message struct.

Get the params field of a irc-message struct.

Get the trailing field of a irc-message struct.

Get the target of a message (first param, usually channel or nick).

(irc-message-target msg)  ; => "#channel" or "nickname"

Get the text content of a message (alias for trailing).

Look up a single IRCv3 tag value on a message. Returns the tag's value string, the symbol 'flag for valueless tags (the tag is present but has no =value), or #f if absent.

(irc-message-tag msg "time")           ; => "2026-04-27T12:00:00Z"
(irc-message-tag msg "echo-message")   ; => 'flag (present, no value)
(irc-message-tag msg "missing")        ; => #f

Predicate: is the named tag present on this message? Returns #t for both valueless and value-bearing tags.

Escape a tag value per IRCv3 message-tags rules.

; -> : space -> s -> \ CR -> r LF -> n

(irc-tag-escape "hello world; rest") ; => "hello\\sworld\\:\\srest"

Unescape a tag value per IRCv3 rules. Lone backslashes (followed by nothing or by a non-escape char) are dropped per spec — \x for unknown x becomes just x.

Parse the body of an IRCv3 tags prefix (without the leading @) into an alist of (key . value-or-#f).

(parse-irc-tags "time=2026-04-27T12:00:00Z;account=alice;echo")
; => (("time" . "2026-04-27T12:00:00Z")
;     ("account" . "alice")
;     ("echo" . #f))

Serialize a tags alist (without leading @).

(irc-tags->string '(("time" . "2026Z") ("echo" . #f)))
; => "time=2026Z;echo"

Parse a raw IRC message line into an irc-message record.

Handles IRCv3 message tags (@key=value;...), an optional prefix (:nick!user@host or :server), the command, middle parameters, and a trailing parameter.

Returns an irc-message record, or #f if parsing fails.

(parse-irc-message "@time=2026Z :nick!u@h PRIVMSG #c :Hello")
; => #<irc-message tags: (("time" . "2026Z")) command: PRIVMSG ...>

Create an IRC command string (no tags, no prefix) from parts.

The last argument gets a : prefix when it's text-shaped — i.e. contains a space, starts with :, or is empty — to disambiguate the trailing parameter from the middle params. For unambiguous single-token last params (like a nick or channel name) the : is omitted, matching the canonical IRCv2 wire (e.g. MODE #c +o alice, not MODE #c +o :alice).

Note: this leaves a hazard for messages whose semantic role is "trailing text" but whose value happens to be a single spaceless token (e.g. PRIVMSG #c connected.). Some parsers (the strict ones) treat that as a middle param and leave the trailing slot empty — losing the message body. For those use cases, prefer make-trailing-command (or irc-privmsg/ irc-notice, which always force :) over make-irc-command.

(make-irc-command "PRIVMSG" "#channel" "Hello world")
; => "PRIVMSG #channel :Hello world"

(make-irc-command "MODE" "#channel" "+o" "alice")
; => "MODE #channel +o alice"

Build a wire string where the LAST argument is unconditionally emitted with a : prefix. Use this for commands whose final parameter is human/agent text (PRIVMSG, NOTICE, TOPIC, QUIT, PART message, KICK reason). The unconditional : ensures strict parsers always populate irc-message-trailing rather than tucking a single-token body into params.

(make-trailing-command "PRIVMSG" "#chan" "hi")
; => "PRIVMSG #chan :hi"

(make-trailing-command "PRIVMSG" "#chan" "hello world")
; => "PRIVMSG #chan :hello world"

Build a tagged IRC command line (no CRLF). Pass tags as an alist of (key . value-or-#f). Keys with value #f or 'flag serialize as valueless. Pass prefix: to include a sender prefix.

(make-irc-command/tags
  tags: '(("time" . "2026Z") ("account" . "alice"))
  prefix: "alice!a@host"
  command: "PRIVMSG"
  params: '("#chan" "hi"))
; => "@time=2026Z;account=alice :alice!a@host PRIVMSG #chan :hi"

Convert an IRC command to a wire-format string (with CRLF).

(irc-command->string "PRIVMSG" "#channel" "Hello")
; => "PRIVMSG #channel :Hello\r\n"