@q
@program jbots.muf
1 9999 d
i
( jbots.muf   v1.1    Jessy @ FurryMUCK    11/00
  
  A program for configuring and running AI or bot object.
  
  INSTALLATION:
  
  Set jbots.muf W and L. Create a global action with a name such as
  'bot;bots', and link it to the program.
  
  CONFIGURATION:
  
  Bots may be set with individual security levels. Bot capabilities
  such as wandering, unlimited speed, and MPI execution may be either
  disabled or enabled for specific security levels. The program's
  #help option gives a complete discussion of how to configure the
  system and set bot security levels.
  
  USE:
  
    <cmd> #config ...................... Display system configuration
    <cmd> #config <param>=<value> ...... Configure bot system
    <cmd> #edit <bot> .................. List scripts on <bot>
    <cmd> #edit <bot>=<script> ......... Edit <script> on <bot>
    <cmd> #gag <bot> ................... List ignored players for <bot>
    <cmd> #gag <bot>=<player> .......... Set <bot> to ignore <player>
    <cmd> #level <bot> ................. Show security level for <bot>
    <cmd> #level <bot>=<level> ......... Set security level for <bot>
    <cmd> #notbot <bot> ................ Deactivate <bot>; erase scripts
    <cmd> #sleep <bot> ................. Turn off bot listening for <object>
    <cmd> #speed <bot>=<num seconds> ... Set <bot>'s wander speed
    <cmd> #stop <bot> .................. Stop <bot> wandering
    <cmd> #ungag <bot>=<player> ........ Clear gag for <player> on <bot>
    <cmd> #wake <object> ............... Wake <object>; bot listening on
    <cmd> #wander <bot> ................ Start <bot> wandering
    <cmd> #window <bot>=<num seconds> .. Set: <bot> resets after <num seconds>
 
  The program's #help option gives more complete information.
  
  SCRIPTS:
  
  Bot actions are controlled by matching MUCK activity that the bot
  sees or hears, and executing an appropriate action... usually a force.
  The matching patterns and actions are stored in bot scripts.
  For example:
  
    *{hi|hello|hey|heya}*{tess}*>>>force>>:waves.
  
  If a bot named Tess had this line in her current script, she would
  respond to greetings by waving. The program's '#help scripts' option
  gives a complete discussion of creating bot scripts.
  
  Jbots.muf may be freely ported. Please comment any changes.

	CHANGES:

	v1.1:  Fixed a problem with wandering speeds. Thanks to Alia
	       for reporting the bug.
)
 
$include $lib/lmgr
$include $lib/editor
$include $lib/strings
$include $lib/reflist
  
