(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-messageprocedureConstruct a irc-message struct.
irc-message?procedureTest if a value is a irc-message struct.
irc-message-rawprocedureGet the raw field of a irc-message struct.
irc-message-tagsprocedureGet the tags field of a irc-message struct.
irc-message-prefixprocedureGet the prefix field of a irc-message struct.
irc-message-nickprocedureGet the nick field of a irc-message struct.
irc-message-userprocedureGet the user field of a irc-message struct.
irc-message-hostprocedureGet the host field of a irc-message struct.
irc-message-commandprocedureGet the command field of a irc-message struct.
irc-message-paramsprocedureGet the params field of a irc-message struct.
irc-message-trailingprocedureGet the trailing field of a irc-message struct.
irc-message-targetprocedureGet the target of a message (first param, usually channel or nick).
(irc-message-target msg) ; => "#channel" or "nickname"irc-message-textprocedureGet the text content of a message (alias for trailing).
irc-message-tagprocedureLook 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") ; => #firc-message-has-tag?procedurePredicate: is the named tag present on this message? Returns #t for both valueless and value-bearing tags.
irc-tag-escapeprocedureEscape a tag value per IRCv3 message-tags rules.
; -> : space -> s -> \ CR -> r LF -> n
(irc-tag-escape "hello world; rest") ; => "hello\\sworld\\:\\srest"irc-tag-unescapeprocedureUnescape 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-irc-tagsprocedureParse 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))irc-tags->stringprocedureSerialize a tags alist (without leading @).
(irc-tags->string '(("time" . "2026Z") ("echo" . #f)))
; => "time=2026Z;echo"parse-irc-messageprocedureParse 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 ...>make-irc-commandprocedureCreate 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"make-trailing-commandprocedureBuild 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"make-irc-command/tagsvariableBuild 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"irc-command->stringprocedureConvert an IRC command to a wire-format string (with CRLF).
(irc-command->string "PRIVMSG" "#channel" "Hello")
; => "PRIVMSG #channel :Hello\r\n"