X-Git-Url: https://mudpy.org/gitweb?p=mudpy.git;a=blobdiff_plain;f=mudpy%2Fmisc.py;h=f99fac357cc79cbf196fa341581d4edd19401290;hp=930d4d7a72bc8ce18e001e78e6b4b61b04f80769;hb=f38d6c0f396d2ff3da597b0fbac2cee2891df934;hpb=e25d11c16634028eaf5a498adf0aacc0ad9ffda0 diff --git a/mudpy/misc.py b/mudpy/misc.py index 930d4d7..f99fac3 100644 --- a/mudpy/misc.py +++ b/mudpy/misc.py @@ -1,10 +1,11 @@ """Miscellaneous functions for the mudpy engine.""" -# Copyright (c) 2004-2018 mudpy authors. Permission to use, copy, +# Copyright (c) 2004-2019 mudpy authors. Permission to use, copy, # modify, and distribute this software is granted under terms # provided in the LICENSE file distributed with this software. import codecs +import datetime import os import random import re @@ -636,7 +637,7 @@ class User: def authenticate(self): """Flag the user as authenticated and disconnect duplicates.""" - if self.state is not "authenticated": + if self.state != "authenticated": self.authenticated = True if ("mudpy.limit" in universe.contents and self.account.subkey in universe.contents["mudpy.limit"].get("admins")): @@ -660,6 +661,30 @@ class User: self.error = False self.adjust_echoing() + def prompt(self): + """"Generate and return an input prompt.""" + + # Start with the user's preference, if one was provided + prompt = self.account.get("prompt") + + # If the user has not set a prompt, then immediately return the default + # provided for the current state + if not prompt: + return get_menu_prompt(self.state) + + # Allow including the World clock state + if "$_(time)" in prompt: + prompt = prompt.replace( + "$_(time)", + str(universe.groups["internal"]["counters"].get("elapsed"))) + + # Append a single space for clear separation from user input + if prompt[-1] != " ": + prompt = "%s " % prompt + + # Return the cooked prompt + return prompt + def adjust_echoing(self): """Adjust echoing to match state menu requirements.""" if mudpy.telnet.is_enabled(self, mudpy.telnet.TELOPT_ECHO, @@ -722,7 +747,7 @@ class User: if not just_prompt: output += "$(eol)" if add_prompt: - output += "> " + output += self.prompt() mode = self.avatar.get("mode") if mode: output += "(" + mode + ") " @@ -986,7 +1011,7 @@ def log(message, level=0): file_name = "" max_log_lines = 0 syslog_name = "" - timestamp = time.asctime()[4:19] + timestamp = datetime.datetime.now().isoformat(' ') # turn the message into a list of nonempty lines lines = [x for x in [(x.rstrip()) for x in message.split("\n")] if x != ""] @@ -1191,7 +1216,7 @@ def weighted_choice(data): # create the expanded list of keys for key in data.keys(): - for count in range(data[key]): + for _count in range(data[key]): expanded.append(key) # return one at random @@ -1242,7 +1267,7 @@ def random_name(): name = "" # create a name of random length from the syllables - for syllable in range(random.randrange(2, 6)): + for _syllable in range(random.randrange(2, 6)): name += weighted_choice(syllables) # strip any leading quotemark, capitalize and return the name @@ -1449,6 +1474,27 @@ def check_for_connection(listening_socket): return user +def find_command(command_name): + """Try to find a command by name or abbreviation.""" + + # lowercase the command + command_name = command_name.lower() + + command = None + if command_name in universe.groups["command"]: + # the command matches a command word for which we have data + command = universe.groups["command"][command_name] + else: + for candidate in sorted(universe.groups["command"]): + if candidate.startswith(command_name) and not universe.groups[ + "command"][candidate].get("administrative"): + # the command matches the start of a command word and is not + # restricted to administrators + command = universe.groups["command"][candidate] + break + return command + + def get_menu(state, error=None, choices=None): """Show the correct menu text to a user.""" @@ -1839,21 +1885,35 @@ def handler_active(user): else: command_name, parameters = first_word(input_data) - # lowercase the command - command_name = command_name.lower() - - # the command matches a command word for which we have data - if command_name in universe.groups["command"]: - command = universe.groups["command"][command_name] - else: - command = None + # expand to an actual command + command = find_command(command_name) # if it's allowed, do it + ran = False if actor.can_run(command): - exec(command.get("action")) - - # otherwise, give an error - elif command_name: + # dereference the relative object path for the requested function + action = mudpy + action_fname = command.get("action", command.key) + for component in action_fname.split("."): + try: + action = getattr(action, component) + ran = True + except AttributeError: + log('Could not find action function "%s" for command "%s"' + % (action_fname, command_name)) + action = None + break + if action: + try: + action(actor, parameters) + except Exception: + log('Command string "%s" from user %s raised an ' + 'exception...\n%s' % ( + input_data, actor.owner.account.get("name"), + traceback.format_exc())) + + # if the command was not run, give an error + if not ran: mudpy.command.error(actor, input_data) # if no input, just idle back with a prompt