@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