@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