@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