From 86dd34a0f43a916402d4fe9684902717488f9928 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Thu, 1 Jun 2006 17:35:23 +0000 Subject: [PATCH] Added the TellHandler. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4168 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../crowd/chat/client/ChatDirector.as | 27 +++- .../crowd/chat/client/ClearHandler.as | 16 +- .../crowd/chat/client/CommandHandler.as | 5 +- .../crowd/chat/client/EmoteHandler.as | 17 +- .../crowd/chat/client/HelpHandler.as | 22 ++- .../crowd/chat/client/SpeakHandler.as | 16 +- .../crowd/chat/client/TellHandler.as | 148 ++++++++++++++++++ .../crowd/chat/client/ThinkHandler.as | 17 +- 8 files changed, 203 insertions(+), 65 deletions(-) create mode 100644 src/as/com/threerings/crowd/chat/client/TellHandler.as diff --git a/src/as/com/threerings/crowd/chat/client/ChatDirector.as b/src/as/com/threerings/crowd/chat/client/ChatDirector.as index f08f5772b..ca34e1a8b 100644 --- a/src/as/com/threerings/crowd/chat/client/ChatDirector.as +++ b/src/as/com/threerings/crowd/chat/client/ChatDirector.as @@ -93,11 +93,12 @@ public class ChatDirector extends BasicDirector } var msg :MessageBundle = _msgmgr.getBundle(_bundle); // register our default chat handlers - registerCommandHandler(msg, "help", new HelpHandler(this)); - registerCommandHandler(msg, "clear", new ClearHandler(this)); - registerCommandHandler(msg, "speak", new SpeakHandler(this)); - registerCommandHandler(msg, "emote", new EmoteHandler(this)); - registerCommandHandler(msg, "think", new ThinkHandler(this)); + registerCommandHandler(msg, "help", new HelpHandler()); + registerCommandHandler(msg, "clear", new ClearHandler()); + registerCommandHandler(msg, "speak", new SpeakHandler()); + registerCommandHandler(msg, "emote", new EmoteHandler()); + registerCommandHandler(msg, "think", new ThinkHandler()); + registerCommandHandler(msg, "tell", new TellHandler()); } /** @@ -320,7 +321,7 @@ public class ChatDirector extends BasicDirector var cmd :CommandHandler = (possibleCommands.get(cmdName) as CommandHandler); var result :String = - cmd.handleCommand(speakSvc, cmdName, args, hist); + cmd.handleCommand(_cctx, speakSvc, cmdName, args, hist); if (result != ChatCodes.SUCCESS) { return result; } @@ -836,6 +837,15 @@ public class ChatDirector extends BasicDirector return text; } + /** + * Check that after mogrification the message is not too long. + * @return an error mesage if it is too long, or null. + */ + internal function checkLength (msg :String) :String + { + return null; // TODO + } + /** * Returns a hashmap containing all command handlers that match the * specified command (i.e. the specified command is a prefix of their @@ -860,6 +870,11 @@ public class ChatDirector extends BasicDirector return matches; } + internal function accessHistory () :Array + { + return _history; + } + /** * Adds a chatter to our list of recent chatters. */ diff --git a/src/as/com/threerings/crowd/chat/client/ClearHandler.as b/src/as/com/threerings/crowd/chat/client/ClearHandler.as index 5a4fd2614..aed5ab1cc 100644 --- a/src/as/com/threerings/crowd/chat/client/ClearHandler.as +++ b/src/as/com/threerings/crowd/chat/client/ClearHandler.as @@ -1,23 +1,17 @@ package com.threerings.crowd.chat.client { +import com.threerings.crowd.util.CrowdContext; + import com.threerings.crowd.chat.data.ChatCodes; public class ClearHandler extends CommandHandler { - public function ClearHandler (chatdir :ChatDirector) - { - _chatdir = chatdir; - } - public override function handleCommand ( - speakSvc :SpeakService, cmd :String, args :String, history :Array) - :String + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String { - _chatdir.clearDisplays(); + ctx.getChatDirector().clearDisplays(); return ChatCodes.SUCCESS; } - - /** Our ChatDirector. */ - protected var _chatdir :ChatDirector; } } diff --git a/src/as/com/threerings/crowd/chat/client/CommandHandler.as b/src/as/com/threerings/crowd/chat/client/CommandHandler.as index 3b963dd40..a6eb0bb85 100644 --- a/src/as/com/threerings/crowd/chat/client/CommandHandler.as +++ b/src/as/com/threerings/crowd/chat/client/CommandHandler.as @@ -1,6 +1,7 @@ package com.threerings.crowd.chat.client { import com.threerings.crowd.data.BodyObject; +import com.threerings.crowd.util.CrowdContext; /** * Used to implement a slash command (e.g. /who). @@ -26,8 +27,8 @@ public /* abstract */ class CommandHandler * ChatCodes#SUCCESS}. */ public function handleCommand ( - speakSvc :SpeakService, cmd :String, args :String, history :Array) - :String + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String { throw new Error("abstract"); } diff --git a/src/as/com/threerings/crowd/chat/client/EmoteHandler.as b/src/as/com/threerings/crowd/chat/client/EmoteHandler.as index c1da3d775..5cd919ee8 100644 --- a/src/as/com/threerings/crowd/chat/client/EmoteHandler.as +++ b/src/as/com/threerings/crowd/chat/client/EmoteHandler.as @@ -2,27 +2,22 @@ package com.threerings.crowd.chat.client { import com.threerings.util.StringUtil; +import com.threerings.crowd.util.CrowdContext; + import com.threerings.crowd.chat.data.ChatCodes; public class EmoteHandler extends CommandHandler { - public function EmoteHandler (chatdir :ChatDirector) - { - _chatdir = chatdir; - } - public override function handleCommand ( - speakSvc :SpeakService, cmd :String, args :String, history :Array) - :String + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String { if (StringUtil.isBlank(args)) { return "m.usage_emote"; } history[0] = cmd + " "; - return _chatdir.deliverChat(speakSvc, args, ChatCodes.EMOTE_MODE); + return ctx.getChatDirector().deliverChat( + speakSvc, args, ChatCodes.EMOTE_MODE); } - - /** Our ChatDirector. */ - protected var _chatdir :ChatDirector; } } diff --git a/src/as/com/threerings/crowd/chat/client/HelpHandler.as b/src/as/com/threerings/crowd/chat/client/HelpHandler.as index 30d781ca8..676e710ff 100644 --- a/src/as/com/threerings/crowd/chat/client/HelpHandler.as +++ b/src/as/com/threerings/crowd/chat/client/HelpHandler.as @@ -4,18 +4,15 @@ import com.threerings.util.Map; import com.threerings.util.MessageBundle; import com.threerings.util.StringUtil; +import com.threerings.crowd.util.CrowdContext; + import com.threerings.crowd.chat.data.ChatCodes; public class HelpHandler extends CommandHandler { - public function HelpHandler (chatdir :ChatDirector) - { - _chatdir = chatdir; - } - public override function handleCommand ( - speakSvc :SpeakService, cmd :String, args :String, history :Array) - :String + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String { var hcmd :String = ""; @@ -33,16 +30,18 @@ public class HelpHandler extends CommandHandler hcmd = hcmd.substring(1); } + var chatdir :ChatDirector = ctx.getChatDirector(); + // handle "/help help" and "/help boguscmd" - var possibleCmds :Map = _chatdir.getCommandHandlers(hcmd); + var possibleCmds :Map = chatdir.getCommandHandlers(hcmd); if ((hcmd === "help") || (possibleCmds.size() == 0)) { - possibleCmds = _chatdir.getCommandHandlers(""); + possibleCmds = chatdir.getCommandHandlers(""); possibleCmds.remove("help"); // remove help from the list } switch (possibleCmds.size()) { case 1: - _chatdir.displayFeedback(null, "m.usage_" + possibleCmds.keys()[0]); + chatdir.displayFeedback(null, "m.usage_" + possibleCmds.keys()[0]); return ChatCodes.SUCCESS; default: @@ -55,8 +54,5 @@ public class HelpHandler extends CommandHandler return MessageBundle.tcompose("m.usage_help", cmdList); } } - - /** Our ChatDirector. */ - protected var _chatdir :ChatDirector; } } diff --git a/src/as/com/threerings/crowd/chat/client/SpeakHandler.as b/src/as/com/threerings/crowd/chat/client/SpeakHandler.as index cc2ffcfc1..79d026ebb 100644 --- a/src/as/com/threerings/crowd/chat/client/SpeakHandler.as +++ b/src/as/com/threerings/crowd/chat/client/SpeakHandler.as @@ -2,27 +2,21 @@ package com.threerings.crowd.chat.client { import com.threerings.util.StringUtil; +import com.threerings.crowd.util.CrowdContext; + import com.threerings.crowd.chat.data.ChatCodes; public class SpeakHandler extends CommandHandler { - public function SpeakHandler (chatdir :ChatDirector) - { - _chatdir = chatdir; - } - public override function handleCommand ( - speakSvc :SpeakService, cmd :String, args :String, history :Array) - :String + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String { if (StringUtil.isBlank(args)) { return "m.usage_speak"; } history[0] = cmd + " "; - return _chatdir.requestChat(null, args, true); + return ctx.getChatDirector().requestChat(null, args, true); } - - /** Our ChatDirector. */ - protected var _chatdir :ChatDirector; } } diff --git a/src/as/com/threerings/crowd/chat/client/TellHandler.as b/src/as/com/threerings/crowd/chat/client/TellHandler.as new file mode 100644 index 000000000..e79d2b9ce --- /dev/null +++ b/src/as/com/threerings/crowd/chat/client/TellHandler.as @@ -0,0 +1,148 @@ +package com.threerings.crowd.chat.client { + +import com.threerings.util.Name; +import com.threerings.util.StringUtil; + +import com.threerings.crowd.data.BodyObject; +import com.threerings.crowd.util.CrowdContext; +import com.threerings.crowd.chat.data.ChatCodes; + +public class TellHandler extends CommandHandler +{ + public override function handleCommand ( + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String + { + if (StringUtil.isBlank(args)) { + return "m.usage_tell"; + } + + var chatdir :ChatDirector = ctx.getChatDirector(); + + var useQuotes :Boolean = (args.charAt(0) == "\""); + var bits :Array = parseTell(args); + var handle :String = (bits[0] as String); + var message :String = (bits[1] as String); + + // validate that we didn't eat all the tokens making the handle + if (StringUtil.isBlank(message)) { + return "m.usage_tell"; + } + + // make sure we're not trying to tell something to ourselves + var self :BodyObject = + (ctx.getClient().getClientObject() as BodyObject); + if (handle.toLowerCase() === + self.getVisibleName().toString().toLowerCase()) { + return "m.talk_self"; + } + + // and lets just give things an opportunity to sanitize the name + var target :Name = normalizeAsName(handle); + + // mogrify the chat + message = chatdir.mogrifyChat(message); + var err :String = chatdir.checkLength(message); + if (err != null) { + return err; + } + + // clear out from the history any tells that are mistypes + var fullHist :Array = chatdir.accessHistory(); + for (var ii :int = fullHist.length - 1; ii >= 0; ii--) { + var hist :String = (fullHist[ii] as String); + if (0 == hist.indexOf("/" + cmd)) { + var harg :String = StringUtil.trim( + hist.substring(cmd.length + 1)); + // we blow away any historic tells that have msg content + if (!StringUtil.isBlank(parseTell(harg)[1] as String)) { + fullHist.splice(ii, 1); + } + } + } + + // store the full command in the history, even if it was mistyped + var histEntry :String = cmd + " " + + (useQuotes ? ("\"" + target + "\"") : target.toString()) + + " " + message; + history[0] = histEntry; + + var rl :ResultListener = new ResultAdapter( + function (result :Object) :void { + + // replace the full one in the history with just: + // /tell "" + var newEntry :String = "/" + cmd + " " + + (useQuotes ? ("\"" + result + "\"") + : result.toString()) + " "; + var fullHist :Array = chatdir.accessHistory(); + var dex :int = fullHist.indexOf(newEntry); + if (dex != -1) { + fullHist.splice(dex, 1); + } + var del :int = fullHist.lastIndexOf("/" + histEntry); + if (del >= 0) { + fullHist[del] = newEntry; + } else { + fullHist.push(newEntry); + } + }, null); + chatdir.requestTell(target, message, rl); + + return ChatCodes.SUCCESS; + } + + /** + * Parse the tell into two strings, handle and message. If either + * one is null then the parsing did not succeed. + */ + protected function parseTell (args :String) :Array + { + var handle :String; + var message :String; + + if (args.charAt(0) == "\"") { + var nextQuote :int = args.indexOf("\"", 1); + if (nextQuote == -1 || nextQuote == 1) { + handle = message = null; // bogus parsing + + } else { + handle = StringUtil.trim(args.substring(1, nextQuote)); + message = StringUtil.trim(args.substring(nextQuote + 1)); + } + + } else { + var idx :int = args.search(/\s/); + if (idx == -1) { + handle = args; + message = ""; + + } else { + handle = StringUtil.trim(args.substring(0, idx)); + message = StringUtil.trim(args.substring(idx)); + } + } + + return [ handle, message ]; + } + + /** + * Turn the user-entered string into a Name object, doing + * any particular normalization we want to do along the way + * so that "/tell Bob" and "/tell BoB" don't both show up in history. + */ + protected function normalizeAsName (handle :String) :Name + { + return new Name(handle); + } + + /** + * Escape or otherwise do any final processing on the message + * prior to sending it. + */ + protected function escapeMessage (msg :String) :String + { + return msg; + } +} +} diff --git a/src/as/com/threerings/crowd/chat/client/ThinkHandler.as b/src/as/com/threerings/crowd/chat/client/ThinkHandler.as index 181103744..21fdfd826 100644 --- a/src/as/com/threerings/crowd/chat/client/ThinkHandler.as +++ b/src/as/com/threerings/crowd/chat/client/ThinkHandler.as @@ -2,27 +2,22 @@ package com.threerings.crowd.chat.client { import com.threerings.util.StringUtil; +import com.threerings.crowd.util.CrowdContext; + import com.threerings.crowd.chat.data.ChatCodes; public class ThinkHandler extends CommandHandler { - public function ThinkHandler (chatdir :ChatDirector) - { - _chatdir = chatdir; - } - public override function handleCommand ( - speakSvc :SpeakService, cmd :String, args :String, history :Array) - :String + ctx :CrowdContext, speakSvc :SpeakService, + cmd :String, args :String, history :Array) :String { if (StringUtil.isBlank(args)) { return "m.usage_think"; } history[0] = cmd + " "; - return _chatdir.deliverChat(speakSvc, args, ChatCodes.THINK_MODE); + return ctx.getChatDirector().deliverChat( + speakSvc, args, ChatCodes.THINK_MODE); } - - /** Our ChatDirector. */ - protected var _chatdir :ChatDirector; } }