JOBYX REFERENCE/MANUAL
    Herein you will find enough information to write basic JobyX modules,
    and/or get JobyX running. If you need any other assistance, visit the
    SourceForge project page at <http://www.sourceforge.net/projects/jobyx>.

  JobyX configuration
    The file jobyxconfig.pm contains global constants that JobyX uses to
    login to the server, load default modules, etc. A sample is provided,
    called jobyxconfig.pm.demo. Rename this to jobyxconfig.pm and edit it to
    suit your needs. Note that you should remove the sample language
    customization constants, unless you want JobyX to parse shouts instead
    of tells.

    For information on customizing what you call your TUs (Trusted Users),
    see the top of JX_Mod/CORE/English.pm. It will explain how to do this by
    editing jobyxconfig.pm.

    I have tried to supply meaningful default command levels for each
    command. Feel free to tweak them. They are located in JX_Mod/CORE.pm.
    After you modify them, *back up the file* lest a JobyX update clobber
    your modified version. This also means you will have to re-edit them
    after a JobyX update. The backup you made will help you remember which
    settings you chose.

  Location of Modules
    Modules should be placed in "*path_to_jobyx*/JX_Mod/" and should have a
    package name of "JX_Mod::*your_module_name*". For example, my Tourney
    module is placed in /home/chris/jobyx/JX_Mod/Tourney.pm and the first
    line is "package JX_Mod::Tourney;". Note that you may not create nested
    modules; "JX_Mod::Tourney::Seek" won't work.

  User Levels
    Every user has a numeric userlevel, which defaults to 10, unless the
    user is a guest, then the level is 1. Userlevels can range from 0-100.
    All commands should be above level 0, as this level is reserved for
    abusers. Userlevels can be customized by editing the @levels definition
    in jobyx.pl. The following levels should ALWAYS be in place -
    modification could cause problems.

    0 = Abuser

    1 = Guest

    10 = Player

    99 = Operator

    Any level defined as 100 will be ignored, as it is reserved for JobyX
    itself. On startup it will automatically set level 100 to the bot's
    server handle.

    You may modify the names of the default levels, but make sure they still
    mean the same thing. For example, 0 could be "Offender", 1 could be
    "Unreg", 10 could be "User", and 99 could be "Super User".

  Qualifications
    ----------------

    This is experimental and subject to change. In the future, requirements
    will be tested via a verification sub. For example:

            sub verify_insmod {
                    my @ui=&::userinfo($_[0]);
                    $ui[0] or return 0;
                    return ($ui[1]->[4] >= 99 or &::inlist('tadmin',$_[0]));
            }

    While a bit clunkier, this will allow more complicated checks, as well
    as a cleaner display of ListCommands.

    Note that if only a userlevel check is needed, a number may be used in
    place of a verification sub. This level would also be displayed on the
    ListCommands output. If a verification sub is used, the userlevel will
    not be displayed on this output.

    ----------------

    To restrict access to commands by userlevel or position in lists, a
    (sort of) language has evolved. Here are the possible arguments:

    number (like 10 or 50)
        Specifies the userlevel required.

    ly:<list>
        The user must be in the "<list>" list.

    ln:<list>
        The user must not be in the "<list>" list.

    You can combine these with the following "operators":

    +   logical AND

    ,   logical OR

    Operators are read from left to right. There is no "order of
    operations".

    Note that parentheses may *not* be used for grouping! If you need to do
    something really complex, verify it in the verification/command
    subroutine.

    Any time that "userlevel" is mentioned, it really means "qualifications
    string" unless it says "numeric userlevel".

  Core Verification Subs
    The core provides several subroutines for common variable/list types.
    Note that some have no application for a list. For example, &::boolean
    probably wouldn't make a good list validator. To create your own
    verification subs, use either of the following formats:

    lists
        &verifysub($handle,$list,$new_item)

    variables/avariables
        &verifysub($handle,$var,$new_setting,$old_setting)

    Note that it is not required to use any parameters. For example,
    &::handle only uses $new_item (or $new_setting for variables) to
    validate the item. On the other hand, &::boolean uses $old_setting when
    $new_setting is null. It is possible to create subs for lists *and*
    variables, like &::handle or &::notnull.

    \&::boolean *variable*
        Looks for "n/no/off/0" for false, and "y/yes/on/1" for true. Return
        value will be (0) when value is invalid, (1) when value is OK, or
        (2,0 || 1) if a string is used instead of "0" or "1". If the new
        setting is omitted, the old setting will be negated and returned.

    \&::notnull *list, variable*
        Return true when the new item is not null or whitespace.

    \&::true *variable*
        Always return true. (Dangerous! Consider \&::notnull.)

    \&::handle *list, variable*
        The item must be a server handle. Automatic name completion (I.E.
        "techde" -> "TechDeck") will occur.

    \&::handle_no_complete *list, variable*
        Same as above, but no completion. (I.E. "techde" -> error.)

    \&::handle_lower *list, variable*
        The person adding the item must be on a higher numeric userlevel
        than the person to be added.

    \&::handle_lower_no_complete *list, variable*
        Combination of previous two subs.

  Server Connection
    Because JobyX uses a nonblocking socket, detecting when the server
    closes the connection is not as easy as waiting for input to run out. In
    order to detect a broken connection, JobyX will examine each line
    against the regular expressions found in @Lang::CORE::connection_fail.

    However, JobyX anticipates that your connection may not always be
    cleanly cut, so if it doesn't recieve anything from the server for one
    full minute, it will send the bogus "notacommand" command. If JobyX
    doesn't recieve a reply within 30 seconds, it assumes that the
    connection was dropped, saves all data, and restarts itself.

  Command Definition
    Commands are the heart of JobyX's operation. When JobyX is told
    something, it will attempt to locate the best match from the commands it
    knows, and pass execution to the appropriate subroutine.

    @command=(

    $name,
        This is the name of the command (duh). It is what users will type to
        execute it. You should stick with upper and lowercase letters. An
        exception is when you are creating a one-character alias-type-thing,
        like "+", "-", "=", or ".". If a command's name is one character,
        and is not a letter, then no space is required between it and the
        first argument. All other command names *must* start with a letter.
        If you make a command "!helloworld", nobody will be able to use it,
        because the command parser will try to execute the "!" command with
        "helloworld" as the first argument.

    \&handler,
        Reference to the subroutine that will handle this command. The sub
        will be called like "&handler($handle_of_caller,@args)", where
        $handle_of_caller is the handle of the user that is executing the
        command, and @arg is the arguments supplied with the command.

    $execlv,
        Userlevel required to execute the command.

    $seelv,
        Userlevel required to see the command. Not neccessary if
        "$visible==0", but you should still supply it so you don't confuse
        yourself.

    $args,
        Maximum number of arguments, 0 for infinite.

    $visible,
        Whether or not the command will be displayed on the ListCommands
        output. Generally, this should be on for the first instance of a
        command, and off for aliases (commands with different names but the
        same command handler). See the CORE module for some examples.

    );

  List Definition
    JobyX comes with a standard ICS-compatible list feature. Lists can be
    used to store a variety of information including handles, IP addresses,
    email addresses, port numbers, "bad words", and anything else you can
    think of.

    @list=(

    $name,
        The name of the list. This is used with the
        +addlist/-sublist/=showlist commands.

    $seelv,
        Userlevel required to see the list on the output of ShowList when no
        listname is specified.

    $viewlv,
        Userlevel required to view the contents of the list.

    $addlv,
        Userlevel required to add an item to the list.

    $sublv,
        Userlevel required to remove an item from the list.

    $private,
        If true, each user will have their own copy of the list, like
        "notify" or "censor". If false, the list is shared, like "admin" or
        "muzzle".

    \&verifysub,
        Reference to a subroutine that will be called to verify that an item
        is indeed valid for this list. It will *only* be called when an
        AddList is in progress. SubList will not make use of it. It will be
        called like:

                &verifysub($handle_of_caller,$list_name,$item)

        and will expect a return list of:

                return (1)              #item is OK, add it
                return (2,$item)        #add $item instead
                return (0,$error)       #don't add the item, and give the error
                                        #$error to the caller.

        The "(2,$item)" format may be used to add a different item instead.

        If anything is acceptable, set the reference to "\&::notnull".

    \&addsub,
        Reference to a subroutine that will be called *after* an item has
        been added. It will be called like:

                &addsub($handle_of_caller,$list_name,$item,$add)
                # note that &subsub has the same parameters
                # $add == 1 if the item was added

        A return value of true will qtell $item the You have been added
        to/removed from the $list_name list by $handle_of_caller. message.
        If you don't want to write a handler just to return true, set the
        reference to "\&::true". You should definately not return true if
        the list contains something other than handles. To make the list
        automatically add a comment for the user, set the reference to
        "\&::list_comment", which also returns true, meaning that the user
        will also be notified.

        This can be employed to do a variety of things. For my Tourney
        module, I use it to call "\&::setlevel" to change the user's level
        to 20 when $list_name eq Manager, providing that the user is below
        level 20. I could also make it "&::send("tell 6 $item has been made
        a TM by $handle_of_caller.\n")", though that could be overkill.

    \&subsub,
        Reference to a subroutine that will be called *after* an item has
        been removed. See "\&addsub" for details.

    $max,
        Maximum number of items that can be in this list. If you don't want
        a limit, use 0. It's a good idea to put limits on personal lists so
        that malicious users can't eat disk space or memory. Global lists
        shouldn't need a limit unless you're worried that your bot's admins
        won't behave. But you're the one training them, right?

    $type,
        The plural name of what is stored on the list. This is displayed
        like -- $listname list: "$num $type" --. For example, lists that
        store handles should stick with "names"; lists that store IP
        addresses may want to use "ips"; etc.

    \@entries,
        Reference to a list containing the default items for this list. Note
        that these will be used *only* if the list file on disk *does not
        exist*. If the file is merely empty, the list will be too. For
        example, my ProxyBuster module defines a few default ports to scan.
        You probably don't want to set this for personal lists.

    );

  Variable Definition
    Variables are a simple, yet effective way for each user to keep their
    own settings. First, check to see if a personal list would be better for
    the option you're considering. For example, "censor" probably wouldn't
    make a good variable; a personal list would be more efficient.

    @variable=(

    $name,
        Name of the variable. Used in Set and displayed in Variables.

    $havelv,
        Userlevel required to have the variable at all. In my Tourney
        module, for example, there is an Open variable, which is used by TMs
        to specify whether or not they are open to receive tourney requests.
        This variable's $havelv is 20, thus preventing non-TMs from getting
        confused about it.

    $default,
        Default value of the variable.

    $visibility,
        This specifies who can see the setting:

        0 = Everybody can see the setting. 1 = Each user can only see
        his/her own setting. 2 = Nobody can see the setting.

    \&verifysub,
        Reference to a subroutine that will be called when a variable is
        about to be set. It will be called like:

                &verifysub($handle_of_caller,$variable,$setting)

        and expects a return value of:

                return (1)              # setting is OK, set it
                return (2,$setting)     # set to $setting instead
                return (0,$error)       # setting is not OK.  use error $error
                                        # if present, else use generic "Bad
                                        # setting for variable $variable."

        If the variable is a standard boolean (on/off) variable, use
        "\&::boolean".

    \&setsub,
        Reference to a subroutine that will be called *after* a variable has
        been set. It will be called like:

                &setsub($handle_of_caller,$variable,$setting)

        In my Tourney module, I use &::send to add or remove the user from
        the server's TM list depending on the $setting of the Open variable.
        This will turn their (TM) light on or off.

        Note that you can use different subs for different variables, or the
        same sub for a few or all variables. It's up to you how you want to
        use it. $variable is supplied specifically for this case. If you use
        different subs for different variables, you can safely ignore it.

    );

  Avariable Definition
    Avariables are in a sense JobyX's personal variables. They should be
    used to hold things that affect JobyX's behavior globally. Like
    variables and personal lists, consider whether a public list would be
    better suited. In my BadWord module, for example, I use a global list,
    BadWords, to store the list of unacceptable terms. However, the same
    module uses the MaxWarnings avariable to set how many warnings a user
    can receive before he/she is automatically muzzled. The ASet command is
    used to modify avariables, and you may view them by using AVariables.

    @avariable=(

    $name,
        Name of the avariable. Used with ASet to modify it, and displayed
        with AVariables.

    $seelevel,
        Level required to see the avariable.

    $setlevel,
        Level required to set the avariable.

    \&verifysub,
        Reference to a subroutine that will be called when this avariable is
        about to be set. See the "\&verifysub" definition under Variable
        Definition.

        It is probably safe to use "\&::notnull" here, since only people in
        the aconfig list can use it, and that will most likely be just you
        and a few select others that (supposedly) know what they are doing.
        If you plan on having a lot of (clueless) people in this list, you
        may wish to define verification subs.

    $default,
        Default value of the avariable.

    \&setsub,
        Reference to a subroutine that will be called *after* an avariable
        is set. It will be called like:

                &setsub($handle_of_caller,$avariable,$setting)

        You probably won't need to use this one, but it is provided for
        flexibility.

    );

  Loading a Module
    When a module is inserted, its "ICS_init" sub will be called like:

            &JX_Mod::<module>::ICS_init($username,$JobyX_version,$handle_of_insertee)

    $username is JobyX's handle on the server, and $JobyX_version is the
    version of JobyX in the format major.minor.revision. You can use this to
    check for a particular version. $handle_of_insertee is the server handle
    of the user that inserted the module. Note that the module may be
    loading before the server connection is established, so don't do
    anything requiring a connection. Your module's "ICS_begin" sub will be
    called (without any parameters) after the connection to the server is
    complete. If a bot operator inserts the module, "ICS_begin" will be
    called immediately following "ICS_init".

    The real meat of this subroutine is the return value. This is your
    chance to tell JobyX about any commands, lists, variables, and
    avariables your module provides.

            return (
            [\@command1   ... \@commandN],
            [\@list1      ... \@listN],
            [\@variable1  ... \@variableN],
            [\@avariable1 ... \@avariableN],
            \&idlesub
            );

    Notice that the actual commands/lists/etc are *references*. This is
    *very* important! The syntax of the @command/@list/etc lists are
    specified above.

    Returning "(0,$error)" will abort the load. I use this in my Tourney
    module to abort a load when a stored tourney can't be read.

    "\&idlesub" will be called when there is no input from the server. This
    means that your sub could get called as many as five times per second!
    This sub should be quick and efficient--too much work could delay
    JobyX's response, as it will not begin reading from the server again
    until this function returns. In my Tourney module, I use it to
    periodically announce tourneys. Like most other subroutine references,
    you may supply "undef" if you're not going to use it.

    If one of your modules depends on another, use
    "&::ismoduleloaded($module)" to determine if a required module is
    loaded. Generally, modules should not be dependant on other modules. If
    you think two modules need each other to function, consider merging them
    in to one module. This will be cleaner and more efficient.

    Also note that you can't specify what number the module will have. More
    on this in Conflicts.

  Removing a Module
    When a module is about to be removed, the "ICS_term" sub will be called
    like:

            &Module::ICS_term($handle_of_remover)

    $handle_of_remover is the server handle of the user that removed the
    module. If the handle is *DEBUG*, JobyX itself unloaded the module,
    probably during a shutdown/restart.

    Like "ICS_init", you can return stuff here:

            return (0)              #All OK, remove the module
            return (1,$error)       #Something's wrong, don't remove the module,
                                    #and tell the remover $error.

    In my Tourney module, I return "(1,$error)" when I can't save the
    tourney list, for example. If the error is not serious, it is a good
    idea to return "(0)". Note that returning "(1,$error)" will not cancel
    the unload if the connection to the server was terminated, or if a bot
    operator forced a shutdown/restart.

    This sub should be used to save any persistant items. Note that variable
    settings and lists are managed by JobyX; you don't need to do anything
    here to save those things.

    If your sub returns "(0)", Joby will remove all commands, lists,
    variables, and avariables specific to your module from memory. This
    allows you to customize a bot on-the-fly. Note that the actual module
    code will not be removed, but all connections between JobyX and it will
    be effectively severed. No hooks will be called.

  Core Features
    The core provides some other subs that you can call from your modules:

    $::last_flags
        This is about the only global *variable* of interest. It will
        contain the flags captured from the last tell. See the Tell sample
        module for an example. I was considering passing it to the command
        handler, right after the user's handle, but it's not often you'd
        need it, so this seems more efficient and out-of-the-way.

    &::columns(@items)
        Arrange the elements of @items in columns. The width will never
        exceed 79. The return list is suitable for feeding to &::qtell.

    &::execcommand($handle,$command,$args)
        Pretend that user $handle is executing command $command with
        arguments $args. Note that $args *is not a list*! Doing this:

                &::execcommand('Hitmonchan','addlist','censor','tupac');

        would not have the desired effect. Hitmonchan would be told how to
        use AddList. This is probably what you want:

                &::execcommand('Hitmonchan','addlist','censor tupac');

    &::getavar($avariable)
        Returns the current value of the $avariable avariable.

    &::getvar($handle,$variable)
        Returns the current value of $handle's $variable variable. Note that
        this is not always the same as the data contained in the %var hash
        in the user's parray, which will be unset if the variable hasn't
        been set by the user.

    &::getnextline
        Returns the next line from the server. Note that when called outside
        of the core input loop, lines are "push()"ed to an array and
        "shift()"ed off by the input loop after your handler returns. This
        means that any lines read via &::getnextline *will* be parsed by the
        input loop, which is probably a good thing. Also note that "\&idle"
        subs may be called while waiting for an input line, so don't do
        anything in your idle sub that could interfere with the sub you're
        calling &::getnextline from.

    &::getpi($handle,$expect_complete)
        Returns user info for $handle. Use &::userinfo if all the info you
        need is supplied by it, as it is more efficient. Generally, you
        should only need to call &::getpi for $flags or "\%ratings". Note
        that $::last_flags could possibly take care of the flags you need,
        but on some servers xtells don't contain flags, so look out.

        If $expect_complete is on, &::getpi will not return success when the
        server returns a completed handle. For example, if the user HotPawn
        existed, "&::getpi("hotpaw",1)" would fail. If $expect_complete was
        off, or omitted, &::getpi would return stats for "HotPawn". (These
        examples assume that the user "HotPaw" doesn't exist.)

        return (

        $exists,
                1 if the user exists, 0 otherwise.

        $handle,
                The handle of the player, in the correct case.

        $online,
                1 if the user is online, 0 otherwise. If this is all you're
                going to check, &::userinfo will be more efficient.

        $guest, 1 if the user is a guest, 0 otherwise.

        $flags, *Current* flags for this user, such as "(*)", "(TM)", or
                "(C)". Note that admins can turn off their (*), so only
                depend on constant flags. On all servers that I know of, the
                (C) flag is constant. I use this in my Tourney module to
                reject computer players from non-computer tourneys, and in
                my Vote module to restrict voting to human accounts.

        \%ratings,
                Hash *reference* to the user's ratings.
                "$ratings->{'blitz'}" will return the user's blitz rating,
                for example.

        $email, User's registered email address. Only available if JobyX's
                server handle has admin status.

        $ip,    User's current IP address. Like $email, admin status is
                required.

        );

    &::inlist($list,$item)
        Returns 1 if $item is in the $list list.

    &::inlist($list,$item,$owner)
        Returns 1 if $item is in "${owner}"'s personal $list list.

    &::ipc($module,@args)
        Send list @args to module $module via the "ICS_ipc" hook. The return
        value will be whatever was returned from the other module's
        "ICS_ipc". If module $module is not loaded, or does not implement
        "ICS_ipc", "undef" will be returned. Since modules are meant to be
        independant, there is little use for this. Flexibility is the key
        though, so I leave the judgement up to you.

    &::ismoduleloaded($module)
        Returns 1 if module $module is currently loaded.

    &::parse($string)
        Parses $string as if it was received from the server. Be careful not
        to start an infinite loop by making it parse a tell to the same
        command handler that you're calling &::parse from.

    &::qtell($handle,@lines)
        Page the lines in @lines to user $handle. Don't worry about newlines
        embedded in the lines; they will be handled the same was as if the
        strings on both sides were separate elements of @lines. The user's
        Height variable is taken into account here.

    &::qualified($handle,$userlevel)
        Returns 1 if $handle meets the qualifications specified in
        $userlevel. See Qualifications at the beginning of this document for
        information on qualification strings.

    &::table($padding,@rows)
        Create a nicely formatted table, using the values you supplied.
        (Basically, beefed up version of &::columns.) If $padding is on,
        each cell will be wrapped with spaces to make the output easier to
        read. Please note that cells *will not wrap*, even if the width of
        the table would exceed 79! Cells are left-aligned by default. To
        make a cell right-aligned, prefix the content with character 0. The
        format of @rows is:

                @rows=(\@row1, \@row2, ... \@rowN)

        For each row, you can do either of the following:

                # Standard row.
                @row1=('Column 1','Column 2', ... 'Column X');
                # Second column, value "50", is right-aligned.  Note that just "\050"
                # won't work... character 50 will be displayed left-aligned.  See the
                # perlop manpage.
                @row2=('Blitz:',"\00050");
        
                # Divider.
                @row2=();

        The number of columns actually displayed depends on the number of
        items in the longest row.

        For example, the following code:

                &::table(
                ['Manager','Level','Status'],
                [],
                ['ChrisHowie','Operator(99)','Always busy'],
                ['Venom','Master(30)'],
                );

        Would return a list that, when used with &::qtell, would display the
        following:

                +-----------------------------------------+
                | Manager    | Level        | Status      |
                |------------|--------------|-------------|
                | ChrisHowie | Operator(99) | Always busy |
                | Venom      | Master(30)   |             |
                +-----------------------------------------+

        Nifty, no? Of course, you can modify the returned list before you
        pass it to &::qtell, either to add a few status rows to the end, or
        something completely bizzare.

        If you want to create a merged cell, span the area with "undef"s.
        For example:

                &::table(
                ['Statistics',undef],
                [],
                ['One','Two'],
                ['Three',undef],
                );

        Would result in this:

                +--------------+
                | Statistics   |
                |--------------|
                | One   | Two  |
                | Three        |
                |       | Four |
                +--------------+

        Note that you cannot supply "undef" as the content for the first
        column, or your command handler will die.

        To right-align a cell, preceed the cell with the null character. For
        example, "\0001.00" will display "1.00" aligned to the right.

        There is no special way to create extra-tall rows. If you want to
        wrap cells, you'll have to do something really interesting and
        complicated.

    &::userinfo($handle)
        Returns an array:

        return (

        $success,
            1 if the user was online, or if the user was offline but the
            player's file was successfully read. Note that if the user is
            offline, changes to the player's array *will not* be saved!

        \@parray,
            The user's array.

        );

        As for the format of the player array:

        [

        $handle,
            User's handle. The case of the handle is saved to disk, so the
            handle case is reliable whether the user is online or offline,
            unless an admin has changed the case of the handle since the
            user's last logoff.

        $guest,
            Guest status. If the file was read from disk, this will never be
            1; guest parrays aren't saved.

        $email,
            Email address.

        $ip,
            IP address.

        $userlevel,
            User's numeric userlevel.

        \%vars,
            Hash *reference* to the user's variable settings. In general,
            &::getvar is more reliable, because it will take into account
            unset variables with default values.

        \%misc,
            Miscellanous data about the user. This stuff will get saved in
            the user's file, so you can put things you will want to remember
            later in here. For example, my Tourney module stores tourney
            statistics in this hash.

        ]

  Hooks
    Though not hooks in the traditional sense, JobyX will let your module
    know when something interesting happens on the server, or locally. It is
    not neccessary to supply any of these. (Exceptions: "ICS_init" and
    "ICS_term".) If your module doesn't have a specific hook, JobyX will
    skip it when a hook event occurs. Modules receive hooks in the order
    that they were loaded in.

    The list is sorted *somewhat* alphabetically; similar hooks are grouped.

    ICS_init($username,$version,$handle)
        $handle is trying to insert your module. See Loading a Module above.

    ICS_begin
        See Loading a Module above.

    ICS_term
        See Removing a Module above.

    ICS_connect($handle,$guest,$ip)
        $handle has connected to the server. $guest and $ip will only be
        available if JobyX's server account has admin status.

    ICS_disconnect($handle)
        $handle has left the server.

    ICS_dis_connect($handle,$arrive)
        $handle has connected if $arrive, else disconnected.

    ICS_finger($handle_of_caller,$requested_handle)
        $handle_of_caller is requesting finger information on
        $requested_handle. You can return a string to append to the finger
        notes, with newlines if more than one line is required. In my
        Tourney module, I return the user's tournament statistics. Each
        module's return information will be separated with a newline
        automatically.

    ICS_input($line)
        Called when a line is received from the server. $line *will not*
        have a trailing newline, and any prompt will be removed. A return
        value of "(1)" tells JobyX not to parse this line further. A return
        value of "(2)" tells JobyX to not parse this line further, and also
        to not call "ICS_input" for any remaining modules. Generally, if you
        want to stop JobyX from parsing, "(1)" should be used. There's no
        telling what another module can see in this line. An exception is
        when your module is going to take exclusive action on this line and
        you don't want another module to take action. For example,
        interpreting a shout as a command and you don't want another module
        doing the same thing and flooding the shouter.

    ICS_tell($handle,$flags,$message)
        $handle (with flags $flags) has told JobyX $message. A return value
        of "(1)" will make JobyX ignore this command. A return value of
        "(2)" will make JobyX ignore this command, and also not call
        "ICS_tell" for any remaining modules. Generally, you should use
        "(2)", for obvious reasons. We don't want a whole load of modules
        interpreting a tell as a command and responding at once. For
        example, a VoteBot module could do something like:

                sub ICS_tell {
                        my($handle,undef,$mess)=@_;
                        if ($mess=~/^ \d+ [a-z]? $/xi) {
                                my @res=&::userinfo($handle);
                                $res[0] or return;
                                my $lt=$res[1]->[6]->{'lasttopic'};
                                ($lt>0) or return;
                                &::execcommand($_[0],'vote',"$lt $message");
                                return 2;
                        }
                }

        to make it behave like giantfish's VoteBot. (Note that the Vote
        command would most likely be setting the user's
        "$misc->{'lasttopic'}" miscellaneous setting.

    ICS_ipc($module,@args)
        This will be called when module $module wants to talk to your
        module. This can be used to share any type of data, which is
        transmitted in @args. See &::ipc.

    ICS_module_info
        The modinfo command has been called on your module. Return either a
        string with lines separated by newlines, or a @list of lines. You
        can display anything that you think is relevant to the current state
        of the module. For example, a Tourney module might display how many
        tourneys are in memory. This can be *very* useful in debugging.

    ICS_lsmod($maxlen)
        Similar to "ICS_module_info", but the return should be one string,
        without newlines. Called when lsmod is executed. It is perfectly
        okay to return nothing. Should yo decide to return anything, it
        should be a brief quip about the internal state of the module. Try
        not to exceed $maxlen characters. $maxlen is found by subtracting
        the length of your module name and number from 79, thus giving the
        longest string that you can return without wrapping. You can exceed
        this if neccessary, but you should try to keep long output for
        "ICS_module_info".

    ICS_pre_module_load($module)
        Module $module is about to be loaded. A return value of "(1,$error)"
        will cancel the load and supply the error message Module1 refused
        the load of Module2. $error. Note that at this point, the module has
        not been loaded yet. If you need to do &::ipc with another module,
        use the "ICS_module_loaded" hook instead.

    ICS_module_loaded($module)
        Module $module has been loaded.

    ICS_pre_module_unload($module)
        Module $module is about to be unloaded. A return value of
        "(1,$error)" will cancel the unload and supply the error message
        Module1 refused the removal of Module2. $error. This can be useful
        if your module depends on another. Note that at this point, the
        module has not been unloaded yet. &::ipc will still work.

    ICS_module_unloaded($module)
        Module $module has been unloaded.

  Dealing With Conflicts
    Occasionally, two modules may want the same command, list, variable, or
    avariable. How does JobyX determine which module's command handler is
    called? Simple: The first module that was loaded is given preference.

    Note that both commands *are* stored in memory, so if the first module
    is removed, the second module's command will become available. There is
    no way to specifically call the second module's handler while the first
    module is loaded.

    Note that this applies to lists, variables, and avariables as well.

    If you know of other modules that have the same commands/lists/etc as
    one of yours, you can do something like the following:

            package JX_Mod::Foo;
        
            sub ICS_init {
                    if (&::ismoduleloaded('Bar')) {
                            return (0,"Bar conflicts with this module.");
                    }
                    # return commands and stuff...
            }
        
            sub ICS_pre_module_load {
                    if (shift eq 'Bar') {
                            return (1,"Bar conflicts with this module.");
                    }
            }

    While not very compatible, this method is the best when you can't help a
    conflict. The downside is that you must be aware of the module that is
    causing the conflict. The upside is that the other module's author need
    not insert code to check for your module, becuase the
    "ICS_pre_module_load" hook will take care of it.

    Try to name your commands and stuff with names that apply to your
    module. For example, my Vote module names the command used to create
    topics CreateTopic. Naming it Create could cause conflicts with other
    modules very easily.

    To end on a happy note, if JobyX detects a conflict when you are using
    insmod, it will include them in the response. The best thing to do at
    that point is to use rmmod and try to fix the conflict.

  Help System
    JobyX provides a fully-functional helpfile system. By using the Help
    command, users can request documentation on any command or topic. To
    create help files for your module, simply place them in
    "path_to_jobyx/help/*module_name*". For example, my Tourney module
    places its helpfiles in /home/chris/jobyx/help/Tourney/.

    Usage files can be placed in "path_to_jobyx/usage/*module_name*".

    Conflicting helpfiles are dealt with in the same manner as
    commands/lists/etc. The first module loaded is given preference.

    When the Help command is called, JobyX builds an index of all of the
    helpfiles, working through each module's help folders in the order that
    they were loaded. If two files have the same name, the second is
    ignored. The practical upshot of this work is for helpfile name
    completion. For example, if a user types help me, and the first module
    has the "mean" file, while the second has the "metaphor" helpfile, the
    result will be a Helpfile ambiguous error. If JobyX looked in each
    directory until it found a match, "mean" would be displayed.

    To restrict access to helpfiles and usage files, the following check is
    performed: When a user requests a help/usage file, the entire command
    database is checked for commands with the same

    Aside from plain helpfiles, you can use some directives to mutate them
    based on a user's qualifications. Directives are in effect until the
    _next_ directive is encountered. Note that directives are not effective
    in usage files. If a directive starts with "#", it must be flush left;
    if "%", then it can appear anywhere.

    #SEE $string
        This directive is totally ignored by the helpfile parser, but it is
        read by the index builder. In order to view the helpfile (including
        seeing it in a "helpfile ambiguous" error), the user must pass the
        qualification string $string.

        To save time, the index builder only reads the first line of each
        helpfile. As a result, this directive is effective only on the first
        line.

    #COMMAND
        Similar to the "#SEE" directive, this line will limit visibility of
        the helpfile. Like "#SEE", it is ignored by the parser. When the
        indexer encounters this line, it will search for a command with the
        same name *within the same module*. If it finds a match, the
        visibility level of the helpfile will be the same as the visibility
        of the matching command. This will save you time when editing
        command levels; you won't have to edit each helpfile as well. Note
        that this will cause a performance hit when building the index, but
        that's why we don't build the index each time a helpfile is
        requested.

        Because it is also effective only on the first line, the "#SEE" and
        "#COMMAND" directives are mutually exclusive.

        NOTE: Since usage files are always associated with commands, they
        have, hypothetically speaking, an implied "#COMMAND" directive in
        them. That is, the usage command will filter visibility based on the
        visibility of the matching command.

    #QUALIFIED $string
        The user must pass the qualification string $string to read the next
        chunk.

    #!QUALIFIED $string
        The user must *not* pass the qualification string $string to read
        the next chunk.

    #STOP
        Stop reading the helpfile. This is only effective if the user would
        normally be able to read this line. While not absolutely neccessary,
        it can speed up parsing. See the example below.

    #BREAK
        Synonym for #QUALIFIED 1, and slightly faster.

    #USAGE
        Dumps the usage file with the same name as the helpfile.

    %me The name of JobyX's server account is substituted in here. This is
        the only directive that does not have to be flush left, and can
        appear more than once per line.

    %tell
        The value of the TellMe avariable is substituted in here. This
        avariable should contain the quickest way to talk to JobyX. If
        defaults to "tell $::username".

    %var'*expression*'
        Substitutes the result of a Perl expression. In the simplest use, it
        returns the value of a variable, hence the name "%var". Note that
        all calls are from "JX_Mod::CORE"'s namespace. You cannot extend
        expressions across lines.

        Single quotes were chosen because you can always use double quotes
        in place of single quotes, but the reciprocal isn't always true.

    *

    Here is an example helpfile for the command CreateTourney from my
    Tourney module:

            #COMMAND
            Creates a tournament.
        
            #USAGE

    Quite simple, really. I could also have done "#SEE 20" instead of using
    "#COMMAND", but now this helpfile and the CreateTourney command are
    effectively linked; any change to the visibility of CreateTourney will
    be followed by the helpfile.

    Here is a more complex example of an index helpfile:

            Hello!  My name is %me, and I am your friendly tournament robot!
        
            For a list of tournaments, type "%tell lt".
            To join a tournament, type "%tell join <number>".
            #QUALIFIED 20
            For tournament managing directions, type "%tell help tm_guide".
            #QUALIFIED ly:admin
            For server admin commands, type "%tell help admin_guide".
            #BREAK
        
            Have fun!

    Notice the creative use of directives to provide all staff members, TM
    or admin, with directions, and only when they *are* a TM or admin.

    Please note that #comments are not allowed, and will be displayed. The #
    directive notation is used because it is fairly common in programming
    languages.

    As far as restricting usage files, JobyX will try to find a command with
    the same name as a usage file requested. If it finds one, the user must
    be qualified to *see* the command in order to view the file. If JobyX
    can't find a command with the same name, the usage file will be
    displayed regardless of the user's level. (NOTE: In the future,
    helpfiles may be dealt with in the same manner; I.E. finding a matching
    command.)

BUGS
    While I'm not aware of any yet, this *is* the first release, so I'm sure
    there are some. Post any you find on the Bug Tracker on the SourceForge
    project page at <http://www.sourceforge.net/projects/jobyx> and I'll fix
    them ASAP.

CREDITS
    Special thanks to the Free Internet Chess Server (FICS -
    <http://www.freechess.org>) and the DeepNet Chess Server (DNCS -
    <http://chess.deepnet.com>) for allowing me to test JobyX on their
    servers.

