# Source code for evennia.commands.default.general

    """
    General Character commands usually available to all characters
    """
    import re

    from django.conf import settings
    from evennia.typeclasses.attributes import NickTemplateInvalid
    from evennia.utils import utils

    COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)

    # limit symbol import for API
    __all__ = (
        "CmdHome",
        "CmdLook",
        "CmdNick",
        "CmdInventory",
        "CmdSetDesc",
        "CmdGet",
        "CmdDrop",
        "CmdGive",
        "CmdSay",
        "CmdWhisper",
        "CmdPose",
        "CmdAccess",
    )

    class CmdHome(COMMAND_DEFAULT_CLASS):
        """
        move to your character's home location

        Usage:
          home

        Teleports you to your home location.
        """

        key = "home"
        locks = "cmd:perm(home) or perm(Builder)"
        arg_regex = r"$"

        def func(self):
            """Implement the command"""
            caller = self.caller
            home = caller.home
            if not home:
                caller.msg("You have no home!")
            elif home == caller.location:
                caller.msg("You are already home!")
            else:
                caller.msg("There's no place like home ...")
                caller.move_to(home, move_type="teleport")

    class CmdLook(COMMAND_DEFAULT_CLASS):
        """
        look at location or object

        Usage:
          look
          look <obj>
          look *<account>

        Observes your location or objects in your vicinity.
        """

        key = "look"
        aliases = ["l", "ls"]
        locks = "cmd:all()"
        arg_regex = r"\s|$"

        def func(self):
            """
            Handle the looking.
            """
            caller = self.caller
            if not self.args:
                target = caller.location
                if not target:
                    caller.msg("You have no location to look at!")
                    return
            else:
                target = caller.search(self.args)
                if not target:
                    return
            desc = caller.at_look(target)
            # add the type=look to the outputfunc to make it
            # easy to separate this output in client.
            self.msg(text=(desc, {"type": "look"}), options=None)

    class CmdNick(COMMAND_DEFAULT_CLASS):
        """
        define a personal alias/nick by defining a string to
        match and replace it with another on the fly

        Usage:
          nick[/switches] <string> [= [replacement_string]]
          nick[/switches] <template> = <replacement_template>
          nick/delete <string> or number
          nicks

        Switches:
          inputline - replace on the inputline (default)
          object    - replace on object-lookup
          account   - replace on account-lookup
          list      - show all defined aliases (also "nicks" works)
          delete    - remove nick by index in /list
          clearall  - clear all nicks

        Examples:
          nick hi = say Hello, I'm Sarah!
          nick/object tom = the tall man
          nick build $1 $2 = create/drop $1;$2
          nick tell $1 $2=page $1=$2
          nick tm?$1=page tallman=$1
          nick tm\=$1=page tallman=$1

        A 'nick' is a personal string replacement. Use $1, $2, ... to catch arguments.
        Put the last $-marker without an ending space to catch all remaining text. You
        can also use unix-glob matching for the left-hand side <string>:

            * - matches everything
            ? - matches 0 or 1 single characters
            [abcd] - matches these chars in any order
            [!abcd] - matches everything not among these chars
            \= - escape literal '=' you want in your <string>

        Note that no objects are actually renamed or changed by this command - your nicks
        are only available to you. If you want to permanently add keywords to an object
        for everyone to use, you need build privileges and the alias command.

        """

        key = "nick"
        switch_options = ("inputline", "object", "account", "list", "delete", "clearall")
        aliases = ["nickname", "nicks"]
        locks = "cmd:all()"

        def parse(self):
            """
            Support escaping of = with \=
            """
            super().parse()
            args = (self.lhs or "") + (" = %s" % self.rhs if self.rhs else "")
            parts = re.split(r"(?<!\\)=", args, 1)
            self.rhs = None
            if len(parts) < 2:
                self.lhs = parts[0].strip()
            else:
                self.lhs, self.rhs = [part.strip() for part in parts]
            self.lhs = self.lhs.replace("\=", "=")

        def func(self):
            """Create the nickname"""

            def _cy(string):
                "add color to the special markers"
                return re.sub(r"(\$[0-9]+|\*|\?|\[.+?\])", r"|Y\1|n", string)

            caller = self.caller
            switches = self.switches
            nicktypes = [switch for switch in switches if switch in ("object", "account", "inputline")]
            specified_nicktype = bool(nicktypes)
            nicktypes = nicktypes if specified_nicktype else ["inputline"]

            nicklist = (
                utils.make_iter(caller.nicks.get(category="inputline", return_obj=True) or [])
                + utils.make_iter(caller.nicks.get(category="object", return_obj=True) or [])
                + utils.make_iter(caller.nicks.get(category="account", return_obj=True) or [])
            )

            if "list" in switches or self.cmdstring in ("nicks",):

                if not nicklist:
                    string = "|wNo nicks defined.|n"
                else:
                    table = self.styled_table("#", "Type", "Nick match", "Replacement")
                    for inum, nickobj in enumerate(nicklist):
                        _, _, nickvalue, replacement = nickobj.value
                        table.add_row(
                            str(inum + 1), nickobj.db_category, _cy(nickvalue), _cy(replacement)
                        )
                    string = "|wDefined Nicks:|n\n%s" % table
                caller.msg(string)
                return

            if "clearall" in switches:
                caller.nicks.clear()
                caller.account.nicks.clear()
                caller.msg("Cleared all nicks.")
                return

            if "delete" in switches or "del" in switches:
                if not self.args or not self.lhs:
                    caller.msg("usage nick/delete <nick> or <#num> ('nicks' for list)")
                    return
                # see if a number was given
                arg = self.args.lstrip("#")
                oldnicks = []
                if arg.isdigit():
                    # we are given a index in nicklist
                    delindex = int(arg)
                    if 0 < delindex <= len(nicklist):
                        oldnicks.append(nicklist[delindex - 1])
                    else:
                        caller.msg("Not a valid nick index. See 'nicks' for a list.")
                        return
                else:
                    if not specified_nicktype:
                        nicktypes = ("object", "account", "inputline")
                    for nicktype in nicktypes:
                        oldnicks.append(caller.nicks.get(arg, category=nicktype, return_obj=True))

                oldnicks = [oldnick for oldnick in oldnicks if oldnick]
                if oldnicks:
                    for oldnick in oldnicks:
                        nicktype = oldnick.category
                        nicktypestr = "%s-nick" % nicktype.capitalize()
                        _, _, old_nickstring, old_replstring = oldnick.value
                        caller.nicks.remove(old_nickstring, category=nicktype)
                        caller.msg(
                            "%s removed: '|w%s|n' -> |w%s|n."
                            % (nicktypestr, old_nickstring, old_replstring)
                        )
                else:
                    caller.msg("No matching nicks to remove.")
                return

            if not self.rhs and self.lhs:
                # check what a nick is set to
                strings = []
                if not specified_nicktype:
                    nicktypes = ("object", "account", "inputline")
                for nicktype in nicktypes:
                    nicks = [
                        nick
                        for nick in utils.make_iter(
                            caller.nicks.get(category=nicktype, return_obj=True)
                        )
                        if nick
                    ]
                    for nick in nicks:
                        _, _, nick, repl = nick.value
                        if nick.startswith(self.lhs):
                            strings.append(
                                "{}-nick: '{}' -> '{}'".format(nicktype.capitalize(), nick, repl)
                            )
                if strings:
                    caller.msg("\n".join(strings))
                else:
                    caller.msg("No nicks found matching '{}'".format(self.lhs))
                return

            if not self.rhs and self.lhs:
                # check what a nick is set to
                strings = []
                if not specified_nicktype:
                    nicktypes = ("object", "account", "inputline")
                for nicktype in nicktypes:
                    if nicktype == "account":
                        obj = account
                    else:
                        obj = caller
                    nicks = utils.make_iter(obj.nicks.get(category=nicktype, return_obj=True))
                    for nick in nicks:
                        _, _, nick, repl = nick.value
                        if nick.startswith(self.lhs):
                            strings.append(
                                "{}-nick: '{}' -> '{}'".format(nicktype.capitalize(), nick, repl)
                            )
                if strings:
                    caller.msg("\n".join(strings))
                else:
                    caller.msg("No nicks found matching '{}'".format(self.lhs))
                return

            if not self.rhs and self.lhs:
                # check what a nick is set to
                strings = []
                if not specified_nicktype:
                    nicktypes = ("object", "account", "inputline")
                for nicktype in nicktypes:
                    if nicktype == "account":
                        obj = account
                    else:
                        obj = caller
                    nicks = utils.make_iter(obj.nicks.get(category=nicktype, return_obj=True))
                    for nick in nicks:
                        _, _, nick, repl = nick.value
                        if nick.startswith(self.lhs):
                            strings.append(
                                "{}-nick: '{}' -> '{}'".format(nicktype.capitalize(), nick, repl)
                            )
                if strings:
                    caller.msg("\n".join(strings))
                else:
                    caller.msg("No nicks found matching '{}'".format(self.lhs))
                return

            if not self.args or not self.lhs:
                caller.msg("Usage: nick[/switches] nickname = [realname]")
                return

            # setting new nicks

            nickstring = self.lhs
            replstring = self.rhs

            if replstring == nickstring:
                caller.msg("No point in setting nick same as the string to replace...")
                return

            # check so we have a suitable nick type
            errstring = ""
            string = ""
            for nicktype in nicktypes:
                nicktypestr = "%s-nick" % nicktype.capitalize()
                old_nickstring = None
                old_replstring = None

                oldnick = caller.nicks.get(key=nickstring, category=nicktype, return_obj=True)
                if oldnick:
                    _, _, old_nickstring, old_replstring = oldnick.value
                if replstring:
                    # creating new nick
                    errstring = ""
                    if oldnick:
                        if replstring == old_replstring:
                            string += "\nIdentical %s already set." % nicktypestr.lower()
                        else:
                            string += "\n%s '|w%s|n' updated to map to '|w%s|n'." % (
                                nicktypestr,
                                old_nickstring,
                                replstring,
                            )
                    else:
                        string += "\n%s '|w%s|n' mapped to '|w%s|n'." % (
                            nicktypestr,
                            nickstring,
                            replstring,
                        )
                    try:
                        caller.nicks.add(nickstring, replstring, category=nicktype)
                    except NickTemplateInvalid:
                        caller.msg(
                            "You must use the same $-markers both in the nick and in the replacement."
                        )
                        return
                elif old_nickstring and old_replstring:
                    # just looking at the nick
                    string += "\n%s '|w%s|n' maps to '|w%s|n'." % (
                        nicktypestr,
                        old_nickstring,
                        old_replstring,
                    )
                    errstring = ""
            string = errstring if errstring else string
            caller.msg(_cy(string))

    class CmdInventory(COMMAND_DEFAULT_CLASS):
        """
        view inventory

        Usage:
          inventory
          inv

        Shows your inventory.
        """

        key = "inventory"
        aliases = ["inv", "i"]
        locks = "cmd:all()"
        arg_regex = r"$"

        def func(self):
            """check inventory"""
            items = self.caller.contents
            if not items:
                string = "You are not carrying anything."
            else:
                from evennia.utils.ansi import raw as raw_ansi

                table = self.styled_table(border="header")
                for item in items:
                    table.add_row(
                        f"|C{item.name}|n",
                        "{}|n".format(utils.crop(raw_ansi(item.db.desc or ""), width=50) or ""),
                    )
                string = f"|wYou are carrying:\n{table}"
            self.caller.msg(text=(string, {"type": "inventory"}))

    class CmdGet(COMMAND_DEFAULT_CLASS):
        """
        pick up something

        Usage:
          get <obj>

        Picks up an object from your location and puts it in
        your inventory.
        """

        key = "get"
        aliases = "grab"
        locks = "cmd:all();view:perm(Developer);read:perm(Developer)"
        arg_regex = r"\s|$"

        def func(self):
            """implements the command."""

            caller = self.caller

            if not self.args:
                caller.msg("Get what?")
                return
            obj = caller.search(self.args, location=caller.location)
            if not obj:
                return
            if caller == obj:
                caller.msg("You can't get yourself.")
                return
            if not obj.access(caller, "get"):
                if obj.db.get_err_msg:
                    caller.msg(obj.db.get_err_msg)
                else:
                    caller.msg("You can't get that.")
                return

            # calling at_pre_get hook method
            if not obj.at_pre_get(caller):
                return

            success = obj.move_to(caller, quiet=True, move_type="get")
            if not success:
                caller.msg("This can't be picked up.")
            else:
                caller.msg("You pick up %s." % obj.name)
                caller.location.msg_contents(
                    "%s picks up %s." % (caller.name, obj.name), exclude=caller
                )
                # calling at_get hook method
                obj.at_get(caller)

    class CmdDrop(COMMAND_DEFAULT_CLASS):
        """
        drop something

        Usage:
          drop <obj>

        Lets you drop an object from your inventory into the
        location you are currently in.
        """

        key = "drop"
        locks = "cmd:all()"
        arg_regex = r"\s|$"

        def func(self):
            """Implement command"""

            caller = self.caller
            if not self.args:
                caller.msg("Drop what?")
                return

            # Because the DROP command by definition looks for items
            # in inventory, call the search function using location = caller
            obj = caller.search(
                self.args,
                location=caller,
                nofound_string="You aren't carrying %s." % self.args,
                multimatch_string="You carry more than one %s:" % self.args,
            )
            if not obj:
                return

            # Call the object script's at_pre_drop() method.
            if not obj.at_pre_drop(caller):
                return

            success = obj.move_to(caller.location, quiet=True, move_type="drop")
            if not success:
                caller.msg("This couldn't be dropped.")
            else:
                caller.msg("You drop %s." % (obj.name,))
                caller.location.msg_contents("%s drops %s." % (caller.name, obj.name), exclude=caller)
                # Call the object script's at_drop() method.
                obj.at_drop(caller)

    class CmdGive(COMMAND_DEFAULT_CLASS):
        """
        give away something to someone

        Usage:
          give <inventory obj> <to||=> <target>

        Gives an item from your inventory to another person,
        placing it in their inventory.
        """

        key = "give"
        rhs_split = ("=", " to ")  # Prefer = delimiter, but allow " to " usage.
        locks = "cmd:all()"
        arg_regex = r"\s|$"

        def func(self):
            """Implement give"""

            caller = self.caller
            if not self.args or not self.rhs:
                caller.msg("Usage: give <inventory object> = <target>")
                return
            to_give = caller.search(
                self.lhs,
                location=caller,
                nofound_string="You aren't carrying %s." % self.lhs,
                multimatch_string="You carry more than one %s:" % self.lhs,
            )
            target = caller.search(self.rhs)
            if not (to_give and target):
                return
            if target == caller:
                caller.msg("You keep %s to yourself." % to_give.key)
                return
            if not to_give.location == caller:
                caller.msg("You are not holding %s." % to_give.key)
                return

            # calling at_pre_give hook method
            if not to_give.at_pre_give(caller, target):
                return

            # give object
            success = to_give.move_to(target, quiet=True, move_type="give")
            if not success:
                caller.msg("This could not be given.")
            else:
                caller.msg("You give %s to %s." % (to_give.key, target.key))
                target.msg("%s gives you %s." % (caller.key, to_give.key))
                # Call the object script's at_give() method.
                to_give.at_give(caller, target)

    class CmdSetDesc(COMMAND_DEFAULT_CLASS):
        """
        describe yourself

        Usage:
          setdesc <description>

        Add a description to yourself. This
        will be visible to people when they
        look at you.
        """

        key = "setdesc"
        locks = "cmd:all()"
        arg_regex = r"\s|$"

        def func(self):
            """add the description"""

            if not self.args:
                self.caller.msg("You must add a description.")
                return

            self.caller.db.desc = self.args.strip()
            self.caller.msg("You set your description.")

    class CmdSay(COMMAND_DEFAULT_CLASS):
        """
        speak as your character

        Usage:
          say <message>

        Talk to those in your current location.
        """

        key = "say"
        aliases = ['"', "'"]
        locks = "cmd:all()"

        # don't require a space after `say/'/"`
        arg_regex = None

        def func(self):
            """Run the say command"""

            caller = self.caller

            if not self.args:
                caller.msg("Say what?")
                return

            speech = self.args

            # Calling the at_pre_say hook on the character
            speech = caller.at_pre_say(speech)

            # If speech is empty, stop here
            if not speech:
                return

            # Call the at_post_say hook on the character
            caller.at_say(speech, msg_self=True)

    class CmdWhisper(COMMAND_DEFAULT_CLASS):
        """
        Speak privately as your character to another

        Usage:
          whisper <character> = <message>
          whisper <char1>, <char2> = <message>

        Talk privately to one or more characters in your current location, without
        others in the room being informed.
        """

        key = "whisper"
        locks = "cmd:all()"

        def func(self):
            """Run the whisper command"""

            caller = self.caller

            if not self.lhs or not self.rhs:
                caller.msg("Usage: whisper <character> = <message>")
                return

            receivers = [recv.strip() for recv in self.lhs.split(",")]

            receivers = [caller.search(receiver) for receiver in set(receivers)]
            receivers = [recv for recv in receivers if recv]

            speech = self.rhs
            # If the speech is empty, abort the command
            if not speech or not receivers:
                return

            # Call a hook to change the speech before whispering
            speech = caller.at_pre_say(speech, whisper=True, receivers=receivers)

            # no need for self-message if we are whispering to ourselves (for some reason)
            msg_self = None if caller in receivers else True
            caller.at_say(speech, msg_self=msg_self, receivers=receivers, whisper=True)

    class CmdPose(COMMAND_DEFAULT_CLASS):
        """
        strike a pose

        Usage:
          pose <pose text>
          pose's <pose text>

        Example:
          pose is standing by the wall, smiling.
           -> others will see:
          Tom is standing by the wall, smiling.

        Describe an action being taken. The pose text will
        automatically begin with your name.
        """

        key = "pose"
        aliases = [":", "emote"]
        locks = "cmd:all()"
        arg_regex = ""

        # we want to be able to pose without whitespace between
        # the command/alias and the pose (e.g. :pose)
        arg_regex = None

        def parse(self):
            """
            Custom parse the cases where the emote
            starts with some special letter, such
            as 's, at which we don't want to separate
            the caller's name and the emote with a
            space.
            """
            args = self.args
            if args and not args[0] in ["'", ",", ":"]:
                args = " %s" % args.strip()
            self.args = args

        def func(self):
            """Hook function"""
            if not self.args:
                msg = "What do you want to do?"
                self.caller.msg(msg)
            else:
                msg = "%s%s" % (self.caller.name, self.args)
                self.caller.location.msg_contents(text=(msg, {"type": "pose"}), from_obj=self.caller)

    class CmdAccess(COMMAND_DEFAULT_CLASS):
        """
        show your current game access

        Usage:
          access

        This command shows you the permission hierarchy and
        which permission groups you are a member of.
        """

        key = "access"
        aliases = ["groups", "hierarchy"]
        locks = "cmd:all()"
        arg_regex = r"$"

        def func(self):
            """Load the permission groups"""

            caller = self.caller
            hierarchy_full = settings.PERMISSION_HIERARCHY
            string = "\n|wPermission Hierarchy|n (climbing):\n %s" % ", ".join(hierarchy_full)

            if self.caller.account.is_superuser:
                cperms = "<Superuser>"
                pperms = "<Superuser>"
            else:
                cperms = ", ".join(caller.permissions.all())
                pperms = ", ".join(caller.account.permissions.all())

            string += "\n|wYour access|n:"
            string += "\nCharacter |c%s|n: %s" % (caller.key, cperms)
            if hasattr(caller, "account"):
                string += "\nAccount |c%s|n: %s" % (caller.account.key, pperms)
            caller.msg(string)