lvar ourArg                   (* string: command arg; may be modified *)
lvar ourBot                                      (* dbref: bot object *)
lvar ourCounter                    (* int or string: loop control var *)
lvar ourOption                              (* string: #option string *)
lvar ourScratch                                   (* x: workspace var *)
lvar ourScript                      (* string: name of current script *)
lvar ourString                               (* string: workspace var *)
lvar ourSpeed                                      (* int: delay time *)
  
$define Tell me @ swap notify $enddef
$define DoNukeStack begin depth while pop repeat $enddef
  
(2345678901234567890123456789012345678901234567890123456789012345678901)
 
: DoScriptHelp  (  --  )             (* show help for writing scripts *)
  
  "Bots handled by this program work by matching strings that they "
  "see and hear against patterns stored in bot scripts, and then "
  "acting as indicated in the script. The most basic and most often "
  "used script action is a force. Here's an example showing the "
  "syntax:" strcat strcat strcat strcat Tell " " Tell
  
  "  *{hi|hello|hey|heya}*{tess}*>>>force>>:waves." Tell " " Tell
  
  "The first portion of this line, before the >>> delimeter, is the "
  "pattern to match: this line will match a greeting to a bot (or "
  "anyone else) named 'Tess'. (See 'man smatch' for a complete "
  "discussion of matching rules.) The next portion of the line, after "
  "the >>> delimiter and before the >> delimeter, is the action to "
  "take, in this case a force. (There are other possible actions, as "
  "discussed below.) The final portion of the line, following the >> "
  "delimeter, is the action parameter. In this case, it indicates what "
  "the bot should be forced to do when the patter matches: Tess will "
  "wave when someone greets her."
  strcat strcat strcat strcat strcat strcat strcat strcat strcat
  Tell " " Tell
  
  "Lines such as the example above are stored in bot scripts: lists "
  "containing lines with some combination of matching patterns, "
  "actions, and action parameters. A single bot can hold multiple "
  "scripts. The primary script, and the one used by default, is called "
  "'main'. To configure it, type '$com #edit <bot>=main', and enter "
  "script lines. Type '.end' on a line by itself to save the list and "
  "exit the list editor."
  strcat strcat strcat strcat strcat strcat
  command @ "$com" subst Tell " " Tell
  
  "Additional, more specific scripts can be supplied. For example, "
  "Tess might have a 'main' script that handles greetings and "
  "general conversation, and then a more specific script called "
  "'building' containing lines that will let her give someone a "
  "tutorial on building. To invoke the more specific script, a "
  "line in her main script would need to include either a 'switch' "
  "or 'fswitch' (for force-switch) action. A switch action immediately "
  "switches the bot's working script, and then looks for a match "
  "against the current string. For example:"
  strcat strcat strcat strcat strcat strcat strcat strcat Tell " " Tell
  
  "  *{tess}*{tell|ask|know}*about*building>>>switch>>building"
  Tell " " Tell
  
  "This line would cause Tess to respond to a string like 'Tess, can "
  "you tell me about building?' by immediately switching to her "
  "'building' script, and again looking for a match. In this case, "
  "the building script should contain a line that matches the same "
  "pattern and includes a force action that let's the player talking "
  "to Tess know that yes, she can tell them about building. Something "
  "like:"
  strcat strcat strcat strcat strcat strcat Tell " " Tell
  
  "  *{tess}*{tell|ask|know}*about*building>>>force>>\"OK, let's talk "
  "about building. Do you want to know about digging rooms, setting "
  "descriptions, or creating exits?"
  strcat strcat Tell " " Tell
  
  "Another way to handle this sort of context switch is with the "
  "'fswitch' action, which does a force, and then switches context. "
  "The syntax for fswitch lines is:"
  strcat strcat Tell " " Tell
  
  "  [pattern]>>>fswitch>>[force string]>>[script]"
  Tell " " Tell
  
  "So, we could also handle the context switch above with something "
  "like:" strcat Tell " " Tell
  
  "  *{tess}*{tell|ask|know}*about*building>>>fswitch>>\"OK, let's talk "
  "about building. Do you want to know about digging rooms, setting "
  "descriptions, or creating exits?>>building"
  strcat strcat Tell " " Tell
  
  "The fswitch is more complex, but handles both actions -- forcing "
  "the bot and switching contexts -- in a single statement."
  strcat Tell " " Tell
  
  "Contexts -- that is, the current script being used by a bot -- "
  "are handled on a per-player basis. So, if Player A were in a "
  "discussion about building with Tess, and Player B entered "
  "the room, Tess would interact with the Player B based on her "
  "her 'main' script while continuing to interact with Player A "
  "based on her 'building' script. A bot will remain in a context-"
  "specific switch until either (A) a matching statement within "
  "the script causes a context switch back to 'main' or some other "
  "script, or (B) the player does not interact with the bot for a "
  "period of time exceeding the bot's 'idle window'. The default "
  "idle window is 180 seconds. If Player A is discussing building "
  "with Tess, but then doesn't say anything to her for three minutes "
  "or more, Tess will, in effect, assume that the conversation about "
  "building is over, and go back to her main script when interacting "
  "with Player A. You can change a bot's idle window by typing "
  "'$com #window <bot>=<num seconds>'."
  strcat strcat strcat strcat strcat strcat strcat strcat 
  strcat strcat strcat strcat strcat strcat strcat Tell " " Tell
  
  "The 'force' action causes the bot to perform a single, specific "
  "action when a matching pattern is found. The 'random' action "
  "can be used to pull a random force action from a list. For example, "
  "to give Tess a variety of greetings, we could include a line like "
  "the following in her main script:"
  strcat strcat strcat strcat Tell " " Tell
  
  "  *{hi|hello|hey|heya}*{tess}*>>>random>>greetings." Tell " " Tell
  
  "This would cause Tess to perform a random force string from "
  "a script called 'greetings'. Lines in scripts such as this, "
  "containing possible choices for a random action, should contain "
  "*only* the force string, not matching patterns or action strings. "
  "To set up Tess's random greetings, we would type '$com #edit tess="
  "greetings' to create the script, and enter a variety of greeting "
  "actions:"
  strcat strcat strcat strcat strcat strcat 
  command @ "$com" subst Tell " " Tell
  
  "  :waves." Tell
  "  :smiles. \"Hi there.\"" Tell
  "  :waves back cheerfully." Tell " " Tell
  
  "When Tess is greeted, she will perform one of these actions at "
  "random." strcat Tell " " Tell
  
  "The final script action is 'null'. If for some reason you want "
  "to explicitly trap and ignore a certain string, include the string "
  "as a matching pattern, followed by a >>> delimeter and the 'null' "
  "action:" strcat strcat strcat Tell " " Tell
  
  "  *yiff*>>>null" Tell " " Tell
  
  "Some Final Notes:" Tell " " Tell
  
  "Matching patterns are checked top to bottom in a script, in order. "
  "For best results, put more specific patterns and actions near the "
  "beginning of the script and more general patterns and actions near "
  "the end." strcat strcat strcat Tell " " Tell
  
  "Depending on your system's settings and your bot's security level "
  "(both of which are set by the MUCK administrators), your scripts "
  "may be able to parse MPI. Consult bulletin boards and policy docs "
  "for information on whether or not you can use MPI in your bot "
  "scripts. If so, the scripts will be dynamically formatted at "
  "runtime. A simple example:"
  strcat strcat strcat strcat strcat Tell " " Tell
  
  "  *{hi|hello|hey|heya}*{tess}*>>>force>>"
  ":smiles and says \"Hi, {name:me}.\"" strcat
  Tell " " Tell
  
  "This would cause Tess to greet players by name." Tell " " Tell
  
  "Punctuation can confuse pattern matching. For this reason, all "
  "punctuation is stripped from the strings that the bot will be "
  "examing for matches. Your matching strings shouldn't include "
  "any punctuation other than those required for wildcard and range "
  "matching, as discussed in 'man smatch'."
  strcat strcat strcat strcat Tell " " Tell
  
  "A bot must have a script called 'main' in order to work." Tell
;
 
: DoConfigHelp  (  --  )         (* show help on system configuration *)
  
  "The bot system has several administratively configurable paramters, "
  "all of which are set using the following syntax:" 
  strcat Tell " " Tell
  
  "  $com #config <param>=<value>"
  command @ "$com" subst Tell " " Tell
  
  "Valid input for <value> will vary, depending on the parameter being "
  "configured, as discussed below." 
  strcat Tell " " Tell
  
  "System: $com #config system=<on|off>"
  command @ "$com" subst Tell " " Tell
  
  "The 'system' parameter turns the bot system on and off. To temp"
  "orarily disable all bots, use '$com #config system=off'. To renable "
  "them, use '$com #config system=on'."
  strcat strcat
  command @ "$com" subst Tell " " Tell
  
  "Speed: $com #config speed=<number of seconds>"
  command @ "$com" subst Tell " " Tell
  
  "The 'speed' parameter sets a global speed limit: that is, the "
  "minimum time between moves for wandering bots. (However, see "
  "also the 'unlimited' parameter below, which specifies which "
  "bots are able to ignore this limit.)"
  strcat strcat strcat Tell " " Tell
  
  "MPI: $com #config mpi=<[1-4]|yes|no>" 
  command @ "$com" subst Tell " " Tell
  
  "The 'mpi' parameter controls whether or not bots may execute MPI from "
  "scripts, and if so, what security level is required. A numerical "
  "entry indicates that MPI parsing is enabled, but may only be used "
  "by bots that have at least the specified security level. Example: "
  "entering '$com #config mpi=2' sets the system so that level 2, 3, "
  "and 4 bots may use MPI, but level 1 bots may not. Entering 'yes' "
  "as the parameter value enables MPI at the default level of 3. "
  "Entering 'no' completely disables MPI in scripts."
  strcat strcat strcat strcat strcat strcat strcat
  command @ "$com" subst Tell " " Tell
  
  "Wandering:  $com #config wander=<[1-4]|yes|no>"
  command @ "$com" subst Tell " " Tell
  
  "The wandering parameter uses the same format. Enter a numeric "
  "parameter to enable bot wandering for bots having at least the "
  "specified security level. Enter 'yes' to enable wander at the "
  "default level of 2. Enter 'no' to completely disable wandering."
  strcat strcat strcat Tell " " Tell
  
  "Unlimited Wander Speed: $com #config unlimited=<[1-4]|yes|no>"
  command @ "$com" subst Tell " " Tell
  
  "The wandering speed (that is, the minimum time between moves for a "
  "wandering bot) is normally limited by the global speed limit "
  "parameter. The 'unlimited' parameter controls the security "
  "level required in order for a bot to ignore this limit. "
  "A numeric value specifies a required security level. An "
  "entry of 'yes' enables unlimited speed at the default level of 4. "
  "An entry of 'no' disables unlimited speed wandering, causing all "
  "bots to obey the global speed limit."
  strcat strcat strcat strcat strcat strcat strcat Tell " " Tell
  
  "Admin: $com #config admin=<player|!player>"
  command @ "$com" subst Tell " " Tell
  
  "By default, only wizards may make administrative configuration "
  "settings for the bot system. Wizards may delegate permission "
  "to configure the system to non-wiz players with the 'admin' "
  "parameter. To add a player to the admin list, simply specify "
  "the player's name as the parameter value. To remove a player, "
  "precede the player name with an ! exclamation point."
  strcat strcat strcat strcat strcat Tell
;
 
: DoLevelHelp  (  --  )           (* show info on bot security levels *)
  
  "Bot permissions for actions such as wandering, using MPI, or "
  "exceeding the bot speed limit are controlled by bot security "
  "levels, which range from 0 to 4."
  strcat strcat Tell " " Tell
  
  "To display current security level configurations, type '$com "
  "#config'. To display a bot's security level, type '$com #level " 
  "<bot>."
  strcat strcat command @ "$com" subst Tell " " Tell
  
  "Administrators may set a bot's security level by typing '$com "
  "#level <bot>=<0-4>'. Bots may use any abilities configured "
  "for security levels equal to or less than their own. Level 0 "
  "bots are disabled. By default, bots have a security level of 1."
  strcat strcat strcat command @ "$com" subst Tell 
;
 
: DoWanderHelp  (  --  )                (* show info on bot wandering *)
  
  "Bots may be instructed to wander -- that is, to travel about the "
  "MUCK randomly, moving at predetermined intervals -- by typing "
  "'$com #wander <bot>'. To stop a wandering bot, type '$com #stop "
  "<bot>'. You must control a bot in order to start it wandering. "
  "The bot's owner or any bot system admin may stop a bot from "
  "wandering." strcat strcat strcat strcat strcat 
  command @ "$com" subst Tell " " Tell
  
  "Wandering bots move randomly, once per speed interval. To set a "
  "bot's speed interval, type '$com #speed <bot>=<number of seconds>'. "
  "A bot cannot travel any faster than the global speed limit (type '$com "
  "#config' to display the limit), unless its security level allows it "
  "ignore this limit." strcat strcat strcat strcat
  command @ "$com" subst Tell " " Tell
  
  "A bot will stop wandering if either an authorized player issues "
  "a #stop command or it finds itself in a room with no exits that "
  "it can use. If wandering bot interacts with a player, it will 'dally' " 
  "at that location for five minutes, and then resume wandering."
  strcat strcat strcat Tell 
;
  
: DoHelp  (  --  )                             (* display help screen *)
  
  prog name " (#" strcat prog intostr strcat ")" strcat Tell " " Tell
  
  ourArg @ if                       (* check for specific help option *)
    "scripts" ourArg @ stringpfx if
      DoScriptHelp exit
    then
    "levels" ourArg @ stringpfx if
      DoLevelHelp exit
    then
    "wandering" ourArg @ stringpfx if
      DoWanderHelp exit
    then
    "configuration" ourArg @ stringpfx
    "configuring"   ourArg @ stringpfx or if
      DoConfigHelp exit
    then
  then
  
    (* if no specific help option specified, show default help screen *)
  "A program for configuring and running AIs or bot objects."
  Tell " " Tell
  
  "  $com #config ...................... Display system configuration"
  command @ "$com" subst Tell
  me @ "W" flag?
  prog "@jbot/admins" me @ REF-inlist? or if
    "  $com #config <param>=<value> ...... Configure bot system"
    command @ "$com" subst Tell
  then
  "  $com #edit <bot> .................. List scripts on <bot>"
  command @ "$com" subst Tell
  "  $com #edit <bot>=<script> ......... Edit <script> on <bot>"
  command @ "$com" subst Tell
  "  $com #gag <bot> ................... List ignored players for <bot>"
  command @ "$com" subst Tell
  "  $com #gag <bot>=<player> .......... Set <bot> to ignore <player>"
  command @ "$com" subst Tell
  me @ "W" flag?
  prog "@jbot/admins" me @ REF-inlist? or if
    "  $com #level <bot> ................. Show security level for <bot>"
    command @ "$com" subst Tell
    "  $com #level <bot>=<level> ......... Set security level for <bot>"
    command @ "$com" subst Tell
  then
  "  $com #notbot <bot> ................ Deactivate <bot>; erase scripts"
  command @ "$com" subst Tell
  "  $com #sleep <bot> ................. Turn off bot listening for <object>"
  command @ "$com" subst Tell
  "  $com #speed <bot>=<num seconds> ... Set <bot>'s wander speed"
  command @ "$com" subst Tell
  "  $com #stop <bot> .................. Stop <bot> wandering"
  command @ "$com" subst Tell
  "  $com #ungag <bot>=<player> ........ Clear gag for <player> on <bot>"
  command @ "$com" subst Tell
  "  $com #wake <object> ............... Wake <object>; bot listening on"
  command @ "$com" subst Tell
  "  $com #wander <bot> ................ Start <bot> wandering"
  command @ "$com" subst Tell
  "  $com #window <bot>=<num seconds> .. Set: <bot> resets after <num seconds>"
  command @ "$com" subst Tell " " Tell
  
  "See '$com #help scripts' for information on bot scripts (long)." 
  command @ "$com" subst Tell
  "See '$com #help levels' for information on bot security levels."
  command @ "$com" subst Tell
  "See '$com #help wander' for information on bot wandering."
  command @ "$com" subst Tell
  me @ "W" flag?
  prog "@jbot/admins" me @ REF-inlist? or if
    "See '$com #help config' for information on system configuration."
    command @ "$com" subst Tell
  then
;
 
: DoCheckSystem  (  --  ) (* notify and kill process if system is off *)
  
  prog "@jbot/settings/no_bots" getpropstr if
    ">>  The bot system is currently disabled." Tell pid kill
  then
;
 
: DoCheckActive (  --  )(* notify and kill process if bot is inactive *)
  
                   (* security level 0 means 'inactive' or 'disabled' *)
  ourBot @ "@jbot/settings/level" getpropstr dup if
    "0" smatch if 
      ">>  $bot has been disabled by an administrator."
      ourBot @ name "$bot" subst Tell pid kill
    then
  else
    pop
  then
;
 
: DoCheckMPI  ( d -- i )      (* return true if bot d can execute MPI *)
  
                                     (* check: mpi globally disabled? *)
  prog "@jbot/settings/no_mpi" getpropstr if pop 0 exit then
  
                     (* compare bot's seclev to system's required lev *)
  "@jbot/settings/level" getpropstr dup if
    strip atoi 
  else
    pop 1 
  then
  
  prog "@jbot/settings/wander_lev" getpropstr dup if
    strip atoi
  else
    pop 3
  then
  
  >= if 1 else 0 then
;
 
: DoCheckWander  ( d -- i )        (* return true if bot d can wander *)
  
                               (* check: wandering globally disabled? *)
  prog "@jbot/settings/no_wander" getpropstr if pop 0 exit then
  
                     (* compare bot's seclev to system's required lev *)
  "@jbot/settings/level" getpropstr dup if
    strip atoi 
  else
    pop 1 
  then
  
  prog "@jbot/settings/mpi_lev" getpropstr dup if
    strip atoi
  else
    pop 2
  then
  
  >= if 1 else 0 then
;
 
: DoNoisyMatch  ( s -- d )    (* match s; check and notify for errors *)
  
  dup strip ourScratch !                 (* bot may be somewhere else *)
  me @ "@jbot/mybots" REF-allrefs (* check user's reflist of bots 1st *)
  begin
    dup while
    over name ourScratch @ smatch if
      over ourScratch !
      begin
        dup while
        swap pop
        1 -
      repeat
      pop ourScratch @ exit
    then
    swap pop
    1 -
  repeat
  pop
  
     (* not in reflist; try a normal match; filter non-viable results *)
  match 
  #-1 over dbcmp if
    ">>  I don't see that here." 
    Tell 
  else
  #-2 over dbcmp if
    ">>  Ambiguous. I don't know which one you mean!" 
    Tell pop #-1 
  else
  #-3 over dbcmp if
    ">>  I don't see that here."
    Tell pop #-1 
  else
  dup thing? not if
    ">>  Sorry, only objects of type Thing may be bots." 
    Tell pop #-1
  then then then then
;
 
: DoCleanString  ( s -- s' )  (* remove punct and extra spaces from s *)
  
  "" "."   subst
  "" ","   subst
  "" "'"   subst
  "" ":"   subst
  "" ";"   subst
  "" "!"   subst
  "" "\""  subst
  " " "  " subst
; 
  
: DoEditLoop  ( listname dbref {rng} mask currline cmdstring  --  )
                                        (* read input for list editor *)
  EDITORloop
  dup "save" stringcmp not if
    pop pop pop pop
    3 pick 3 + -1 * rotate
    over 3 + -1 * rotate
    dup 5 + pick over 5 + pick 
    over over LMGR-DeleteList
    1 rot rot LMGR-PutRange
    4 pick 4 pick LMGR-GetList
    dup 3 + rotate over 3 + rotate
    ">>  List saved." Tell
    "" DoEditLoop exit
  then
  dup "abort" stringcmp not if
    ">>  List not saved." Tell
    pop pop pop pop pop pop pop pop pop exit
  then
  dup "end" stringcmp not if
    pop pop pop pop pop pop 
    dup 3 + rotate over 3 + rotate
    over over LMGR-DeleteList
    1 rot rot LMGR-PutRange
    ">>  List saved." Tell exit
  then
;
  
: DoEditList  ( d s --  )                         (* edit list s on d *)
  
  swap
">>  Welcome to the list editor. You can get help by entering '.h' on"
Tell
">>  a line by itself. '.end' will save and exit. '.abort' will abort"
Tell
">>  any changes. To save changes and continue editing, use '.save'."
Tell
  over over LMGR-GetList
  "save" 1 ".i $" DoEditLoop
;
  
: DoRemoveList  ( d s --  )                   (* remove list s from d *)
  
  "#" strcat ourScript ! ourBot !
  ourBot @ ourScript @ remove_prop
  ourScript @ "/" strcat ourScript !
  
  "1" ourCounter !
  begin                                   (* begin line-removing loop *)
    ourBot @ ourScript @ ourCounter @ strcat over over
    getpropstr while
    remove_prop
    ourCounter @ atoi 1 + intostr ourCounter !
  repeat                                    (* end line-removing loop *)
  pop pop
  
  ourBot @ ourScript @
  dup "*/" smatch if
    dup strlen 1 - strcut pop strip 
  then
  remove_prop
;
  
: DoShowList  ( d s --  )              (* display list s on object d *)
  
  "#/" strcat swap LMGR-GetList
  begin                                    (* begin line-listing loop *)
    dup while
    dup 1 + rotate Tell
    1 -
  repeat                                     (* end line-listing loop *)
  pop
;
 
: DoListScripts  (  --  )                      (* list scripts on bot *)
  
  ourArg @ DoNoisyMatch dup if ourBot ! else pop exit then
  me @ ourBot @ controls not if 
    ">>  Permission denied." Tell exit
  then
  DoCheckActive
  
  ">>  CURRENT SCRIPTS ON $BOT:"
  ourBot @ name toupper "$BOT" subst Tell 
  
  ourBot @ "@jbot/scripts/" nextprop dup if
    begin
      dup while
      dup "" "@jbot/scripts/" subst
      dup strlen 1 - strcut pop
      "    " swap strcat Tell
      ourBot @ swap nextprop
    repeat
  else
    ">>  <none>" Tell
  then
  pop
;
 
: DoEditScript  (   --  )                        (* edit a bot script *)
  
  ourArg @ if
    ourArg @ "=" instr if   (* if a script name is specified, edit it *)
      ourArg @ dup "=" instr strcut
      strip ourScript !
      strip dup strlen 1 - strcut pop
      DoNoisyMatch dup not if
        pop exit
      then
      me @ over controls not if
        ">>  Permission denied." Tell exit
      then
      prog "_settings/no_bots" getpropstr if
        ">>  Sorry, the 'bot system is currently disabled." Tell exit
      then
      ourBot !
      DoCheckActive
      ourBot @ "@jbot/scripts/" ourScript @ strcat DoEditList
      ourBot @ "@jbot/scripts/main#/" nextprop not if
        ">>  NOTE: $bot does not have a 'main' script."
        ourBot @ name "$bot" subst Tell
        ">>  You will need to create one in order to activate the bot."
        Tell
        ">>  To do so, type '$com #edit $bot=main'."
        command @ "$com" subst
        ourBot @ name "$bot" subst Tell
        ">>  See '$com #help scripts' for information on bot scripts."
        command @ "$com" subst Tell
      then
    else
      DoListScripts    (* if no script specified, list current scripts *)
    then
  else
    ">>  Syntax: $com <bot>=<script>"
    command @ "$com" subst Tell
  then
;
 
: DoShowConfig  (  --  )                (* show current system config *)
  
  ">>  JBOT SYSTEM CONFIGURATION:" Tell 
  ">>  The bot system is currently "
  prog "@jbot/settings/no_bots" getpropstr if "OFF." else "ON." then
  strcat Tell
  ">>  Default idle window: "
  prog "@jbot/settings/window" getpropstr dup not if pop "180" then
  strcat Tell
  ">>  Global speed limit: "
  prog "@jbot/settings/speed_limit" getpropstr dup not if pop "30" then
  strcat Tell
  prog "@jbot/settings/no_speedbreak" getpropstr if
    ">>  All bots must obey global speed limit." Tell
  else
    ">>  Breaking global speed limit requires level $num."
    prog "@jbot/settings/unlimited_lev" getpropstr dup not if pop "4" then
    "$num" subst Tell
  then
  prog "@jbot/settings/no_mpi" getpropstr if
    ">>  MPI execution in scripts is disabled." Tell
  else
    ">>  MPI execution in scripts requires security level $num."
    prog "@jbot/settings/mpi_lev" getpropstr dup not if pop "3" then
    "$num" subst Tell
  then
  prog "@jbot/settings/no_wander" getpropstr if
    ">>  Bot wandering is disabled." Tell
  else
    ">>  Bot wandering requires security level $num."
    prog "@jbot/settings/wander_lev" getpropstr dup not if pop "2" then
    "$num" subst Tell
  then
  prog "@jbot/admins" getpropstr if
    ">>  System administrators: All wizards, plus $list."
    prog "@jbot/admins" REF-list "$list" subst Tell
  then
;
 
: DoShowConfigSyntax  (  --  )      (* show syntax for #config option *)
  
  ">>  Syntax: $com #config <paramter>=<value>"
  command @ "$com" subst Tell
  ">>  See '$com #help config' for more information."
  command @ "$com" subst Tell
;
 
: DoConfigure  (  --  )              (* apply program config settings *)
  
  ourArg @ if                      (* parse input; find config option *)
    ourArg @ "=" instr if
      ourArg @ dup "=" instr strcut
      strip ourString !
      strip dup strlen 1 - strcut pop
      strip ourArg !
      ourString @ ourArg @ and if
        me @ "W" flag?                            (* check permission *)
        prog "@jbot/admins" me @ REF-inlist? or not if
          ">>  Permission denied." Tell exit
        then
        0 ourCounter !
        "administrator" ourArg @ stringpfx if     (* edit admin list *)
          me @ "W" flag? not if   (* only wizzes can edit admin list *)
            ">>  Sorry, only wizards can change the bot admin list."
            Tell exit
          then
          ourString @ "!" stringpfx if
            ourString @ 1 strcut strip ourString ! pop
            1 ourCounter !
          then
          prog "@jbot/admins"
          ourString @ .pmatch dup not if
            ">>  Player not found." Tell DoNukeStack exit
          then
          ourCounter @ if
            ">>  Set. $player is now not a bot admin."
            over name "$player" subst Tell
            REF-delete 
          else
            ">>  Set. $player is now a bot admin."
            over name "$player" subst Tell
            REF-add
          then
        else
        "mpi" ourArg @ stringpfx if              (* set MPI sec level *)
          ourString @ "[1-4]" smatch if
            prog "@jbot/settings/no_mpi" remove_prop
            prog "@jbot/settings/mpi_lev" ourString @ setprop
            ">>  Set. Bots must be at least level $num to execute MPI "
            "from scripts." strcat ourString @ "$num" subst Tell
          else
          "no" ourString @ stringpfx if 
            prog "@jbot/settings/no_mpi" "yes" setprop
            ">>  Set. MPI excuted from bot scripts disabled." Tell
          else
          "yes" ourString @ stringpfx if 
            prog "@jbot/settings/no_mpi" remove_prop
            prog "@jbot/settings/mpi_lev" "3" setprop
            ">>  Set. Bots must be at least level 3 to execute MPI (default)."
            Tell
          else
            ">>  Syntax: $com #config mpi=<[1-4]|yes|no>" Tell
          then then then
        else
        "wander" ourArg @ stringpfx if     (* set wandering sec level *)
          ourString @ "[1-4]" smatch if
            prog "@jbot/settings/no_wander" remove_prop
            prog "@jbot/settings/wander_lev" ourString @ setprop
            ">>  Set. Bots must be at least level $num to wander."
            ourString @ "$num" subst Tell
          else
          "no" ourString @ stringpfx if 
            prog "@jbot/settings/no_wander" "yes" setprop
            ">>  Set. Bot wandering disabled." Tell
          else
          "yes" ourString @ stringpfx if 
            prog "@jbot/settings/no_wander" remove_prop
            prog "@jbot/settings/wander_lev" "2" setprop
            ">>  Set. Bots must be at least level 2 to wander (default)."
            Tell
          else
            ">>  Syntax: $com #config wander=<[1-4]|yes|no>" Tell
          then then then 
        else
        "unlimited" ourArg @ stringpfx if  (* set unlimited sec level *)
          ourString @ "[1-4]" smatch if
            prog "@jbot/settings/no_unlimited" remove_prop
            prog "@jbot/settings/unlimited_lev" ourString @ setprop
            ">>  Set. Bots must be at least level $num to break speed limit."
            ourString @ "$num" subst Tell
          else
          "no" ourString @ stringpfx if 
            prog "@jbot/settings/no_unlimited" "yes" setprop
            ">>  Set. Unlimited speed is disabled." Tell
          else
          "yes" ourString @ stringpfx if 
            prog "@jbot/settings/no_unlimited" remove_prop
            prog "@jbot/settings/unlimited_lev" "4" setprop
            ">>  Set. Bots must be at least level 4 "
            "to break speed limit (default)." strcat Tell
          else
            ">>  Syntax: $com #config unlimited=<[1-4]|yes|no>" Tell
          then then then 
        else
        "system" ourArg @ stringpfx if       (* turn system on or off *)
          ourString @ "on" stringpfx if
            prog "@jbot/settings/no_bots" remove_prop
            ">>  Bot system enabled." Tell
          else
            ourString @ "off" stringpfx if
              prog "@jbot/settings/no_bots" "yes" setprop
              ">>  Bot system disabled." Tell
            else
              ">>  Syntax: $com #config system=<on|off>"
              command @ "$com" subst Tell
            then
          then
        else
        "speed" ourArg @ stringpfx if       (* set global speed limit *)
          ourString @ number? not if
            ">>  Syntax: $com #config speed=<number>" Tell
            ">>  <number> is minimum number of seconds between bot "
            "wandering moves." strcat Tell exit
          then
          ourString @ atoi 0 <= if
            ">>  Syntax: $com #config speed=<number>" Tell
            ">>  <number> must be positive." Tell exit
          then
          prog "@jbot/settings/speed_limit" ourString @ setprop
          ">>  Global bot speed limit set to $num seconds."
          ourString @ "$num" subst Tell
        else
          ">>  Unrecognized #config option." Tell
          ">>  See '$com #help config' for more information."
          command @ "$com" subst Tell
        then then then then then then
      else
        DoShowConfigSyntax
      then
    else
      DoShowConfigSyntax
    then
  else
    DoShowConfig
  then
;
 
: DoSetLevel  (  --  )            (* apply security settings to a bot *)
   
  me @ "W" flag?                                  (* check permission *)
  prog "@jbot/admins" me @ REF-inlist? or not if
    ">>  Permission denied." Tell exit
  then
  
  ourArg @ if
    ourArg @ "=" instr if        (* parse and verify input; set level *)
      ourArg @ dup "=" instr strcut
      strip ourString !
      strip dup strlen 1 - strcut pop
      strip ourBot !
      ourBot @ DoNoisyMatch dup not if pop exit then
      ourBot !
      ourString @ number? not if
        ">>  Syntax: $com #level <bot>=<number>"
        command @ "$com" subst Tell exit
      then
      ourString @ atoi 
      0 over > swap 4 > or if
        ">>  Syntax: $com #level <bot>=<number>"
        command @ "$com" subst Tell 
        ">>  <num> must be between 0 and 4, inclusive." Tell exit
      then
      ourBot @ "@jbot/settings/level" ourString @ setprop
      ">>  $bot's security level set to $num."
      ourBot @ name "$bot" subst
      ourString @ "$num" subst Tell
    else
                   (* if no level specified, show bot's current level *)
      ourArg @ DoNoisyMatch dup if ourBot ! else pop exit then
      ">>  $bot's bot security level is $num."
      ourBot @ "@jbot/settings/level" getpropstr dup not if pop "1" then
      "$num" subst ourBot @ name "$bot" subst Tell
    then
  else
    ">>  Syntax: $com #level <bot>=<number>"
    command @ "$com" subst Tell
  then
;
 
: DoSetSpeed  (  --  )                    (* set a bot's wander speed *)
  
  ourArg @ if
    ourArg @ "=" instr if  (* parse and verify input; set bot's speed *)
      ourArg @ dup "=" instr strcut
      strip ourString !
      strip dup strlen 1 - strcut pop
      strip ourBot !
      ourBot @ DoNoisyMatch dup not if pop exit then
      me @ over controls not if
        ">>  Permission denied." Tell exit
      then
      ourBot !
      ourString @ number? not if
        ">>  Syntax: $com #speed <bot>=<number>"
        command @ "$com" subst Tell exit
      then
      ourString @ atoi 0 <= if
        ">>  Syntax: $com #speed <bot>=<number>"
        command @ "$com" subst Tell 
        ">>  <number> must be positive." Tell exit
      then
      ourBot @ "@jbot/settings/speed" ourString @ setprop
      ">>  $bot's speed set to $num."
      ourBot @ name "$bot" subst
      ourString @ "$num" subst Tell
      ourString @ atoi
      prog "@jbot/settings/speed_limit" getpropstr dup not if pop "30" then
      atoi <
      ourBot @ "@jbot/settings/level" getpropstr dup not if pop "1" then
      atoi
      prog "@jbot/settings/unlimited_lev" getpropstr dup not if pop "4" then
      atoi 
      < and if
        ">>  NOTE: $num is faster than the global bot speed limit." 
        ourString @ "$num" subst Tell
        ">>  $bot will use the global speed limit of $num when wandering."
        ourBot @ name "$bot" subst
        prog "@jbot/settings/speed_limit" getpropstr dup not if pop "30" then
        "$num" subst Tell
      then
    else
                           (* if no speed specified, reset to default *)
      ourArg @ DoNoisyMatch dup if ourBot ! else pop exit then
      ">>  $bot's speed set to $num (default)."
      prog "@jbot/settings/speed_limit" getpropstr dup not if pop "30" then
      "$num" subst 
      ourBot @ name "$bot" subst Tell
    then
  else
    ">>  Syntax: $com #level <bot>=<number>"
    command @ "$com" subst Tell
  then
;
 
: DoWake  (  --  )           (* wake object ourArg; i.e., turn on bot *)
  
  ourArg @ if
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ over controls not if
      ">>  Permission denied." Tell exit
    then
    prog "@jbot/settings/no_bots" getpropstr if
      ">>  Sorry, the 'bot system is currently disabled." Tell exit
    then
    ourBot !
    DoCheckActive
  
    me @ "@jbot/mybots" ourBot @ REF-add
    ourBot @ "_listen/jbots" prog setprop            (* set listening *)
    ourBot @ "@jbot/active" "yes" setprop           (* set bot marker *)
  
    ">>  Set. $bot is now an active bot."                   (* notify *)
    ourBot @ name "$bot" subst Tell
  else                              (* show syntax if no arg supplied *)
    ">>  Syntax: $com #wake <object>" 
    command @ "$com" subst Tell
  then
;
 
: DoSleep  (  --  )                                 (* deactivate bot *)
  
  ourArg @ if
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ over controls not if
      ">>  Permission denied." Tell exit
    then
    ourBot !
  
    ourBot @ "_listen/jbots" remove_prop             (* set listening *)
    ourBot @ "@jbot/active"  remove_prop         (* remove bot marker *)
  
    ">>  Set. $bot is now not an active bot."               (* notify *)
    ourBot @ name "$bot" subst Tell
  else                              (* show syntax if no arg supplied *)
    ">>  Syntax: $com #sleep <object>" 
    command @ "$com" subst Tell
  then
;
 
: DoShowGagList  (  --  )              (* list gagged players for bot *)
  
  ">>  Gagged players for $bot: " 
  ourBot @ name "$bot" subst
  ourBot @ "@jbot/settings/ignore" getpropstr if
    ourBot @ "@jbot/settings/ignore" REF-list
  else
    "<none>"
  then
  strcat Tell
;
  
: DoSetGag  (  --  )       (* set a gag on puppet: will ignore player *)
  
  ourArg @ if
  
    ourArg @ "=" instr if
      ourArg @ dup "=" instr strcut
      strip ourString !
      strip dup strlen 1 - strcut pop
      strip ourArg !
    then
  
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ over controls not if 
      ">>  Permission denied." Tell exit
    then
    dup "@jbot/" nextprop not if
      ">>  $obj is not a bot."
      swap name "$obj" subst Tell exit
    then
    ourBot !
    DoCheckActive
  
    ourString @ not if   (* if no player specified, show current list *)
      DoShowGagList exit
    then
                                  (* if player found, add to gag list *)
    ourString @ .pmatch dup if
      dup name ourString !
      ourBot @ "@jbot/settings/ignore" rot REF-add
    else
      ourString @ DoNoisyMatch dup if
        dup name ourString !
        ourBot @ "@jbot/settings/ignore" rot REF-add
      then
    then
    ">>  Set. $bot will now ignore $player."
    ourBot @ name "$bot" subst
    ourString @ "$player" subst Tell
    DoNukeStack
  else
    ">>  Syntax: $com #gag <bot>=<player>"
    command @ "$com" subst Tell
  then
;
  
: DoClearGag  (  --  )     (* set a gag on puppet: will ignore player *)
  
  ourArg @ if
  
    ourArg @ "=" instr if
      ourArg @ dup "=" instr strcut
      strip ourString !
      strip dup strlen 1 - strcut pop
      strip ourArg !
    then
  
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ over controls not if 
      ">>  Permission denied." Tell exit
    then
    dup "@jbot/" nextprop not if
      ">>  $obj is not a bot."
      swap name "$obj" subst Tell exit
    then
    ourBot !
    DoCheckActive
  
    ourString @ not if   (* if no player specified, show current list *)
      DoShowGagList exit
    then
  
                             (* if player found, remove from gag list *)
    ourString @ .pmatch dup if
      dup name ourString !
      ourBot @ "@jbot/settings/ignore" rot REF-delete
    else
      ourString @ DoNoisyMatch dup if
        dup name ourString !
        ourBot @ "@jbot/settings/ignore" rot REF-delete
      then
    then
    ">>  Set. $bot will now not ignore $player."
    ourBot @ name "$bot" subst
    ourString @ "$player" subst Tell
    DoNukeStack
  else
    ">>  Syntax: $com #gag <bot>=<player>"
    command @ "$com" subst Tell
  then
;
 
: DoSetWindow  (  --  )                 (* set bot's idle window time *)
  
  ourArg @ if
  
    ourArg @ "=" instr if       (* parse and verify input; set window *)
      ourArg @ dup "=" instr strcut
      strip ourString !
      strip dup strlen 1 - strcut pop
      strip ourArg !
    then
  
    ourArg @ DoNoisyMatch dup not if
      pop exit
    then
    me @ over controls not if
      ">>  Permission denied." Tell exit
    then
  
    ourBot !
    DoCheckActive
  
    ourCounter @ if
      ourCounter @ number? not if
        ">>  Syntax: $com #window <bot>[=<seconds>]"
        command "$com" subst Tell
        ">>  <seconds> must be a positive number."
        Tell exit
      then
      ourCounter @ atoi 0 < if
        ourCounter @ atoi -1 * intostr ourCounter !
      then
    else
      "180" ourCounter !
    then
  
    ourBot @ "@jbot/settings/window" ourCounter @ setprop
  
    ">>  Set. $bot will now reset to main context after $num seconds."
    ourBot @ name "$bot" subst
    ourCounter @ "$num" subst Tell
  else
    ">>  Syntax: $com #window <bot>[=<seconds>]"
    command @ "$com" subst Tell
    ">>  If <seconds> is no supplied, window will be reset to default 180."
    Tell
  then
;
 
: DoWander  (  --  )          (* handle travel loop for wandering bot *)
  
  background
  
         (* verify that bot has a high enough security level to wander *)
  ourBot @ "@jbot/settings/level" getpropstr dup not if pop "1" then
  atoi 
  prog "@jbot/settings/unlimited_lev" getpropstr dup not if pop "4" then
  atoi
  >= if
    ourBot @ "@jbot/settings/speed" getpropstr dup not if
      pop prog "@jbot/settings/speed_limit" 
      getpropstr dup not if pop "30" then
    then
  else
    ourBot @ "@jbot/settings/speed" getpropstr 
    dup not if pop "30" then
  then
  atoi ourSpeed !
  
  begin                                          (* begin wander loop *)
  
                              (* check: bot still supposed to wander? *)
    ourBot @ "@jbot/settings/wandering" getpropstr not if
      exit
    then
    prog "@jbot/settings/no_wander" getpropstr if
      ">>  Bot wandering disabled." Tell
      ">>  $bot stops wandering." 
      ourBot @ name "$bot" subst Tell exit
    then
    ourBot @ "@jbot/settings/no_wander" getpropstr if
      ">>  Bot wandering disabled for $bot." 
      ourBot @ name "$bot" subst
      ">>  $bot stops wandering."
      ourBot @ name "$bot" subst Tell exit
    then
    DoCheckActive
                                           (* check: is bot dallying? *)
    ourBot @ "@jbot/settings/dally_till" getprop dup if
      systime > if
        60 sleep continue
      else
        ourBot @ "@jbot/settings/dally_till" remove_prop
      then
    else
      pop
    then
  
    DoNukeStack                                 (* grab a random exit *)
    ourBot @ location exits
    begin
      dup while
      depth 500 > if break then    (* ju-uuust in case... check stack *)
      dup getlink not if
        next continue
      then
      dup getlink #-3 dbcmp if                     (* skip HOME exits *)
        next continue
      then
      dup getlink room? not if                 (* skip non-room exits *)
        next continue
      then
      dup "Z" flag? if               (* honor no-puppets for bots too *)
        next continue
      then
                                     (* skip exits locked against bot *)
      dup getlockstr dup "*UNLOCKED*" smatch not if 
        parselock ourBot @ swap testlock not if
          next continue
        then
      else
        pop
      then
      dup next
    repeat
    pop 
                                       (* bail out if no viable exits *)
    depth not if
      ">>  $bot is trapped in a room with no exits." 
      ourBot @ name "$bot" subst Tell
      ">>  Wandering off." Tell
      exit
    then
                                     (* grab a random exit from stack *)
    depth random swap % 1 + pick ourCounter !
    DoNukeStack
                                 (* strip exit aliases from exit name *)
    ourCounter @ name
    dup ";" instr if
      dup dup ";" instr 1 - strcut pop strip
    then
    ourBot @ swap force                     (* force bot through exit *)
  
    DoNukeStack
    ourSpeed @ sleep
  repeat                                           (* end wander loop *)
  DoNukeStack
;
 
: DoStartWander  (  --  )              (* start random travel for bot *)
  
  ourArg @ if
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ over controls not if
      ">>  Permission denied." Tell exit
    then
    dup "@jbot/" nextprop not if
      ">>  $bot is not a bot. Cannot wander."
      swap name "$bot" subst Tell exit
    then
    prog "@jbot/settings/no_wander" getpropstr if
      ">>  Sorry, bot wandering is currently disabled." Tell exit
    then
    dup DoCheckWander not if
      ">>  Sorry, $bot is not allowed to wander." 
      swap name "$bot" subst Tell exit
    then
    ourBot !
    DoCheckActive
                         (* set props to show this is a wandering bot *)
    ourBot @ "@jbot/settings/wandering" "yes" setprop
    ourBot @ "@jbot/settings/dally_till" remove_prop
    ">>  $bot starts wandering."
    ourBot @ name "$bot" subst Tell
    DoWander                                             (* go wander *)
  else
    ">>  Syntax: $com #wander <bot>"
    command @ "$com" subst Tell
  then
;
  
: DoStopWander  (  --  )                        (* stop bot wandering *)
  
  ourArg @ if
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ "W" flag? not
    prog "@jbot/admins" me @ REF-inlist? not and
    me @ 3 pick controls not and if
      ">>  Permission denied." Tell exit
    then
    dup "@jbot/" nextprop not if
      ">>  $bot is not a bot."
      swap name "$bot" subst Tell exit
    then
    ourBot !
      (* set props: bot will stop wandering at next iteration of loop *)
    ourBot @ "@jbot/settings/wandering"  remove_prop
    ourBot @ "@jbot/settings/dally_till" remove_prop
    ">>  $bot stops wandering."
    ourBot @ name "$bot" subst Tell
  else
    ">>  Syntax: $com #stop <bot>"
    command @ "$com" subst Tell
  then
;
 
: DoUpdateLastTimes  (  --  )     (* clear old last-interaction times *)
  
                                        (* get bot's idle window time *)
  trig "@jbot/settings/window" getpropstr dup if
    atoi
  else
    pop 180
  then
  ourCounter !
  
          (* scan directory holding last-interaction-per-player times *)
                        (* delete entries older than idle window time *)
  trig "@jbot/settings/last/" nextprop
  begin
    dup while
    trig over getprop 
    systime swap - ourCounter @ > if
      dup trig "@jbot/settings/context/$player"
      rot "" "@jbot/settings/last/" subst
      "$player" subst 
      remove_prop
      trig over nextprop
      trig rot remove_prop
    else
      trig swap nextprop
    then
  repeat
  DoNukeStack
;
  
: DoSetLastTime  (  --  )                     (* set interaction time *)
 
           (* set prop recording time that bot interacted with player *)
  trig "@jbot/settings/last/$me" me @ intostr "$me" subst
  systime setprop
  trig "@jbot/settings/wandering" getpropstr if
    trig "@jbot/settings/dally_till"
    systime 300 + setprop
  then
;
 
: DoListen  (  --  )             (* evaluate heard arg as heard event *)
  
                     (* filter input from anyone on bot's ignore list *)
  trig "@jbot/settings/ignore" me @ REF-inlist? if exit then
  
            (* filter input from *other* bots that this bot has talked
               to in last 30 secs, to prevent bot jabber loops        *)
  me @ thing? if
    me @ "_listen/jbot" getpropstr if
      trig "@jbot/settings/lastbot" getprop 
      systime 30 - > if
        exit
      else
        trig "@jbot/settings/lastbot" systime setprop
      then
    then
  then
  
                                    (* if we have scripts to check... *)
  trig "@jbot/scripts/" nextprop if
    DoUpdateLastTimes 
    trig "@jbot/settings/context/$me"           (* get current script *)
    me @ intostr "$me" subst
    getpropstr dup not if
      pop "main"
    then
    ourArg @ DoCleanString ourArg !         (* strip punct from input *)
    trig "@jbot/scripts/$script#/" rot "$script" subst nextprop
  
    begin      (* begin a loop to see if we can match input in script *)
      dup while
      trig over getpropstr
      dup ">>>" instr dup if
        1 - strcut swap strip
        ourArg @ swap smatch if                    (* check for match *)
          3 strcut swap pop
          dup ">>" instr dup if
            1 + strcut swap
            "" ">>" subst       (* match found: get action; act on it *)
            1 sleep
            dup "force" smatch if                     (* force action *)
              pop DoSetLastTime
              trig DoCheckMPI if
                trig "@jbot/settings/tmp" rot setprop
                trig "@jbot/settings/tmp"
                dup 3 pick swap getpropstr 0 parseprop
                trig "@jbot/settings/tmp" remove_prop
              then
              trig swap force break
            then
            dup "fswitch" smatch if                 (* fswitch action *)
              pop DoSetLastTime
              dup ">>" instr 1 - strcut swap
              trig DoCheckMPI if
                trig "@jbot/settings/tmp" rot setprop
                trig "@jbot/settings/tmp"
                dup 3 pick swap getpropstr 0 parseprop
                trig "@jbot/settings/tmp" remove_prop
              then
              trig swap force
              "" ">>" subst
              trig "@jbot/settings/context/$me" 
              me @ intostr "$me" subst
              rot setprop
              break
            then
            dup "random" smatch if                   (* random action *)
              pop DoSetLastTime
              trig "@jbot/scripts/$script#" 
              3 pick "$script" subst getpropstr atoi
              dup if
                random swap % 1 + intostr
                trig "@jbot/scripts/$script#/$line"
                rot "$line" subst
                rot "$script" subst getpropstr
                trig DoCheckMPI if
                  trig "@jbot/settings/tmp" rot setprop
                  trig "@jbot/settings/tmp"
                  dup 3 pick swap getpropstr 0 parseprop
                  trig "@jbot/settings/tmp" remove_prop
                then
                trig swap force
              else
                break
              then
            then
            dup "switch" smatch if                   (* switch action *)
              pop DoSetLastTime
              trig "@jbot/settings/context/$me" 
              me @ intostr "$me" subst
              3 pick setprop
              pop "@jbot/scripts/$script#/" swap "$script" subst
              trig swap nextprop continue
            then
            dup "null" smatch if
              pop DoSetLastTime
              pop break
            then
          else
            pop pop
          then
        else
          pop
        then
      then
      trig swap nextprop
    repeat
    DoNukeStack
  then
;
 
: DoNotBot  (  --  )                   (* completely deactivate a bot *)
  
  ourArg @ if
    ourArg @ DoNoisyMatch dup not if        (* match obj; check perms *)
      pop exit
    then
    me @ over controls not if
      ">>  Permission denied." Tell exit
    then
    ourBot !
                                                  (* get confirmation *)
    ">>  NOTE: Deactivating a bot will remove all jbot "
    "settings, including " strcat Tell
    ">>  scripts, from the object." Tell
    begin
      ">>  Please confirm: You wish to deactivate $bot? (y/n)"
      ourBot @ name "$bot" subst Tell
      read 
      "yes" over stringpfx if                 (* remove all bot props *)
        ourBot @ "_listen/jbots" remove_prop
        me @ "@jbot/mybots" over over getpropstr if
          ourBot @ REF-delete
        else
          pop pop
        then
        ourBot @ "@jbot/" over over nextprop swap pop
        begin
          dup while
          over over nextprop
          3 pick rot remove_prop
        repeat
        pop pop
        ">>  $bot deactivated."
        ourBot @ name "$bot" subst Tell 
        DoNukeStack exit
      then
      "no" over stringpfx if
        ">>  Aborted." Tell 
        DoNukeStack exit
      then
      ">>  Please enter 'yes' or 'no'." Tell pop
    repeat
  else                              (* show syntax if no arg supplied *)
    ">>  Syntax: $com #notbot <object>" 
    command @ "$com" subst Tell
  then
;
  
: main
  
  "me" match me !
  
  trig me @ dbcmp if exit then
  
  dup if
    strip          (* parse arg string; store in ourArg and ourOption *)
    dup "#" stringpfx if
      dup " " instr if
        dup " " instr strcut
        strip ourArg !
        strip ourOption !
      else
        ourOption ! "" ourArg !
      then
    else
      ourArg !
    then
               (* evaluate arg: run as command #option or heard event *)
    ourOption @ if
      "#help"       ourOption @ stringpfx if DoHelp        else
      "#wake"       ourOption @ stringpfx if DoWake        else
      "#sleep"      ourOption @ stringpfx if DoSleep       else
      "#wander"     ourOption @ stringpfx if DoStartWander else
      "#stop"       ourOption @ stringpfx if DoStopWander  else
      "#edit"       ourOption @ stringpfx if DoEditScript  else
      "#speed"      ourOption @ stringpfx if DoSetSpeed    else
      "#gag"        ourOption @ stringpfx if DoSetGag      else
      "#ungag"      ourOption @ stringpfx if DoClearGag    else
      "#level"      ourOption @ stringpfx if DoSetLevel    else
      "#configure"  ourOption @ stringpfx if DoConfigure   else
      "#window"     ourOption @ stringpfx if DoSetWindow   else
      "#notbot"     ourOption @ stringpfx if DoNotBot      else
                                             DoListen
      then then then then then then then 
      then then then then then then
    else
      prog "@jbot/settings/no_bots" getpropstr not if
        DoListen
      then
    then
  then
;
.
c
q