~aleteoryx/tclircc-docs

tclircc-docs/irc.tcl.md -rw-r--r-- 14.2 KiB
52479f5cAleteoryx update for commit 614b00a3 a month ago

#irc.tcl

The core protocol library. irc.tcl exclusively handles the raw stream protocol and event dispatch. All other responsibilities are out-of-scope for it.

It exposes the irc namespace, which contains the logic for message-parsing, message validation, channel management, event dispatch, and various utilities listed below.

#Getting a channel

irc.tcl provides 2 ways to connect to an IRC server.

#irc::connect hostname port ?usetls?

Connects to hostname:port, and sets up all the necessary state. If usetls is set to true, the tls module will be used to connect, instead of the builtin socket command. If unset, socket will be used.

Channel metadata is initialized with proto, hostname, port, and uri set.

#irc::enroll chan ?meta?

Sets up the internal state necessary for chan to be used as an IRC socket. It is called internally by irc::connect. This command is exposed for the use-case where an IRC channel might have a more bespoke acquisition process than a simple socket connection.

meta is the initial state of the channel metadata.

#Listening to it

irc.tcl provides an event dispatch system, via a fileevent script registered on the IRC channel. Events are dispatched by matching their patterns against incoming messages.

#irc::listen subcommand chan

Enable or disable the fileevent script for the dispatch system.

#irc::listen on chan

Apply the fileevent wrapper to chan. Returns the previous fileevent wrapper.

#irc::listen off chan

Remove the fileevent wrapper from chan. Errors if it is not the irc wrapper.

#irc::listener subcommand chan ?arg ...?

Configure listener-type event handlers.

Listener-type handlers are scripts, executed in either a sub-interpreter or a seperate thread. Interpreter listeners should yield themselves with after during long operations, and never block for extended periods. If that is infeasible, threaded listeners are recommended.

listener-type scripts are spawned with the variable dispatch set to the channel they are to recieve dispatches over. Each line will be a dict with contents as described in Event Dispatch Contents. Interpreters are given access to the Dispatch-aliased IRC commands, while threads are given the variable parent, the ID of the main thread.

When a listener is removed, it will recieve a message of just end. It should perform necessary cleanup quickly, as the application is likely exiting. Threads may simply thread::release themselves, while interps may call the provided selfdestruct.

#irc::listener add chan ?-thread? patlist script

Registers script as a listener-type handler on chan, matching patlist as described below. Returns an id that can be passed to irc::listener remove or irc::patlist.

If -thread is passed, it will be created as a threaded listener, otherwise it will be created in a sub-interpreter.

#irc::listener remove chan id

Unregisters the listener identified by id from chan.

Ignores requests for nonexistent handlers or handlers of the wrong type.

#irc::handler subcommand chan ?arg ...?

Configure handler-type event handlers.

Handler-type event handlers execute a script everytime a message is matched. These scripts can be executed in either the global scope, a sub-interpreter, or a seperate thread.

handler-type scripts are spawned with the variable dispatch set as described in Event Dispatch Contents. Interpreters are given access to the Dispatch-aliased irc commands, while threads are given the variable parent, the ID of the main thread.

When a handler is removed, if it has an interpreter or thread, it will be deleted. Applications with persistent state should take care to store it to disk after each command, or use one of the other dispatch types.

It is essentially assumed that an individual handler will have exclusive ownership of its sub-interpreter or thread.

#irc::handler add chan ?-thread? patlist script ?interp-or-thread?

Registers script as a handler-type handler on chan, matching patlist as described below. Returns an id that can be passed to irc::extern remove or irc::patlist.

If neither -thread nor interp-or-thread are set, script will be executed in the global scope. If interp-or-thread is set, it is the ID of the interpreter to execute the script in. If -thread is set, script is executed in a persistent seperate thread. If -thread and interp-or-thread are set, interp-or-thread is the ID of the thread to use.

#irc::handler remove chan id

Unregisters the handler-type event handler identified by id from chan.

Ignores requests for nonexistent handlers or handlers of the wrong type.

#irc::extern subcommand chan ?arg ...?

Configure extern-type event handlers.

