I know I sent a mail about this subject to this list somewhere around september last year. The response was to commit it to CVS, in order to allow review. So, as soon as I finish writing the log message I can commit it (hopefully tonight).
Since the last time, I have rewritten most of the code. Below is the document I used to guide my design. I could commit that too, but was not planning to.
Comments?
------------------------ Mode tracking 'design' -*- text -*-
**** Goal:
enabling the cl-irc library to track modes automatically
**** Mode tracking requirements for cl-irc:
1) mode tracking for modes as defined by rfc281? 2) conformance with draft rfc on RPL_ISUPPORT 3) mode tracking for arbitrary (but defined) modes (which may have been advertised by RPL_ISUPPORT) 4) keeping channel modes and users list in sync where mode arguments are in fact user objects
5) application side modes to be attached to channels and users (my actual reason to implement it)
6) The application should be able to benefit from any new functions added to parse MODE command arguments.
Ad 1 ====
From a storage perspective there are 3 different kinds of modes
in the rfc:
- Those that can be 'on' or 'off' (being identified +e on dancer-ircd) - Those that hold a value when assigned (channel user limit and channel keyword come to mind) - Those that hold a list of values (ban and ban-except for example)
Interpreting the stored value for 'on/off' modes as generalized boolean values, this leaves two different mode types: those that hold a single value and those that hold a list of values.
From a command perspective there are 4 kinds of modes in the rfc:
- Those that take a parameter when (un)set, but which the client can send to the server without parameters in order to query the current setting - Those that always take a parameter, these have two sub-categories A) Those where the parameter is a nickname B) Those where the parameter isn't a nickname - Those that take a parameter when set but not when 'unset' - Those that never take a parameter
The library will need to be able to parse each of these four mode types. When used as a client library, the optional argument form is not relevant however. The default hook will ignore that case.
Ad 2+3 ======
The server can - through the RPL_ISUPPORT message - advertise which channel modes it supports. Conformance with the draft rfc means that the client needs to be able to parse arbitrary modes with the advertised properties.
Next to that, the client may want to define its own modes (which are not automatically tracked).
Ad 4 ====
Some of those modes described above may need to be kept in sync with users joining / leaving the channel. This is supposed to be automatic.
Ad 5 ====
Yea, ok, well, the system needs to be extensible.
**** Implementation decisions & consequences
Ad 1+5 ======
The library defines two classes which are used to store modes: one for the single-value modes, the other for multi-value modes. In order to allow extensibility, an application can implement its own classes derived from the superclass `irc-mode' in order to make mode tracking automatic.
Ad 2+3 ======
The library needs to be able to create its own mode objects in reaction to server messages setting mode values. For this purpose, it defines a mode-description structure which holds all values required.
If applications define their own modes, they need to file such structures with the connection object to allow it to create the modes.
The library integrates with the RPL_ISUPPORT framework by parsing the CHANMODES and PREFIX values in that response and creating mode-description structures for those modes itself. While doing so, it concatenates the PREFIX modes to the list from CHANMODES as described by the draft rfc.
The modes in the PREFIX string take nicknames as their argument automagically.
Ad 4 ====
For the library to be able to automatically maintain the list of properties while user join and leave messages are processed, it needs to know which modes contain users (or user objects) as their value(s). The mode-description structure is used for this purpose too.
Ad 6 ====
The library will provide functions to:
- Translate the CHANMODES and PREFIX arguments of the RPL_ISUPPORT response into a list of mode-description structures - Interpret the MODE command arguments and return a list of mode-change instructions. - Create custom mode descriptions (in order to create custom modes) - Manipulate and query mode values
**** Implementation
`decode-mode-command' should be able to process two types of commands:
1) MODE <target> <mode-char>+ 2) MODE <target> ([+|-]<mode-char>+ <mode-parameter>*)+
If the input does not match either pattern, the return value will be nil. The returned values are (values mode-list query-p) where query-p will be non-nil if the mode command matches the first syntax. If it matches the second pattern, the mode-list will be a list of lists describing the mode-changes as follows:
( [#+|#-] <mode-char> <mode-value>? )
(DONE)
`mode-descs-from-isupport' returns a list of mode-description structures describing the modes in the CHANMODE and PREFIX values. The resulting list can be used for setting up automatic mode tracking for default hooks. (DONE)
`parse-mode-arguments' returns a list of mode-change instructions. (DONE)
`irc-mode', `single-value-mode' and `multi-value-mode' are classes which are used to track mode values. Applications can define their own derivatives in order to have custom behaviour for self-defined modes. (DONE)
`add-mode', `get-mode', `set-mode', `unset-mode' and `remove-mode' operate on channel and user objects to manipulate modes. The effect of `set-mode' and `unset-mode' is determined by the class from which the mode was instantiated. (DONE)
`has-mode-p' and `has-mode-value-p' operate on user and channel objects and return non-nil values if the given mode or mode/value are associated with it. More specifically, has-mode-value-p returns whatever `has-value-p' returns when invoked on the mode object (usually its value) and `has-mode-p' returns the mode object itself. (DONE)
`mode-description' describes the different options which the application uses or expects over the connection from the server. It has the following fields:
- char : character used over the connection to identify this mode - symbol : symbol the application uses to refer to this mode (the default symbols are keywords) - param-on-set-p, param-on-unset-p : indicate whether the library should expect an argument when receiving either on the operations; valid values are: nil (false), :optional, :optional-for-server or any other value (true) - nick-param-p : non-nil when the argument specifies a user (by nickname) - class : specifies the class to instantiate upon creation of a new mode
(DONE)
-------------------------------- EOF
On Mar 19, 2005, at 2:08 PM, Erik Huelsmann wrote:
I know I sent a mail about this subject to this list somewhere around september last year. The response was to commit it to CVS, in order to allow review. So, as soon as I finish writing the log message I can commit it (hopefully tonight).
Since the last time, I have rewritten most of the code. Below is the document I used to guide my design. I could commit that too, but was not planning to.
Comments?
Hi Erik,
What you have here looks great. The only question I have is that you seem to be treating modes which hold a value and modes which are on / not on the same. This is fine but I don't think that generalized booleans should be the right way to look at it: rather modes whose value are t / nil are modes which are on / not on, and modes which have some non-boolean value are modes which hold a value. Does this make sense? Is it the way you've implemented it in the code?
Thanks! -- Brian Mastenbrook brian@mastenbrook.net http://www.iscblog.info/
Hi Erik,
What you have here looks great. The only question I have is that you seem to be treating modes which hold a value and modes which are on / not on the same. This is fine but I don't think that generalized booleans should be the right way to look at it: rather modes whose value are t / nil are modes which are on / not on, and modes which have some non-boolean value are modes which hold a value. Does this make sense? Is it the way you've implemented it in the code?
Hi Brian,
The code differentiates two different mode types: single and multi value. The single value mode can be used for two different purposes: modes with on/off values and modes with a non-boolean value. (This could easily be changed ofcourse, but read on...)
The behaviour of the single value mode is based on the premise that boolean value modes don't take an argument when being set, whereas non-booleans do take a value. When the set-mode-value method is called for a single-value-mode with a nil value, the mode acts as a boolean and sets the value to t.
As I'm writing this, I think I should add a boolean-value-mode class which does exactly that and makes the library a bit more transparent. set-mode-value for a single-value-mode with a value argument of nil would (as expected) set the value to nil (that being equal to unset-mode-value).
The boolean-value-mode can be implemented in maybe 10 lines. Then one more line change is required to actually use the mode. 1 or 2 lines can be removed from the single-value-mode which special-case the nil value argument to the set-mode-value method. All in all not something big.
Thanks for your comments.
bye,
Erik.