def func(self): ch = self.caller ch.msg(self.args) args = self.args.strip() objdb = dict(GLOBAL_SCRIPTS.objdb.vnum) if not objdb: ch.msg("There are no objects within the game") return if not args: table = self.styled_table("VNum", "Description", "Type", border='incols') objs = search_objdb('all') for vnum, obj in objs.items(): data = objdb[vnum] vnum = raw_ansi(f"[|G{vnum:<4}|n]") sdesc = crop(raw_ansi(data['sdesc']), width=50) or '' table.add_row(vnum, sdesc, f"{data['type']}") msg = str(table) ch.msg(msg) return args = args.split(' ') if len(args) < 2: ch.msg("Supply either type or name to search for") return table = self.styled_table("VNum", "Description", "Type", border='incols') type_ = args[0] if type_ not in ('type', 'name'): ch.msg("Please supply either (type or name) to searchby") return criteria = args[1] try: extra_field, extra_criteria = args[2], args[3] except IndexError: extra_field = extra_criteria = None if extra_field and extra_criteria: objs = search_objdb(criteria, **{extra_field: extra_criteria}) else: objs = search_objdb(criteria) for vnum, obj in objs.items(): vnum = raw_ansi(f"[|G{vnum:<4}|n]") sdesc = crop(raw_ansi(obj['sdesc']), width=50) or '' table.add_row(vnum, sdesc, f"{obj['type']}") msg = str(table) ch.msg(msg) return
def func(self): """check inventory""" items = self.caller.contents if not items: string = "Non stai trasportando nulla." 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), width=50) or ""), ) string = f"|wStai trasportando:\n{table}" self.caller.msg(string)
def test_crop(self): # No text, return no text self.assertEqual("", utils.crop("", width=10, suffix="[...]")) # Input length equal to max width, no crop self.assertEqual("0123456789", utils.crop("0123456789", width=10, suffix="[...]")) # Input length greater than max width, crop (suffix included in width) self.assertEqual("0123[...]", utils.crop("0123456789", width=9, suffix="[...]")) # Input length less than desired width, no crop self.assertEqual("0123", utils.crop("0123", width=9, suffix="[...]")) # Width too small or equal to width of suffix self.assertEqual("012", utils.crop("0123", width=3, suffix="[...]")) self.assertEqual("01234", utils.crop("0123456", width=5, suffix="[...]"))
def func(self): "Define extended command" caller = self.caller location = caller.location if self.cmdstring == '@detail': # switch to detailing mode. This operates only on current location if not location: caller.msg("No location to detail!") return if location.db.details is None: caller.msg("|rThis location does not support details.|n") return if self.switches and self.switches[0] in 'del': # removing a detail. if self.lhs in location.db.details: del location.db.details[self.lhs] caller.msg("Detail %s deleted, if it existed." % self.lhs) self.reset_times(location) return if not self.args: # No args given. Return all details on location string = "|wDetails on %s|n:" % location details = "\n".join( " |w%s|n: %s" % (key, utils.crop(text)) for key, text in location.db.details.items()) caller.msg("%s\n%s" % (string, details) if details else "%s None." % string) return if not self.rhs: # no '=' used - list content of given detail if self.args in location.db.details: string = "{wDetail '%s' on %s:\n{n" % (self.args, location) string += str(location.db.details[self.args]) caller.msg(string) else: caller.msg("Detail '%s' not found." % self.args) return # setting a detail location.db.details[self.lhs] = self.rhs caller.msg("Set Detail %s to '%s'." % (self.lhs, self.rhs)) self.reset_times(location) return else: # we are doing a @desc call if not self.args: if location: string = "{wDescriptions on %s{n:\n" % location.key string += " {wspring:{n %s\n" % location.db.spring_desc string += " {wsummer:{n %s\n" % location.db.summer_desc string += " {wautumn:{n %s\n" % location.db.autumn_desc string += " {wwinter:{n %s\n" % location.db.winter_desc string += " {wgeneral:{n %s" % location.db.general_desc caller.msg(string) return if self.switches and self.switches[0] in ("spring", "summer", "autumn", "winter"): # a seasonal switch was given if self.rhs: caller.msg( "Seasonal descs only works with rooms, not objects.") return switch = self.switches[0] if not location: caller.msg("No location was found!") return if switch == 'spring': location.db.spring_desc = self.args elif switch == 'summer': location.db.summer_desc = self.args elif switch == 'autumn': location.db.autumn_desc = self.args elif switch == 'winter': location.db.winter_desc = self.args # clear flag to force an update self.reset_times(location) caller.msg("Seasonal description was set on %s." % location.key) else: # No seasonal desc set, maybe this is not an extended room if self.rhs: text = self.rhs obj = caller.search(self.lhs) if not obj: return else: text = self.args obj = location obj.db.desc = text # a compatibility fallback if obj.attributes.has("general_desc"): obj.db.general_desc = text self.reset_times(obj) caller.msg("General description was set on %s." % obj.key) else: # this is not an ExtendedRoom. caller.msg("The description was set on %s." % obj.key)
def func(self): """Define extended command""" caller = self.caller location = caller.location if self.cmdstring == "@detail": # switch to detailing mode. This operates only on current location if not location: caller.msg("No location to detail!") return if not location.access(caller, "edit"): caller.msg("You do not have permission to use @desc here.") return if not self.args: # No args given. Return all details on location string = "{wDetails on %s{n:\n" % location string += "\n".join( " {w%s{n: %s" % (key, utils.crop(text)) for key, text in location.db.details.items()) caller.msg(string) return if self.switches and self.switches[0] in "del": # removing a detail. if self.lhs in location.db.details: del location.db.details[self.lhs] caller.msg("Detail %s deleted, if it existed." % self.lhs) self.reset_times(location) return if self.switches and self.switches[0] in "fix": if not self.lhs or not self.rhs: caller.msg("Syntax: @detail/fix key=old,new") fixlist = self.rhs.split(",") if len(fixlist) != 2: caller.msg("Syntax: @detail/fix key=old,new") return key = self.lhs try: location.db.details[key] = location.db.details[ key].replace(fixlist[0], fixlist[1]) except (KeyError, AttributeError): caller.msg("No such detail found.") return caller.msg("Detail %s has had text changed to: %s" % (key, location.db.details[key])) return if not self.rhs: # no '=' used - list content of given detail if self.args in location.db.details: string = "{wDetail '%s' on %s:\n{n" % (self.args, location) string += location.db.details[self.args] caller.msg(string) return # setting a detail location.db.details[self.lhs] = self.rhs caller.msg("{wSet Detail %s to {n'%s'." % (self.lhs, self.rhs)) self.reset_times(location) return else: # we are doing a @desc call if not self.args: if location: string = "{wDescriptions on %s{n:\n" % location.key string += " {wspring:{n %s\n" % location.db.spring_desc string += " {wsummer:{n %s\n" % location.db.summer_desc string += " {wautumn:{n %s\n" % location.db.autumn_desc string += " {wwinter:{n %s\n" % location.db.winter_desc string += " {wgeneral:{n %s" % location.db.general_desc caller.msg(string) return if self.switches and self.switches[0] in ( "spring", "summer", "autumn", "winter", ): # a seasonal switch was given if self.rhs: caller.msg( "Seasonal descs only works with rooms, not objects.") return switch = self.switches[0] if not location: caller.msg("No location was found!") return if not location.access(caller, "edit"): caller.msg("You do not have permission to @desc here.") return if switch == "spring": location.db.spring_desc = self.args elif switch == "summer": location.db.summer_desc = self.args elif switch == "autumn": location.db.autumn_desc = self.args elif switch == "winter": location.db.winter_desc = self.args # clear flag to force an update self.reset_times(location) caller.msg("Seasonal description was set on %s." % location.key) else: # Not seasonal desc set, maybe this is not an extended room if self.rhs: text = self.rhs if "char" in self.switches: # if we're looking for a character, find them by player # so we can @desc someone not in the room caller = caller.player obj = caller.search(self.lhs) # if we did a search as a player, get the character object if obj and obj.char_ob: obj = obj.char_ob if not obj: return else: caller.msg( "You must have both an object to describe and the description." ) caller.msg("Format: @desc <object>=<description>") return if not obj.access(caller, "edit"): caller.msg( "You do not have permission to change the @desc of %s." % obj.name) return obj.desc = self.rhs # a compatability fallback if utils.inherits_from(obj, ExtendedRoom): # this is an extendedroom, we need to reset # times and set general_desc obj.db.general_desc = text self.reset_times(obj) caller.msg("General description was set on %s." % obj.key) else: caller.msg("The description was set on %s." % obj.key)
def func(self): "Define extended command" caller = self.caller location = caller.location if self.cmdstring == '@detail': # switch to detailing mode. This operates only on current location if not location: caller.msg("No location to detail!") return if self.switches and self.switches[0] in 'del': # removing a detail. if self.lhs in location.db.details: del location.db.details[self.lhs] caller.msg("Detail %s deleted, if it existed." % self.lhs) self.reset_times(location) return if not self.args: # No args given. Return all details on location string = "{wDetails on %s{n:\n" % location string += "\n".join(" {w%s{n: %s" % (key, utils.crop(text)) for key, text in location.db.details.items()) caller.msg(string) return if not self.rhs: # no '=' used - list content of given detail if self.args in location.db.details: string = "{wDetail '%s' on %s:\n{n" % (self.args, location) string += str(location.db.details[self.args]) caller.msg(string) else: caller.msg("Detail '%s' not found." % self.args) return # setting a detail location.db.details[self.lhs] = self.rhs caller.msg("Set Detail %s to '%s'." % (self.lhs, self.rhs)) self.reset_times(location) return else: # we are doing a @desc call if not self.args: if location: string = "{wDescriptions on %s{n:\n" % location.key string += " {wspring:{n %s\n" % location.db.spring_desc string += " {wsummer:{n %s\n" % location.db.summer_desc string += " {wautumn:{n %s\n" % location.db.autumn_desc string += " {wwinter:{n %s\n" % location.db.winter_desc string += " {wgeneral:{n %s" % location.db.general_desc caller.msg(string) return if self.switches and self.switches[0] in ("spring", "summer", "autumn", "winter"): # a seasonal switch was given if self.rhs: caller.msg("Seasonal descs only works with rooms, not objects.") return switch = self.switches[0] if not location: caller.msg("No location was found!") return if switch == 'spring': location.db.spring_desc = self.args elif switch == 'summer': location.db.summer_desc = self.args elif switch == 'autumn': location.db.autumn_desc = self.args elif switch == 'winter': location.db.winter_desc = self.args # clear flag to force an update self.reset_times(location) caller.msg("Seasonal description was set on %s." % location.key) else: # No seasonal desc set, maybe this is not an extended room if self.rhs: text = self.rhs obj = caller.search(self.lhs) if not obj: return else: text = self.args obj = location obj.db.desc = text # a compatibility fallback if obj.attributes.has("general_desc"): obj.db.general_desc = text caller.msg("General description was set on %s." % obj.key) else: # this is not an ExtendedRoom. caller.msg("The description was set on %s." % obj.key)
def func(self): "Define extended command" caller = self.caller location = caller.location if self.cmdstring == '@detail': # switch to detailing mode. This operates only on current location if not location: caller.msg("Невозможно задать этой локации деталь!") return if self.switches and self.switches[0] in 'del': # removing a detail. if self.lhs in location.db.details: del location.db.details[self.lhs] caller.msg("Деталь %s удалена, если она была." % self.lhs) self.reset_times(location) return if not self.args: # No args given. Return all details on location string = "{wДетали в %s{n:\n" % location string += "\n".join( " {w%s{n: %s" % (key, utils.crop(text)) for key, text in location.db.details.items()) caller.msg(string) return if not self.rhs: # no '=' used - list content of given detail if self.args in location.db.details: string = "{wДеталь '%s' в %s:\n{n" % (self.args, location) string += str(location.db.details[self.args]) caller.msg(string) else: caller.msg("Деталь '%s' не найдена." % self.args) return # setting a detail location.db.details[self.lhs] = self.rhs caller.msg("Здана деталь %s как '%s'." % (self.lhs, self.rhs)) self.reset_times(location) return else: # we are doing a @desc call if not self.args: if location: string = "{wОписания для %s{n:\n" % location.key string += " {wвесна:{n %s\n" % location.db.spring_desc string += " {wлето:{n %s\n" % location.db.summer_desc string += " {wосень:{n %s\n" % location.db.autumn_desc string += " {wзима:{n %s\n" % location.db.winter_desc string += " {wосновное:{n %s" % location.db.general_desc caller.msg(string) return if self.switches and self.switches[0] in ("spring", "summer", "autumn", "winter"): # a seasonal switch was given if self.rhs: caller.msg( "Сезонные описания работают только с комнатами, не с объектами." ) return switch = self.switches[0] if not location: caller.msg("Локация не найдена!") return if switch == 'spring': location.db.spring_desc = self.args elif switch == 'summer': location.db.summer_desc = self.args elif switch == 'autumn': location.db.autumn_desc = self.args elif switch == 'winter': location.db.winter_desc = self.args # clear flag to force an update self.reset_times(location) caller.msg("Сезонное описание было задано для %s." % location.key) else: # No seasonal desc set, maybe this is not an extended room if self.rhs: text = self.rhs obj = caller.search(self.lhs) if not obj: return else: text = self.args obj = location obj.db.desc = text # a compatibility fallback if obj.attributes.has("general_desc"): obj.db.general_desc = text caller.msg("Основное описаине было задано для %s." % obj.key) else: # this is not an ExtendedRoom. caller.msg("Описание было задано для %s." % obj.key)
def func(self): """Get all connected players by polling session.""" player = self.player session_list = SESSIONS.get_sessions() session_list = sorted(session_list, key=lambda o: o.player.key) show_session_data = player.check_permstring('Immortals') if 'f' in self.switches else False nplayers = (SESSIONS.player_count()) if 'f' in self.switches or 'full' in self.switches: if show_session_data: # privileged info - who/f by wizard or immortal table = prettytable.PrettyTable(["|wPlayer Name", "|wOn for", "|wIdle", "|wCharacter", "|wRoom", "|wCmds", "|wProtocol", "|wHost"]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time player = session.get_player() puppet = session.get_puppet() location = puppet.location.key if puppet else 'None' table.add_row([utils.crop(player.name, width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1), utils.crop(puppet.key if puppet else 'None', width=25), utils.crop(location, width=25), session.cmd_total, session.protocol_key, isinstance(session.address, tuple) and session.address[0] or session.address]) else: # unprivileged info - who/f by player table = prettytable.PrettyTable(["|wCharacter", "|wOn for", "|wIdle", "|wLocation"]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time character = session.get_puppet() location = character.location.key if character and character.location else 'None' table.add_row([utils.crop(character.key if character else '- Unknown -', width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1), utils.crop(location, width=25)]) else: if 's' in self.switches or 'species' in self.switches or self.cmdstring == 'ws': my_character = self.caller.get_puppet(self.session) if not (my_character and my_character.location): self.msg("You can't see anyone here.") return table = prettytable.PrettyTable(["|wCharacter", "|wOn for", "|wIdle", "|wSpecies"]) for session in session_list: character = session.get_puppet() # my_character = self.caller.get_puppet(self.session) if not session.logged_in or character.location != my_character.location: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time character = session.get_puppet() species = character.attributes.get('species', default='- None -') table.add_row([utils.crop(character.key if character else '- Unknown -', width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1), utils.crop(species, width=25)]) else: # unprivileged info - who table = prettytable.PrettyTable(["|wCharacter", "|wOn for", "|wIdle"]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time character = session.get_puppet() table.add_row([utils.crop(character.key if character else '- Unknown -', width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1)]) isone = nplayers == 1 string = "%s\n%s " % (table, 'A' if isone else nplayers) string += 'single' if isone else 'unique' plural = '' if isone else 's' string += " account%s logged in." % plural self.msg(string)
def func(self): ch = self.caller ch.msg(self.args) args = self.args.strip() roomdb = dict(GLOBAL_SCRIPTS.roomdb.vnum) if not roomdb: ch.msg("There are no rooms within the game") return vnum_roomdb = roomdb.keys() min_ = min(vnum_roomdb) max_ = max(vnum_roomdb) legend = ["VNum", "Name", "Exits", "Zone"] try: _ = roomdb[1] except KeyError: ch.msg("No rooms are saved to database, try creating one first") return if not args: table = self.styled_table(*legend, border='incols') ch_zone = has_zone(ch) if ch_zone: for vnum in range(min_, max_ + 1): data = roomdb[vnum] if match_string(ch_zone, make_iter(data['zone'])): exits = { k: v for k, v in data['exits'].items() if v > 0 } vnum = raw_ansi(f"[|G{vnum:<4}|n]") sdesc = crop(raw_ansi(data['name']), width=50) or '' table.add_row(vnum, sdesc, f"{exits}", data['zone']) else: for vnum in range(min_, max_ + 1): data = roomdb[vnum] exits = {k: v for k, v in data['exits'].items() if v > 0} vnum = raw_ansi(f"[|G{vnum:<4}|n]") sdesc = crop(raw_ansi(data['name']), width=50) or '' table.add_row(vnum, sdesc, f"{exits}", data['zone']) msg = str(table) ch.msg(msg) return args = args.split(' ') if len(args) < 2: ch.msg("Supply either type or name to search for") return table = self.styled_table(*legend, border='incols') type_ = args[0] if type_ not in ('zone', 'name'): ch.msg("Please supply either (type or name) to searchby") return criteria = args[1] for vnum in range(min_, max_ + 1): # for vnum, data in GLOBAL_SCRIPTS.objdb.vnum.items(): data = roomdb[vnum] if type_ == 'zone': if match_string(criteria, make_iter(data['zone'])): exits = {k: v for k, v in data['exits'].items() if v > 0} vnum = raw_ansi(f"[|G{vnum:<4}|n]") sdesc = crop(raw_ansi(data['name']), width=50) or '' table.add_row(vnum, sdesc, f"{exits}", data['zone']) continue if type_ == 'name': if match_string(criteria, data['name'].split()): exits = {k: v for k, v in data['exits'].items() if v > 0} vnum = raw_ansi(f"[|G{vnum:<4}|n]") sdesc = crop(raw_ansi(data['name']), width=50) or '' table.add_row(vnum, sdesc, f"{exits}", data['zone']) continue msg = str(table) ch.msg(msg) return
def func(self): """Get all connected players by polling session.""" player = self.player session_list = SESSIONS.get_sessions() session_list = sorted(session_list, key=lambda o: o.player.key) show_session_data = player.check_permstring( 'Immortals') if 'f' in self.switches else False nplayers = (SESSIONS.player_count()) if 'f' in self.switches or 'full' in self.switches: if show_session_data: # privileged info - who/f by wizard or immortal table = prettytable.PrettyTable([ "|wPlayer Name", "|wOn for", "|wIdle", "|wCharacter", "|wRoom", "|wCmds", "|wProtocol", "|wHost" ]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time player = session.get_player() puppet = session.get_puppet() location = puppet.location.key if puppet else 'None' table.add_row([ utils.crop(player.name, width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1), utils.crop(puppet.key if puppet else 'None', width=25), utils.crop(location, width=25), session.cmd_total, session.protocol_key, isinstance(session.address, tuple) and session.address[0] or session.address ]) else: # unprivileged info - who/f by player table = prettytable.PrettyTable( ["|wCharacter", "|wOn for", "|wIdle", "|wLocation"]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time character = session.get_puppet() location = character.location.key if character and character.location else 'None' table.add_row([ utils.crop( character.key if character else '- Unknown -', width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1), utils.crop(location, width=25) ]) else: if 's' in self.switches or 'species' in self.switches or self.cmdstring == 'ws': my_character = self.caller.get_puppet(self.session) if not (my_character and my_character.location): self.msg("You can't see anyone here.") return table = prettytable.PrettyTable( ["|wCharacter", "|wOn for", "|wIdle", "|wSpecies"]) for session in session_list: character = session.get_puppet() # my_character = self.caller.get_puppet(self.session) if not session.logged_in or character.location != my_character.location: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time character = session.get_puppet() species = character.attributes.get('species', default='- None -') table.add_row([ utils.crop( character.key if character else '- Unknown -', width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1), utils.crop(species, width=25) ]) else: # unprivileged info - who table = prettytable.PrettyTable( ["|wCharacter", "|wOn for", "|wIdle"]) for session in session_list: if not session.logged_in: continue delta_cmd = time.time() - session.cmd_last_visible delta_conn = time.time() - session.conn_time character = session.get_puppet() table.add_row([ utils.crop( character.key if character else '- Unknown -', width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1) ]) isone = nplayers == 1 string = "%s\n%s " % (table, 'A' if isone else nplayers) string += 'single' if isone else 'unique' plural = '' if isone else 's' string += " account%s logged in." % plural self.msg(string)
def func(self): "Define extended command" caller = self.caller location = caller.location if self.cmdstring == '@detail': # switch to detailing mode. This operates only on current location if not location: caller.msg("Невозможно задать этой локации деталь!") return if self.switches and self.switches[0] in 'del': # removing a detail. if self.lhs in location.db.details: del location.db.details[self.lhs] caller.msg("Деталь %s удалена, если она была." % self.lhs) self.reset_times(location) return if not self.args: # No args given. Return all details on location string = "{wДетали в %s{n:\n" % location string += "\n".join(" {w%s{n: %s" % (key, utils.crop(text)) for key, text in location.db.details.items()) caller.msg(string) return if not self.rhs: # no '=' used - list content of given detail if self.args in location.db.details: string = "{wДеталь '%s' в %s:\n{n" % (self.args, location) string += str(location.db.details[self.args]) caller.msg(string) else: caller.msg("Деталь '%s' не найдена." % self.args) return # setting a detail location.db.details[self.lhs] = self.rhs caller.msg("Здана деталь %s как '%s'." % (self.lhs, self.rhs)) self.reset_times(location) return else: # we are doing a @desc call if not self.args: if location: string = "{wОписания для %s{n:\n" % location.key string += " {wвесна:{n %s\n" % location.db.spring_desc string += " {wлето:{n %s\n" % location.db.summer_desc string += " {wосень:{n %s\n" % location.db.autumn_desc string += " {wзима:{n %s\n" % location.db.winter_desc string += " {wосновное:{n %s" % location.db.general_desc caller.msg(string) return if self.switches and self.switches[0] in ("spring", "summer", "autumn", "winter"): # a seasonal switch was given if self.rhs: caller.msg("Сезонные описания работают только с комнатами, не с объектами.") return switch = self.switches[0] if not location: caller.msg("Локация не найдена!") return if switch == 'spring': location.db.spring_desc = self.args elif switch == 'summer': location.db.summer_desc = self.args elif switch == 'autumn': location.db.autumn_desc = self.args elif switch == 'winter': location.db.winter_desc = self.args # clear flag to force an update self.reset_times(location) caller.msg("Сезонное описание было задано для %s." % location.key) else: # No seasonal desc set, maybe this is not an extended room if self.rhs: text = self.rhs obj = caller.search(self.lhs) if not obj: return else: text = self.args obj = location obj.db.desc = text # a compatibility fallback if obj.attributes.has("general_desc"): obj.db.general_desc = text caller.msg("Основное описаине было задано для %s." % obj.key) else: # this is not an ExtendedRoom. caller.msg("Описание было задано для %s." % obj.key)