Extern-type event handlers are backed by a pair of channels. One is for message dispatch, one is for message replying. Dispatch comes in pairs of lines, the first being the source channel and the second being the raw IRC message. Replies are just single IRC messages. Due to this asymmetry, it is OK to reuse a dispatch channel for multiple extern handlers, but not OK to reuse a reply channel. You will need to make use of FIFOs or pipes for I/O multiplexing.

When an extern-type handler is removed, the channel id and end will be written to it. The dispatch pipe is never closed by irc.tcl. The reply pipe will be closed immediately. Ensure code that uses multiple handlers accounts for this.

#irc::extern add chan patlist ochan ichan

Registers ochan and ichan as the dispatch and reply pipes of a extern-type handler on chan, matching patlist as described below. Returns an id that can be passed to irc::extern remove or irc::patlist.

#irc::extern remove chan id

Unregisters the extern handler identified by id from chan.

Ignores requests for nonexistent handlers or handlers of the wrong type.

#irc::patlist chan id ?patlist?

Get or set the message pattern list for handler id on chan. If patlist is supplied, it will override the current one and return patlist, otherwise it will return the current pattern list.

#Message Pattern Lists

Message pattern lists are lists of lists of string match patterns. Messages are matched on the command and the first N-1 params, where N is the length of the message pattern. If the first N segments all match, the match succeeds. If a message is shorter than a pattern, the match fails. If any of the message patterns in the message pattern list for a handler match, the handler is called.

#Message Pattern List Examples:

# pattern:
*

# messages:
PRIVMSG #general :what's up gamers
# matches
PRIVMSG #amehut :bot, do something
# matches
PRIVMSG #bot :do something
# matches
PING foo
# matches


# pattern:
{{}}

# messages:
PRIVMSG #general :what's up gamers
# doesn't match
PRIVMSG #amehut :bot, do something
# doesn't match
PRIVMSG #bot :do something
# doesn't match
PING foo
# doesn't match


