@q
@program jchat.muf
1 99999 d
i
( jchat.muf v2.1 Jessy @ FurryMUCK 3/95, 11/95, 11/99
A multichannel chat program.
INSTALLATION:
Set jchat.muf Wizard, and link a global action to it. The name
of the action is the name of the channel. Additional channels can
then be created with the #create option. Individual players can
create personal command aliases for channels with the #alias option.
Jchat.muf requires lib-reflist.
USE:
<cmd> <msg> .............. Broadcast <msg> on this channel
<cmd> #list .............. List available channels
<cmd> #on ................ Join this channel
<cmd> #off ............... Leave this channel
<cmd> #nick <nickname> ... Set nickname for this channel
<cmd> #alias <alias> ..... Create command alias for this channel
<cmd> #noalias <alias> ... Remove command alias for this channel
<cmd> #who ............... List online members of this channel
<cmd> #all ............... List all members of this channel
<cmd> #gag <player> ...... Gag <player> for this channel
<cmd> #ungag <player> .... Ungag <player> for this channel
<cmd> #lock .............. Lock yourself to this channel
<cmd> #unlock ............ Remove channel lock
<cmd> #create <channel> .. Create new channel <channel> [admin]
<cmd> #delete <channel> .. Delete channel <channel> [admin]
<cmd> #public ............ Designate channel public [admin]
<cmd> #private ........... Designate channel private [admin]
<cmd> #invite <player> ... Authorize <plyr> for private channel [admin]
<cmd> #exclude <player> .. Unauthorize <plyr> for private channel [admin]
Wizards and the owner of the trigger action have admin permissions.
CHANGES:
1.0 Rewrote talknet.muf completely. Comchannel.muf replaces
talknet.muf; talknet.muf no longer supported.
2.0 Changed the list-membership storage scheme to dbref based.
1.x had been name based, which was just a bad design decision.
2.0 Added command aliasing. Creating a personal action for the alias
strikes me as a bit kludgy, but is the only managable way I can
see to do it, and a number of people had asked for this feature.
2.1 Made comchannel.muf Dark-Wizard aware... Darked players cannot
talk or listen on a channel; they do not appear on #who.
2.1 Added support for _prefs/2space, for double-spacing communica-
tion strings. Integrates with similarly modified versions of
tinysay.muf, cmd-page, and cmd-pose.
2.2 Added #gag/#ungag and #lock/#unlock.
2.2 Changed #join/#leave to #on/#off
2.3 Changed name to jchat, a la jlook, jmail, jboard. Changed
#channels to #list, which seems more intuitive and widespread.
Made the #list option show a more informative display, with
membership status and current #nick setting for each channel.
Jchat.muf may be freely ported. Please comment any changes.
)
(2345678901234567890123456789012345678901234567890123456789012345678901)
$include $lib/reflist
$define Tell me @ swap notify $enddef
$define CCVERSION "2.0" $enddef
lvar ourChannel (* string: name of channel *)
lvar ourFunc (* string: #option string *)
lvar ourParam (* string: #option parameter *)
lvar ourArg (* string: cmd arg *)
lvar ourPlayer (* dbref: player chatting or being modified *)
lvar ourMessage (* string: message to send to channel *)
lvar ourBase (* string: leading portion of channel prop *)
lvar ourExit (* dbref: dbref of alias command *)
lvar ourCounter (* int: loop control counter *)
lvar ourTrig (* dbref: main trigger action... not an alias *)
: DoHelp ( -- ) (* show help screen *)
" " Tell
prog name " (#" strcat prog intostr strcat ")" strcat Tell
" " Tell
" $channel <msg> .............. Broadcast <msg> on this channel"
ourChannel @ "$channel" subst Tell
" $channel #list .............. List available channels"
ourChannel @ "$channel" subst Tell
" $channel #on ................ Join this channel"
ourChannel @ "$channel" subst Tell
" $channel #off ............... Leave this channel"
ourChannel @ "$channel" subst Tell
" $channel #nick <nickname> ... Set nickname for this channel"
ourChannel @ "$channel" subst Tell
" $channel #alias <alias> ..... Create command alias for this channel"
ourChannel @ "$channel" subst Tell
" $channel #noalias <alias> ... Remove command alias for this channel"
ourChannel @ "$channel" subst Tell
" $channel #who ............... List online members of this channel"
ourChannel @ "$channel" subst Tell
" $channel #all ............... List all members of this channel"
ourChannel @ "$channel" subst Tell
" $channel #gag <player> ...... Gag <player> for this channel"
ourChannel @ "$channel" subst Tell
" $channel #ungag <player> .... Ungag <player> for this channel"
ourChannel @ "$channel" subst Tell
" $channel #lock .............. Lock yourself to this channel"
ourChannel @ "$channel" subst Tell
" $channel #unlock ............ Remove channel lock"
ourChannel @ "$channel" subst Tell
" $channel #create <channel> .. Create new channel <channel> (admin)"
ourChannel @ "$channel" subst Tell
" $channel #delete <channel> .. Delete channel <channel> (admin)"
ourChannel @ "$channel" subst Tell
" $channel #public ............ Designate channel public (admin)"
ourChannel @ "$channel" subst Tell
" $channel #private ........... Designate channel private (admin)"
ourChannel @ "$channel" subst Tell
" $channel #invite <player> ... "
"Authorize <plyr> for private channel (admin)"
strcat ourChannel @ "$channel" subst Tell
" $channel #exclude <player> .. "
"Unauthorize <plyr> for private channel (admin)"
strcat ourChannel @ "$channel" subst Tell
(
" " Tell
"The #lock option ensures that broadcasts go to the locked channel, "
"regardless of which channel you use as the command. "
"Wizards and the owner of the trigger action have admin permission. "
"Only channels you may join are listed with the #channels option. It "
"is not necessary to type #options completely: you may type only "
"enough characters to designate a unique option."
strcat strcat strcat strcat strcat Tell
)
;
: remove_dir ( d s -- ) (* remove dir s from d; leave subdirs *)
dup "*/" smatch not if
"/" strcat
then
over over nextprop swap pop
begin
dup while
over over nextprop
3 pick rot "" setprop
repeat
pop pop
;
: DoCheckAdminPerm ( -- ) (* check admin perm; kill process if no *)
me @ "W" flag?
me @ ourTrig @ owner dbcmp or not if
">> Permission denied." Tell pid kill
then
;
: DoCheckMember ( -- i ) (* return true if user is a channel member *)
ourTrig @ "_channels/$channel/$player"
ourChannel @ "$channel" subst
me @ intostr "$player" subst getpropstr if
1
else
0
then
;
: DoCheckJoinPerm ( -- i ) (* return true if user can join channel *)
ourTrig @ "_private/$channel" (* is channel private? ... *)
ourChannel @ "$channel" subst getprop if (* ... if so, check perm *)
ourTrig @ "_private/$channel/$player"
ourChannel @ "$channel" subst
me @ intostr "$player" subst
getprop if
1
else
0
then
else
1 (* ... otherwise, return true *)
then
;
: DoFormatMessage ( -- ) (* format message string *)
ourChannel @ toupper (* prepend channel name *)
" >> " strcat
ourTrig @ "_channels/$channel/$player" (* add name or nick *)
ourChannel @ "$channel" subst
me @ intostr "$player" subst getpropstr strcat
ourArg @ ":" stringpfx if (* format msg as pose or say *)
ourArg @ 1 strcut swap pop striplead ourArg !
ourArg @ "'" stringpfx
ourArg @ "," stringpfx or not if
" " strcat
then
ourArg @ strcat ourMessage ! (* final string stored in ourMessage *)
else
" says, \"" strcat
ourArg @ strcat
"\"" strcat ourMessage !
then
;
: DoChat ( -- ) (* send a message to channel *)
ourMessage @ not (* check: has user joined channel? *)
DoCheckMember not and if
">> You must join a channel before sending messages." Tell exit
then
(* no lurking listening dark wizzes... ethics, ya know *)
me @ "D" flag? if
">> Sorry, you cannot talk or listen on a channel while set Dark."
Tell exit
then
ourMessage @ not if (* go format message *)
DoFormatMessage
then
ourTrig @ "_channels/$channel/"
ourChannel @ "$channel" subst
dup ourBase !
nextprop (* send message to channel members *)
begin
dup while
dup "" ourBase @ subst
atoi dbref
dup ok? not if (* remove invalid dbrefs from channel members *)
ourTrig @ "_private/$channel/"
ourChannel @ "$channel" subst
rot intostr strcat remove_prop
ourTrig @ over nextprop
ourTrig @ rot remove_prop
continue
then (* remove nonplayer dbrefs from channel members *)
dup player? not if
ourTrig @ "_private/$channel/"
ourChannel @ "$channel" subst
rot intostr strcat remove_prop
ourTrig @ over nextprop
ourTrig @ rot remove_prop
continue
then
dup "_prefs/comchan/gag/" ourChannel @ strcat getpropstr if
dup "_prefs/comchan/gag/" ourChannel @ strcat me @ REF-inlist? if
pop ourTrig @ swap nextprop continue
then
then
dup awake?
over "D" flag? not and if
dup "_prefs/2space" getpropstr if
dup " " notify
then
ourMessage @ notify (* send message *)
else
pop
then
ourTrig @ swap nextprop
repeat
pop
;
: DoLock ( -- ) (* lock to current channel *)
DoCheckJoinPerm not if
">> Permission denied." Tell exit
then
DoCheckMember not if
">> You need to join a channel before locking to it." Tell exit
then
me @ "_prefs/comchan/lock" ourChannel @ setprop
">> Locked." Tell
;
: DoUnlock ( -- ) (* lock to current channel *)
DoCheckJoinPerm not if
">> Permission denied." Tell exit
then
me @ "_prefs/comchan/lock" remove_prop
">> Unlocked." Tell
;
: DoGag ( -- ) (* gag specified player *)
DoCheckJoinPerm not if
">> Permission denied." Tell exit
then
ourParam @ if
ourParam @ .pmatch
dup #-1 dbcmp if
">> Player not found." Tell pop exit
then
dup #-2 dbcmp if
">> Ambiguous. I don't know who you mean." Tell pop exit
then
me @ "_prefs/comchan/gag/" ourChannel @ strcat 3 pick REF-add
">> " swap name strcat " gagged." strcat Tell
else
">> Syntax: $command #gag <player>"
command @ "$command" subst Tell
then
;
: DoUngag ( -- ) (* ungag specified player *)
DoCheckJoinPerm not if
">> Permission denied." Tell exit
then
ourParam @ if
ourParam @ .pmatch
dup #-1 dbcmp if
">> Player not found." Tell pop exit
then
dup #-2 dbcmp if
">> Ambiguous. I don't know who you mean." Tell pop exit
then
me @ "_prefs/comchan/gag/" ourChannel @ strcat 3 pick REF-delete
">> " swap name strcat " ungagged." strcat Tell
else
">> Syntax: $command #gag <player>"
command @ "$command" subst Tell
then
;
: DoList ( -- ) (* show available channels *)
" " Tell
"__Channels______Status___Nick______________________________________________"
Tell
ourTrig @ name ";" explode
begin
dup while
ourTrig @ "_private/" 4 pick strcat getprop
ourTrig @ "_private/$chan/" 5 pick "$chan" subst me @ intostr strcat
getprop not and if
swap pop 1 - continue
then
" " 3 pick toupper strcat
" " strcat 16 strcut pop
ourTrig @ "_channels/$chan/" 5 pick "$chan" subst me @ intostr strcat
getpropstr if
"ON"
else
"OFF"
then
strcat " " strcat 25 strcut pop
ourTrig @ "_channels/$chan/" 5 rotate "$chan" subst me @ intostr strcat
getpropstr dup not if
pop me @ name
then
strcat 78 strcut pop Tell
1 -
repeat
pop
;
: DoWho ( -- ) (* show online members of current channel *)
">> Listeners on channel $channel:"
ourChannel @ toupper "$channel" subst Tell " " Tell
"_channels/$channel/" ourChannel @ "$channel" subst ourBase !
ourTrig @ ourBase @ nextprop
begin
dup while
dup "" ourBase @ subst atoi dbref dup
awake?
over "D" flag? not and if
" " swap name strcat dup
ourTrig @ 4 pick getpropstr
" " swap strcat smatch not if
ourTrig @ 3 pick getpropstr
"]" strcat " [" swap strcat strcat
then
Tell
else
pop
then
ourTrig @ swap nextprop
repeat
pop
;
: DoAll ( -- ) (* show all members of current channel *)
DoCheckJoinPerm not if
">> Permission denied." Tell exit
then
">> Members of channel $channel:"
ourChannel @ toupper "$channel" subst Tell " " Tell
"_channels/$channel/" ourChannel @ "$channel" subst ourBase !
ourTrig @ ourBase @ nextprop
begin
dup while
dup "" ourBase @ subst atoi dbref
" " swap name strcat dup
ourTrig @ 4 pick getpropstr
" " swap strcat smatch not if
ourTrig @ 3 pick getpropstr
"]" strcat " [" swap strcat strcat
then
Tell
ourTrig @ swap nextprop
repeat
pop
;
: DoJoin ( -- ) (* join current channel *)
DoCheckJoinPerm not if (* check: user can join? *)
">> Permission denied." Tell exit
then
ourTrig @ "_channels/$channel/$player"
ourChannel @ "$channel" subst (* only set if not a member already *)
me @ intostr "$player" subst getprop not if (* notify channel *)
"$channel >> ## $player joins channel $channel. ##"
ourChannel @ toupper "$channel" subst
me @ name "$player" subst
ourMessage ! DoChat
ourTrig @ "_channels/$channel/$player" (* set membership prop *)
ourChannel @ "$channel" subst
me @ intostr "$player" subst
me @ name setprop
then
">> You join channel $channel." (* notify player *)
ourChannel @ toupper "$channel" subst Tell
;
: DoLeave ( -- ) (* leave current channel *)
ourTrig @ "_channels/$channel/$player" (* remove membership prop *)
ourChannel @ "$channel" subst
me @ intostr "$player" subst
remove_prop
">> You leave channel $channel." (* notify player *)
ourChannel @ toupper "$channel" subst tell
;
: DoCreate ( -- ) (* create a new channel *)
DoCheckAdminPerm (* check: admin permission? *)
ourParam @ if (* get name of new channel *)
ourParam @ strip ourParam !
else
">> Syntax: $channel #create <new channel>"
ourChannel @ "$channel" subst Tell exit
then
ourParam @ "lock" smatch if
">> Sorry, 'lock' is used internally by this program." Tell
">> Please choose a different channel name." Tell exit
then
(* adding exit alias creates channel *)
ourTrig @ dup name ";" strcat ourParam @ strcat setname
">> Channel $channel created." (* notify user *)
ourParam @ toupper "$channel" subst Tell
;
: DoDelete ( -- ) (* delete an existing channel *)
DoCheckAdminPerm (* check: admin permission? *)
ourParam @ if (* get name of channel to delete *)
ourParam @ strip ourParam !
else
">> Syntax: $channel #delete <channel>"
ourChannel @ "$channel" subst Tell exit
then
(* get confirmation *)
begin
">> Please confirm: You wish to delete channel $channel? (y/n)"
ourParam @ "$channel" subst Tell
read
"no" over stringpfx if
">> Aborted." Tell pop exit
then
"yes" swap stringpfx if
break
else
">> Entry not understood." Tell
then
repeat
(* can't delete the last channel... just cuz *)
ourTrig @ name ourParam @ smatch if
">> Sorry, you can't #delete the last channel." Tell exit
then
(* find channel alias string; remove from cmd name; remove propdirs *)
ourTrig @ name ";" ourParam @ strcat ";" strcat instr if
ourTrig @ dup name ";" ";" ourParam @ strcat ";" strcat subst setname
ourTrig @ "_channels/" ourParam @ strcat remove_dir
ourTrig @ "_channels/" ourParam @ strcat remove_prop
ourTrig @ "_private/" ourParam @ strcat remove_dir
ourTrig @ "_private/" ourParam @ strcat remove_prop
">> Channel $channel deleted."
ourParam @ toupper "$channel" subst Tell
exit
then
ourTrig @ name ";" ourParam @ strcat instr if
ourTrig @ dup name "" ";" ourParam @ strcat subst setname
ourTrig @ "_channels/" ourParam @ strcat remove_dir
ourTrig @ "_channels/" ourParam @ strcat remove_prop
ourTrig @ "_private/" ourParam @ strcat remove_dir
ourTrig @ "_private/" ourParam @ strcat remove_prop
">> Channel $channel deleted."
ourParam @ toupper "$channel" subst Tell
exit
then
ourTrig @ name ourParam @ ";" strcat instr if
ourTrig @ dup name "" ourParam @ ";" strcat subst setname
ourTrig @ "_channels/" ourParam @ strcat remove_dir
ourTrig @ "_channels/" ourParam @ strcat remove_prop
ourTrig @ "_private/" ourParam @ strcat remove_dir
ourTrig @ "_private/" ourParam @ strcat remove_prop
">> Channel $channel deleted."
ourParam @ toupper "$channel" subst Tell
exit
then
">> ERROR: Channel $channel not found."
ourParam @ toupper "$channel" subst Tell
;
: DoInvite ( -- ) (* authorize a player for current channel *)
DoCheckAdminPerm (* check: admin permission? *)
ourParam @ .pmatch (* find player *)
dup not if
">> Player not found." Tell exit
then
dup #-2 dbcmp if
">> Ambiguous. I don't know who you mean." Tell exit
then
ourPlayer ! (* set authorizing prop *)
ourTrig @ "_private/$channel/$player"
ourChannel @ "$channel" subst
ourPlayer @ intostr "$player" subst
"1" setprop
">> $player authorized for channel $channel." (* notify user *)
ourChannel @ toupper "$channel" subst
ourPlayer @ name "$player" subst Tell
;
: DoExclude ( -- ) (* unauthorize a player for current channel *)
DoCheckAdminPerm (* check: admin permission? *)
ourParam @ .pmatch (* find player *)
dup not if
">> Player not found." Tell exit
then
dup #-2 dbcmp if
">> Ambiguous. I don't know who you mean." Tell exit
then
ourPlayer ! (* remove authorizing prop *)
ourTrig @ "_private/$channel/$player"
ourChannel @ "$channel" subst
ourPlayer @ intostr "$player" subst
remove_prop
ourTrig @ "_channels/$channel/$player" (* remove channel membership *)
ourChannel @ "$channel" subst
ourPlayer @ intostr "$player" subst
remove_prop
">> $player unauthorized for channel $channel." (* notify user *)
ourChannel @ toupper "$channel" subst
ourPlayer @ name "$player" subst Tell
;
: DoPublic ( -- ) (* designate current channel public *)
DoCheckAdminPerm (* check: admin permission? *)
ourTrig @ "_private/$channel" (* remove private channel prop *)
ourChannel @ "$channel" subst
remove_prop
">> Channel $channel marked Public."
ourChannel @ toupper "$channel" subst Tell
;
: DoPrivate ( -- ) (* designate current channel private *)
DoCheckAdminPerm (* check: admin permission? *)
ourTrig @ "_private/$channel" (* set private channel prop *)
ourChannel @ "$channel" subst
"1" setprop
">> Channel $channel marked Private." (* notify user *)
ourChannel @ toupper "$channel" subst Tell
;
: DoAlias ( -- ) (* create an alias for current channel *)
DoCheckMember not if (* check: is a channel member? *)
">> You must join a channel before creating an alias for it."
Tell exit
then
DoCheckJoinPerm not if (* check: can join? *)
">> Permission denied." Tell exit
then
ourParam @ if (* get alias name *)
ourParam @ strip ourParam !
else
">> Syntax: $channel #alias <alias>"
ourChannel @ "$channel" subst Tell exit
then
(* check: does player already have an alias action? *)
me @ exits
begin
dup while
dup "~comchannel" getpropstr if
dup ourExit ! break
then
next
repeat
pop
(* if no alias action exists, create one... *)
ourExit @ not if
me @ ourParam @ newexit ourExit !
ourExit @ "~comchannel" CCVERSION setprop
ourExit @ prog setlink
else (* ... otherwise, add an alias name to it *)
ourExit @ dup name ";" strcat ourParam @ strcat setname
then
(* set props mapping alias to channel *)
ourExit @ "_alia/$alia"
ourParam @ strip "$alia" subst
ourChannel @ setprop
me @ "_prefs/comchan/$alia"
ourParam @ strip "$alia" subst
ourChannel @ setprop
">> Alias created." Tell (* notify user *)
;
: DoNoAlias ( -- ) (* remove an alias for current channel *)
ourParam @ if (* get alias name *)
ourParam @ strip ourParam !
else
">> Syntax: $channel #noalias <alias>"
ourChannel @ "$channel" subst Tell exit
then
me @ exits (* find alias action *)
begin
dup while
dup "~comchannel" getpropstr if
dup ourExit ! break
then
next
repeat
pop
ourExit @ not if (* exit if none found *)
">> Alias not found." Tell exit
then
(* if only one alias name, recycle action *)
ourExit @ name ourParam @ smatch if
ourExit @ #-1 setlink (* have to unlink before can recycle *)
ourExit @ recycle
me @ "_prefs/comchan/" ourParam @ strcat remove_prop
">> Alias removed." Tell exit
then
(* remove alias name from action name *)
ourExit @ name ";" ourParam @ strcat ";" strcat instr if
ourExit @ dup name ";" ";" ourParam @ strcat ";" strcat subst setname
then
ourExit @ name ";" ourParam @ strcat instr if
ourExit @ dup name "" ";" ourParam @ strcat subst setname
then
ourExit @ name ourParam @ ";" strcat instr if
ourExit @ dup name "" ourParam @ ";" strcat subst setname
then
(* remove mapping props *)
ourExit @ "_alia/" ourParam @ strcat remove_prop
me @ "_prefs/comchan/" ourParam @ strcat remove_prop
">> Alias removed." Tell (* notify user *)
;
: DoNick ( -- ) (* set user's nick for current channel *)
DoCheckJoinPerm not if (* check: can use this channel? *)
">> Permission denied." Tell exit
then
(* make sure we have something in ourParam *)
ourParam @ if
ourParam @ strip ourParam !
else
me @ name ourParam !
then
(* check: nick is not a player name? nick is not in use? *)
ourParam @ .pmatch dup if
me @ dbcmp not if
">> Sorry, that's a player's real name." Tell exit
then
else
pop
then
ourTrig @ "_channels/$channel/"
ourChannel @ "$channel" subst nextprop
begin
dup while
ourTrig @ over getpropstr ourParam @ smatch if
">> Sorry, that nickname is already in use." Tell pop exit
then
ourTrig @ swap nextprop
repeat
pop
(* set membership prop to nick name *)
ourTrig @ "_channels/$channel/$player"
ourChannel @ "$channel" subst
me @ intostr "$player" subst ourParam @ strip
dup not if
pop me @ name
then
setprop
">> Nick for channel $channel set to `$nick'." (* notify user *)
ourChannel @ toupper "$channel" subst
ourParam @ "$nick" subst Tell
;
: main ( s -- )
"me" match me !
(* may be triggered by an alias; put real trig in var trig *)
prog "~maincom" getprop not if
prog "~maincom" trig setprop
trig "~maincom" trig setprop
then
prog "~maincom" getprop ourTrig !
me @ "_prefs/comchan/lock" getpropstr dup if
ourChannel !
else
pop
me @ "_prefs/comchan/" command @ strcat getpropstr dup if
ourChannel !
else
pop command @ ourChannel !
then
then
dup if
dup ";" stringpfx if
1 strcut swap pop
":" swap strcat
then
ourArg !
ourArg @ "#" stringpfx if
ourArg @ " " instr if
ourArg @ dup " " instr strcut
strip ourParam ! strip ourFunc !
else
ourArg @ strip ourFunc !
then
ourFunc @ "#" stringpfx if
"#on" ourFunc @ smatch if DoJoin exit then
"#off" ourFunc @ smatch if DoLeave exit then
"#help" ourFunc @ stringpfx if DoHelp exit then
"#who" ourFunc @ stringpfx if DoWho exit then
"#list" ourFunc @ stringpfx if DoList exit then
"#all" ourFunc @ stringpfx if DoAll exit then
"#lock" ourFunc @ stringpfx if DoLock exit then
"#unlock" ourFunc @ stringpfx if DoUnlock exit then
"#gag" ourFunc @ stringpfx if DoGag exit then
"#ungag" ourFunc @ stringpfx if DoUngag exit then
"#nickname" ourFunc @ stringpfx if DoNick exit then
"#alias" ourFunc @ stringpfx if DoAlias exit then
"#noalias" ourFunc @ stringpfx if DoNoAlias exit then
"#invite" ourFunc @ stringpfx if DoInvite exit then
"#exclude" ourFunc @ stringpfx if DoExclude exit then
"#create" ourFunc @ stringpfx if DoCreate exit then
"#delete" ourFunc @ stringpfx if DoDelete exit then
"#public" ourFunc @ stringpfx if DoPublic exit then
"#private" ourFunc @ stringpfx if DoPrivate exit then
then
then
DoChat
else
pop
then
;
.
c
q