@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