# pattern:
{{PRIVMSG * {bot, *}} {PRIVMSG #bot}}

# messages:
PRIVMSG #general :what's up gamers
# doesn't match
PRIVMSG #amehut :bot, do something
# matches
PRIVMSG #bot :do something
# matches
PING foo
# doesn't match


# pattern:
PING

# messages:
PRIVMSG #general :what's up gamers
# doesn't match
PRIVMSG #amehut :bot, do something
# doesn't match
PRIVMSG #bot :do something
# doesn't match
PING foo
# matches

#Event Dispatch Contents

The event dispatch dictionary contains the following properties:

  • rawmsg: the raw IRC message
  • chan: the source channel
  • tags: the tags portion of the message
  • src: the source portion of the message
  • srctype: the type of message source, either servername or user
  • srcparts: the srcparts dict, returned from irc::src parse
  • cmd: the command of the message
  • params: the params of the message

#Dispatch-aliased irc Comands

In listener and handlers, the following commands are aliased:

  • irc::esc
  • irc::extern
  • irc::handler
  • irc::is
  • irc::listener
  • irc::meta
  • irc::msg
  • irc::patlist
  • irc::src
  • irc::tags
  • irc::unesc

Additionally, the relevant channel is shared for direct writing.

#Channel Metadata

Every channel is initialized with metadata when **enroll**ed. This may be empty, but it's still there. Metadata is backed with a dict.

#irc::meta subcommand chan ?arg ...?

A thin wrapper around dict commands that routes them to the channel metadata dict for chan.

#irc::meta read chan

Returns the entire channel metadata dict for chan.

#irc::meta exists chan key ?key ...?

A thin wrapper around dict exists.

#irc::meta get chan ?key ...?

A thin wrapper around dict get.

#irc::meta set chan key ?key ...? value

A thin wrapper around dict set.

#irc::meta unset chan key ?key ...?

A thin wrapper around dict unset.

#Parsing

irc.tcl exposes a handful of interfaces for parsing IRC primitives.

#irc::tags subcommand ?arg ...?

An interface that allows you to treat an IRC tags string like a dict. O(n) time complexity, but realistically it will never handle a list of more than 3 or 4 so who cares.

All subcommands operate on values, not variables. Escaping and unescaping are handled transparently.

#irc::tags create ?key value ...?

Creates an IRC tags string from the passed key-value pairs and returns it.

Errors if any key includes forbidden characters.

#irc::tags dict tags

Converts the passed tags string to a dict with identical members.

#irc::tags exists tags key

Returns true if tags includes they key key. Returns false otherwise.

#irc::tags get tags ?key?

If key is set, returns the value associated with it, or an error if it does not exist.

If key is unset, returns a list of key-value pairs, similar to dict get.

#irc::tags merge ?tags ...?

Merges all arguments into one tags string, and returns it. Each tags may be either a tags string or a dict string.

Errors if any key includes forbidden characters.

#irc::tags remove tags key

Returns a new tags string without key.

#irc::tags set tags key ?value?

Returns a new tags string with key set to value. If value is unset or empty, the key is serialized alone, with no equals sign.

Errors if key includes forbidden characters.

#irc::src subcommand ?arg ...?

IRC source parsing and generation utility.

#irc::src parse src ?partsVar?

Parses src and returns either server or user, indicating the type of src. If partsVar is specified, it will be set to a dict containing, for user values of src, the nick, username, and host segments as entries, and for server values, the servername segment as an entry.

Errors if src is not a valid IRC source string.

#irc::src server servername

Returns an IRC server source with servername as its component.

Errors if servername is not a valid servername.

#irc::src user *?-user user? ?-host host? nick

Returns an IRC user source with nick as the nickname component. If -user is specified, the user field will be inserted. If -host is specified, the host field will be inserted.

Errors if any argument is invalid for its role.

#irc::msg subcommand ?arg ...?

IRC message formatting and parsing utility.

#irc::msg fmt ?-tags tags? ?-src src? cmd ?arg ...?

Returns an IRC message string with cmd as the command. All arguments present are appended, and the final argument is always serialized as trailing. If -tags is specified, the tags field will be inserted. If -src is specified, the src field will be inserted.

Errors if any argument is invalid for its role.

#irc::msg send chan ?-tags tags? ?-src src? cmd ?arg ...?

Calls irc::msg fmt internally, and sends the message over chan.

#irc::msg parse message tagsVar srcVar cmdVar paramsVar

Parses the message in message. tagsVar will be set to the tags, if present. srcVar will be set to the source, if present. cmdVar will be set to the command. paramsVar will be set to a list of parameters in the message, if any.

#irc::is type value

A general validation utility for IRC strings. Returns true if value is a valid string of type type. Returns false otherwise.

For more details on how these are implemented, read the Modern IRC Client and Message Tags specifications.

Possible values of type:

  • cmd: an IRC command, e.g. PRIVMSG or 421.
  • cmd::named: a named IRC command, e.g. PRIVMSG
  • cmd::numeric: a numeric IRC status code, e.g. 421
  • misc::dict: a tcl dict, e.g. foo bar a 1 b 2 c 3 nested {di cts}
  • msg::param: an IRC message parameter, e.g. #general
  • msg::trailing: an IRC message trailing segment, e.g. hi guys! :D
  • nick: an IRC nickname, e.g. aph
  • src: an IRC source, e.g. aph!~alice@example.com or irc.example.com
  • src::user: an IRC user source, e.g. aph!~alice@example.com
  • src::servername: an IRC servername source, e.g. irc.example.com
  • src::part: a non-nickname part of an IRC source, e.g. ~alice
  • tags: a tags string, e.g. account=aph;time=2024-08-03T23:36:00.320Z
  • tags::tag: an IRC tag string, e.g. account=aph
  • tags::key: an IRC tags key, e.g. account
  • tags::value: an IRC tags value, e.g. aph

#irc::esc type value

Returns value, escaped by the rules of type.

Possible values of type:

  • tags::value: an IRC tags value, e.g. aph or int\smain\s()\n{\n\s\sputs("Hello,\sWorld!")\:\n\s\sreturn\s0\:\n}

#irc::unesc type value

Returns value, unescaped by the rules of type.

Possible values of type:

  • tags::value: an IRC tags value, e.g. aph or int\smain\s()\n{\n\s\sputs("Hello,\sWorld!")\:\n\s\sreturn\s0\:\n}