@program bulletin.muf 1 9999 d i ( bulletin.muf v1.0 Jessy @ Forgotten Paths 3/97 A log-on announcemnt program to supplement MOTD. Bulletins can be either 'sticky' or 'slippery': sticky bulletins are shown to all players on each log-on, and remain until cleared; slippery bulletins are shown to each player one time, but can be recalled by typing a command. Up to 10 slippery bulletins may be stored. Installation: Link a global action, preferably named '@bulletin;@bull', to this program. Set the program Link_OK and put it in the global connect queue: @propset #0=dbref:_connect/bulletin:<prog #dbref> Bulletin.muf requires Mucker level 3. By default, staff-only commands are only available to wizards. To add other designated staff, set a ~keyprop, such as ~staff or @staff. Ex: @set bulletin.muf=~keyprop:~staff In this example, wizards and anyone set with a ~staff property will be able to use staff bulletin commands. Use: The program will automatically display all sticky bulletins and any unread slippery bulletins at log-on. The stickies and recent slipperies can be redisplayed by typing the action name, without arguments. To see all slipperies, regardless of time of entry, use the #all argument. Wizards or designated staff members can #add new slippery bulletins, or set new #sticky bulletins. The #clear argu- ment clears all bulletins. The #undo argument clears only the most recent entry. Bulletins can contain MPI, for selective notifications, storing records of who's received bulletins, calling a longer list or program at log-on, etc. <cmd> Show stickies and any recent slipperies <cmd> #all Show stickies and all slipperies <cmd> #new = <msg> Set a new slippery bulletin <staff only> <cmd> #sticky = <msg> Set a new sticky bulletin <staff only> <cmd> #clear Clear all bulletins <staff only> <cmd> #delete Go to prompt; delete a bulletin <staff only> <cmd> #undo Clear the most recent bulletin <staff only> <cmd> #help Display help screen Bulletin.muf may be freely ported. Please comment any changes. ) lvar ourCounter ! (* str: loop-control ourCounter *) lvar ourBoolean ! (* int: decision control variable *) lvar ourString ! (* str: stores arg string; may be modified *) $define Tell me @ swap notify $enddef $define scounter++ ourCounter @ atoi 1 + intostr ourCounter ! $enddef : Pad ( s i -- s ) (* pad string s to i chars *) swap " " strcat swap strcut pop ; : ParseThis ( d s -- s ) (* returns d's prop s, parsed for MPI *) dup 3 pick swap getpropstr 0 parseprop ; : QCheck ( -- i )(* wrap smatch for .q in an if, to avoid null string match error if user enters a string of all spaces, which SayPose would strip to a null string *) dup if dup ".quit" swap stringpfx if pop ">> Done." Tell pid kill then then ; : SayPose ( -- ) (* scan keyboard input for poses and says. *) (* emit poses and says, and continue *) begin (* BEGIN INPUT-SCANNING LOOP *) (* does input begin with " or say ? -- say if so & continue *) read (* emit poses and says, and continue *) dup "\"" stringpfx over "say " stringpfx or if dup "say " stringpfx if 4 strcut else 1 strcut then swap pop me @ name " says, \"" strcat swap strcat "\"" strcat dup loc @ me @ rot notify_except " (in " strcat caller name dup "*.muf" smatch if dup strlen 4 - strcut pop then strcat ")" strcat Tell continue then (* does input begin with : or pose ? -- pose if so & continue *) dup ":" stringpfx over "pose " stringpfx or if dup "pose " stringpfx if 5 strcut else 1 strcut then swap pop me @ name over "'*" smatch not if " " strcat then swap strcat dup loc @ me @ rot notify_except " (in " strcat caller name dup "*.muf" smatch if dup strlen 4 - strcut pop then strcat ")" strcat Tell continue then exit (* it's not a pose or say; exit *) repeat (* END INPUT-SCANNING LOOP *) ; : CheckPerms ( -- i ) (* returns true if user is wiz or staff *) (* either wiz bit or ~keyprop is OK *) me @ "W" flag? prog "~keyprop" getpropstr dup if me @ swap getpropstr then or if 1 else 0 then ; : TellLoop ( -- ) (* parses bulletins and displays *) begin ourCounter @ while " " Tell prog ourCounter @ ParseThis Tell prog ourCounter @ nextprop ourCounter ! repeat ; : ClearLoop ( -- ) (* clears a directory of bulletins *) begin ourCounter @ while prog ourCounter @ over over nextprop ourCounter ! remove_prop repeat ; : DoHelp ( -- ) (* displays help screen *) " " Tell "Bulletin.muf (#" prog intostr strcat ")" strcat Tell " " Tell "A log-on message program to supplement MOTD. Bulletins may be " "either 'sticky' (permanent) or 'slippery' (shown once, recallable, " "FIFO roll over)." strcat strcat Tell " " Tell command @ 22 Pad "Show permanent and recent bulletins" strcat Tell command @ " #all" strcat 22 Pad "Show all bulletins" strcat Tell CheckPerms if command @ " #new = <msg> strcat 22 Pad "Set a new slippery bulletin (staff only)" strcat Tell command @ " #sticky = <msg> strcat 22 Pad "Set a new sticky bulletin (staff only)" strcat Tell command @ " #clear" strcat 22 Pad "Clear all bulletins (staff only)" strcat Tell command @ " #delete" strcat 22 Pad "Go to prompt; delete a bulletin (staff only)" strcat Tell command @ " #undo" strcat 22 Pad "Clear the most recent bulletin (staff only)" strcat Tell then prog "L" flag? if " " Tell "For more information: @list #" prog intostr strcat " = 1-52" strcat Tell then " " Tell ; : DoNew ( s -- ) (* adds a slippery bulletin *) (* check permission *) CheckPerms not if ">> Permission denied." .tell exit then 1 (* find out how many slips we have now... *) prog "_bul/slippery/" nextprop ourCounter ! begin (* BEGIN SLIP-COUNTING LOOP *) ourCounter @ while 1 + prog ourCounter @ nextprop ourCounter ! repeat (* END SLIP-COUNTING LOOP *) (* if more than 10, clear the old ones; should be only one to clear *) dup 10 > if 10 - prog "_bul/slippery/" nextprop ourCounter ! begin (* BEGIN PROP-CLEARING LOOP *) dup while prog ourCounter @ over over nextprop ourCounter ! remove_prop 1 - repeat (* END PROP-CLEARING LOOP *) pop else pop then (* Bulletins are stored by systime: make sure prop string is not in use, either from another staff member @bull'ing at the same time, or from an upload of several bull's at once. Sleep for 1 sec if prop name is in use. Go to background in case there are a lot, so user won't lock up *) begin prog "_bul/slippery/" systime intostr strcat getpropstr while 1 sleep repeat (* check usage *) ourString @ "=" instr dup not if pop ">> Syntax: " command @ strcat " #new = <message> strcat Tell exit else (* trim signal chars if correct; store actual bull *) ourString @ swap strcut swap pop strip ourString ! then (* set bull prop *) prog "_bul/slippery/" systime intostr strcat ourString @ setprop ">> Bulletins updated." Tell ; : DoSticky ( s -- ) (* sets sticky bulletin *) (* check permission *) CheckPerms not if ">> Permission denied." .tell exit then (* make sure prop name is not in use *) begin prog "_bul/sticky/" systime intostr strcat getpropstr while 1 sleep repeat (* check usage *) ourString @ "=" instr dup not if pop ">> Syntax: " command @ strcat " #sticky = <message> strcat Tell exit else (* trim signal chars if correct; store actual bull *) ourString @ swap strcut swap pop strip ourString ! then (* set bull prop *) prog "_bul/sticky/" systime intostr strcat ourString @ setprop ">> Bulletins updated." Tell ; : DoClear ( -- ) (* clears all bulletins *) (* check permission *) CheckPerms not if ">> Permission denied." .tell exit then prog "_bul/sticky/" nextprop ourCounter ! ClearLoop prog "_bul/slippery/" nextprop ourCounter ! ClearLoop ">> Cleared." Tell ; : DoDelete ( -- ) (* undo specified bulletin *) (* check permission *) CheckPerms not if ">> Permission denied." .tell exit then " " Tell "0" ourCounter ! prog "_bul/sticky/" nextprop ourString ! begin ourString @ while scounter++ ourCounter @ ")" strcat 4 pad prog ourString @ getpropstr 60 strcut if "..." strcat then strcat " (sticky)" strcat Tell ourString @ prog ourString @ nextprop ourString ! repeat prog "_bul/slippery/" nextprop ourString ! begin ourString @ while scounter++ ourCounter @ ")" strcat 4 pad prog ourString @ getpropstr 60 strcut if "..." strcat then strcat " (slippery)" strcat Tell ourString @ prog ourString @ nextprop ourString ! repeat ourCounter @ atoi not if ">> No entries to delete!" Tell exit then " " Tell begin ">> Enter number of bulletin to delete, or .q to quit." Tell SayPose strip QCheck dup number? not if ">> That's not a number." Tell pop continue then dup atoi 0 <= if ">> I'm pretty sure we don't have an entry with that number." Tell pop continue then dup atoi depth 3 - > if ">> Invalid entry." Tell pop continue then break repeat ourString ! depth rotate pop "1" ourCounter ! begin ourCounter @ ourString @ smatch not while depth rotate pop scounter++ repeat depth rotate prog swap remove_prop begin depth while pop repeat ">> Deleted." Tell ; : DoUnDo ( -- ) (* undo most recent bulletin *) (* check permission *) CheckPerms not if ">> Permission denied." .tell exit then (* figure out which is most recent: sticky or slippery? *) prog "_bul/sticky/" nextprop ourCounter ! begin prog ourCounter @ nextprop dup while ourCounter ! repeat pop ourCounter @ 12 strcut swap pop atoi prog "_bul/slippery/" nextprop ourCounter ! begin prog ourCounter @ nextprop dup while ourCounter ! repeat pop ourCounter @ 14 strcut swap pop atoi > if "_bul/sticky/" else "_bul/slippery/" then (* take of last bul from appropriate dir *) prog swap nextprop ourCounter ! begin prog ourCounter @ nextprop dup while ourCounter ! repeat pop prog ourCounter @ remove_prop ">> Last entry erased." Tell ; : DoReview ( -- ) (* shows stickies and recent slipperies *) prog "_bul/sticky/" nextprop dup if ourCounter ! TellLoop 1 ourBoolean ! else pop then prog "_bul/slippery/" ourString @ if (* if arg is #all, start from beginning... *) ourString @ "#all*" smatch not if (* ... otherwise show only recents *) me @ "_prefs/bul/last" getpropstr strcat then then nextprop dup if ourCounter ! TellLoop 1 ourBoolean ! else pop then ; : DoBulletin ( -- ) (* shows bulletins at logon and by user prompt *) DoReview me @ "_prefs/bul/prev" me @ "_prefs/bul/last" getpropstr dup not if pop systime intostr then setprop me @ "_prefs/bul/last" systime intostr setprop ourBoolean @ if "(Type '@bulletin' to see this message again.)" Tell then ; : main "me" match me ! strip dup ourString ! me @ "guest_player" getpropstr if exit then dup if dup "CONNECT" smatch if DoBulletin else dup "#new*" smatch if DoNew else dup "#add*" smatch if DoNew else dup "#sticky*" smatch if DoSticky else dup "#all" smatch if DoReview else dup "#help" swap stringpfx if DoHelp else dup "#undo" swap stringpfx if DoUnDo else dup "#delete" swap stringpfx if DoDelete else dup "#clear" swap stringpfx if DoClear else ">> Command not understood." Tell then then then then then then then then then else DoReview then ; . c q