def update(self, ip, failmsg='Exceeded threshold.'): """ Store the time of the latest failure. Args: ip (str): IP address of requestor failmsg (str, optional): Message to display in logs upon activation of throttle. Returns: None """ # Get current status previously_throttled = self.check(ip) # Enforce length limits if not self.storage[ip].maxlen: self.storage[ip] = deque(maxlen=self.cache_size) self.storage[ip].append(time.time()) # See if this update caused a change in status currently_throttled = self.check(ip) # If this makes it engage, log a single activation event if (not previously_throttled and currently_throttled): logger.log_sec( 'Throttle Activated: %s (IP: %s, %i hits in %i seconds.)' % (failmsg, ip, self.limit, self.timeout))
def func(self): """hook function.""" account = self.account if not self.rhs: self.msg("Usage: password <oldpass> = <newpass>") return oldpass = self.lhslist[0] # Both of these are newpass = self.rhslist[0] # already stripped by parse() # Validate password validated, error = account.validate_password(newpass) if not account.check_password(oldpass): self.msg("The specified old password isn't correct.") elif not validated: errors = [e for suberror in error.messages for e in error.messages] string = "\n".join(errors) self.msg(string) else: account.set_password(newpass) account.save() self.msg("Password changed.") logger.log_sec("Password Changed: %s (Caller: %s, IP: %s)." % (account, account, self.session.address))
def func(self): """Destroy objects cleanly.""" caller = self.caller if not self.args: self.msg("Usage: cdestroy <channelname>") return channel = find_channel(caller, self.args) if not channel: self.msg("Could not find channel %s." % self.args) return if not channel.access(caller, "control"): self.msg("You are not allowed to do that.") return channel_key = channel.key message = "%s is being destroyed. Make sure to change your aliases." % channel_key msgobj = create.create_message(caller, message, channel) channel.msg(msgobj) channel.delete() CHANNELHANDLER.update() self.msg("Channel '%s' was destroyed." % channel_key) logger.log_sec( "Channel Deleted: %s (Caller: %s, IP: %s)." % (channel_key, caller, self.session.address) )
def func(self): """Implement unbanning""" banlist = ServerConfig.objects.conf('server_bans') if not self.args: self.caller.msg(list_bans(self, banlist)) return try: num = int(self.args) except Exception: self.caller.msg("You must supply a valid ban id to clear.") return if not banlist: self.caller.msg("There are no bans to clear.") elif not (0 < num < len(banlist) + 1): self.caller.msg("Ban id |w%s|x was not found." % self.args) else: # all is ok, clear ban ban = banlist[num - 1] del banlist[num - 1] ServerConfig.objects.conf('server_bans', banlist) value = " ".join([s for s in ban[:2]]) self.caller.msg("Cleared ban %s: %s" % (num, value)) logger.log_sec('Unbanned: %s (Caller: %s, IP: %s).' % (value.strip(), self.caller, self.session.address))
def func(self): """Implement the function.""" caller = self.caller if not self.rhs: self.msg("Usage: userpassword <user obj> = <new password>") return # the account search also matches 'me' etc. account = caller.search_account(self.lhs) if not account: return newpass = self.rhs # Validate password validated, error = account.validate_password(newpass) if not validated: errors = [e for suberror in error.messages for e in error.messages] string = "\n".join(errors) caller.msg(string) return account.set_password(newpass) account.save() self.msg("%s - new password set to '%s'." % (account.name, newpass)) if account.character != caller: account.msg("%s has changed your password to '%s'." % (caller.name, newpass)) logger.log_sec('Password Changed: %s (Caller: %s, IP: %s).' % (account, caller, self.session.address))
def func(self): """Implementing the function""" caller = self.caller args = self.args if not args: caller.msg("Usage: @boot[/switches] <account> [:reason]") return if ':' in args: args, reason = [a.strip() for a in args.split(':', 1)] else: args, reason = args, "" boot_list = [] if 'sid' in self.switches: # Boot a particular session id. sessions = SESSIONS.get_sessions(True) for sess in sessions: # Find the session with the matching session id. if sess.sessid == int(args): boot_list.append(sess) break else: # Boot by account object pobj = search.account_search(args) if not pobj: caller.msg("Account %s was not found." % args) return pobj = pobj[0] if not pobj.access(caller, 'boot'): string = "You don't have the permission to boot %s." % (pobj.key, ) caller.msg(string) return # we have a bootable object with a connected user matches = SESSIONS.sessions_from_account(pobj) for match in matches: boot_list.append(match) if not boot_list: caller.msg("No matching sessions found. The Account does not seem to be online.") return # Carry out the booting of the sessions in the boot list. feedback = None if 'quiet' not in self.switches: feedback = "You have been disconnected by %s.\n" % caller.name if reason: feedback += "\nReason given: %s" % reason for session in boot_list: session.msg(feedback) session.account.disconnect_session_from_account(session) if pobj and boot_list: logger.log_sec('Booted: %s (Reason: %s, Caller: %s, IP: %s).' % (pobj, reason, caller, self.session.address))
def func(self): """create the new character""" account = self.account if not self.args: self.msg("Utilisation : créer <nom du personnage> [= description]") return key = self.lhs desc = self.rhs charmax = _MAX_NR_CHARACTERS if not account.is_superuser and ( account.db._playable_characters and len(account.db._playable_characters) >= charmax): plural = "" if charmax == 1 else "s" self.msg(f"Vous pouvez seulement créer un maximum de {charmax} " f"personnage{plural}.") return from evennia.objects.models import ObjectDB typeclass = settings.BASE_CHARACTER_TYPECLASS if ObjectDB.objects.filter(db_typeclass_path=typeclass, db_key__iexact=key): # check if this Character already exists. Note that we are only # searching the base character typeclass here, not any child # classes. self.msg(f"|rUn personnage nommé '|w{key}|r' existe déjà.|n") return # create the character start_location = ObjectDB.objects.get_id(settings.START_LOCATION) default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) permissions = settings.PERMISSION_ACCOUNT_DEFAULT new_character = create.create_object(typeclass, key=key, location=start_location, home=default_home, permissions=permissions) # only allow creator (and developers) to puppet this char new_character.locks.add( f"puppet:id({new_character.id}) or pid({account.id}) or " f"perm(Developer) or pperm(Developer);delete:id({account.id}) or " f"perm(Admin)") account.db._playable_characters.append(new_character) if desc: new_character.db.desc = desc elif not new_character.db.desc: new_character.db.desc = "Ceci est un personnage" self.msg( "Nouveau personnage créé : {name}. " "Use |wic {name}|n to enter the game as this character.".format( name=new_character.key)) logger.log_sec( f"Character Created: {new_character} (Caller: {account}, " f"IP: {self.session.address}).")
def func(self): """create the new character""" account = self.account if not self.args: self.msg("Usage: charcreate <charname> [= description]") return key = self.lhs desc = self.rhs charmax = _MAX_NR_CHARACTERS if not account.is_superuser and ( account.db._playable_characters and len(account.db._playable_characters) >= charmax): plural = "" if charmax == 1 else "s" self.msg( f"You may only create a maximum of {charmax} character{plural}." ) return from evennia.objects.models import ObjectDB typeclass = settings.BASE_CHARACTER_TYPECLASS if ObjectDB.objects.filter(db_typeclass_path=typeclass, db_key__iexact=key): # check if this Character already exists. Note that we are only # searching the base character typeclass here, not any child # classes. self.msg("|rA character named '|w%s|r' already exists.|n" % key) return # create the character start_location = ObjectDB.objects.get_id(settings.START_LOCATION) default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) permissions = settings.PERMISSION_ACCOUNT_DEFAULT new_character = create.create_object(typeclass, key=key, location=start_location, home=default_home, permissions=permissions) # only allow creator (and developers) to puppet this char new_character.locks.add( "puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer);delete:id(%i) or perm(Admin)" % (new_character.id, account.id, account.id)) account.db._playable_characters.append(new_character) if desc: new_character.db.desc = desc elif not new_character.db.desc: new_character.db.desc = "This is a character." self.msg( "Created new character %s. Use |wic %s|n to enter the game as this character." % (new_character.key, new_character.key)) logger.log_sec("Character Created: %s (Caller: %s, IP: %s)." % (new_character, account, self.session.address))
def func(self): """ Bans are stored in a serverconf db object as a list of dictionaries: [ (name, ip, ipregex, date, reason), (name, ip, ipregex, date, reason),... ] where name and ip are set by the user and are shown in lists. ipregex is a converted form of ip where the * is replaced by an appropriate regex pattern for fast matching. date is the time stamp the ban was instigated and 'reason' is any optional info given to the command. Unset values in each tuple is set to the empty string. """ banlist = ServerConfig.objects.conf('server_bans') if not banlist: banlist = [] if not self.args or (self.switches and not any(switch in ('ip', 'name') for switch in self.switches)): self.caller.msg(list_bans(self, banlist)) return now = time.ctime() reason = "" if ':' in self.args: ban, reason = self.args.rsplit(':', 1) else: ban = self.args ban = ban.lower() ipban = IPREGEX.findall(ban) if not ipban: # store as name typ = "Name" bantup = (ban, "", "", now, reason) else: # an ip address. typ = "IP" ban = ipban[0] # replace * with regex form and compile it ipregex = ban.replace('.', '\.') ipregex = ipregex.replace('*', '[0-9]{1,3}') ipregex = re.compile(r"%s" % ipregex) bantup = ("", ban, ipregex, now, reason) # save updated banlist banlist.append(bantup) ServerConfig.objects.conf('server_bans', banlist) self.caller.msg("%s-Ban |w%s|n was added." % (typ, ban)) logger.log_sec('Banned %s: %s (Caller: %s, IP: %s).' % (typ, ban.strip(), self.caller, self.session.address))
def func(self): """implement the function""" if not self.args or not self.rhs: string = "Usage: cboot[/quiet] <channel> = <account> [:reason]" self.msg(string) return channel = find_channel(self.caller, self.lhs) if not channel: return reason = "" if ":" in self.rhs: accountname, reason = self.rhs.rsplit(":", 1) searchstring = accountname.lstrip("*") else: searchstring = self.rhs.lstrip("*") account = self.caller.search(searchstring, account=True) if not account: return if reason: reason = " (reason: %s)" % reason if not channel.access(self.caller, "control"): string = "You don't control this channel." self.msg(string) return if not channel.subscriptions.has(account): string = "Account %s is not connected to channel %s." % ( account.key, channel.key) self.msg(string) return if "quiet" not in self.switches: string = "%s boots %s from channel.%s" % (self.caller, account.key, reason) channel.msg(string) # find all account's nicks linked to this channel and delete them for nick in [ nick for nick in account.character.nicks.get( category="channel") or [] if nick.value[3].lower() == channel.key ]: nick.delete() # disconnect account channel.disconnect(account) CHANNELHANDLER.update() logger.log_sec( "Channel Boot: %s (Channel: %s, Reason: %s, Caller: %s, IP: %s)." % (account, channel, reason, self.caller, self.session.address))
def _callback(caller, callback_prompt, result): if result.lower() == "yes": # only take action delobj = caller.ndb._char_to_delete key = delobj.key caller.db._playable_characters = [ pc for pc in caller.db._playable_characters if pc != delobj ] delobj.delete() self.msg("Character '%s' was permanently deleted." % key) logger.log_sec( "Character Deleted: %s (Caller: %s, IP: %s)." % (key, account, self.session.address)) else: self.msg("Deletion was aborted.") del caller.ndb._char_to_delete
def _callback(caller, callback_prompt, result): if result.lower() == "oui": # only take action delobj = caller.ndb._char_to_delete key = delobj.key caller.db._playable_characters = [ pc for pc in caller.db._playable_characters if pc != delobj ] delobj.delete() self.msg(f"Le personnage '{key}' a été supprimé de " f"manière permanente.") logger.log_sec( "Character Deleted: %s (Caller: %s, IP: %s)." % (key, account, self.session.address)) else: self.msg("La suppression de personnage à été annulée.") del caller.ndb._char_to_delete
def func(self): """ Main puppet method """ account = self.account session = self.session new_character = None if not self.args: new_character = account.db._last_puppet if not new_character: self.msg("Usage: ic <character>") return if not new_character: # search for a matching character new_character = [ char for char in search.object_search(self.args) if char.access(account, "puppet") ] if not new_character: self.msg("That is not a valid character choice.") return if len(new_character) > 1: self.msg( "Multiple targets with the same name:\n %s" % ", ".join("%s(#%s)" % (obj.key, obj.id) for obj in new_character) ) return else: new_character = new_character[0] try: account.puppet_object(session, new_character) account.db._last_puppet = new_character logger.log_sec( "Puppet Success: (Caller: %s, Target: %s, IP: %s)." % (account, new_character, self.session.address) ) except RuntimeError as exc: self.msg("|rYou cannot become |C%s|n: %s" % (new_character.name, exc)) logger.log_sec( "Puppet Failed: %s (Caller: %s, Target: %s, IP: %s)." % (exc, account, new_character, self.session.address) )
def func(self): """List the accounts""" caller = self.caller args = self.args if "delete" in self.switches: account = getattr(caller, "account") if not account or not account.check_permstring("Developer"): caller.msg("You are not allowed to delete accounts.") return if not args: caller.msg("Usage: accounts/delete <name or #id> [: reason]") return reason = "" if ":" in args: args, reason = [arg.strip() for arg in args.split(":", 1)] # We use account_search since we want to be sure to find also accounts # that lack characters. accounts = search.account_search(args) if not accounts: self.msg("Could not find an account by that name.") return if len(accounts) > 1: string = "There were multiple matches:\n" string += "\n".join(" %s %s" % (account.id, account.key) for account in accounts) self.msg(string) return account = accounts.first() if not account.access(caller, "delete"): self.msg("You don't have the permissions to delete that account.") return username = account.username # ask for confirmation confirm = ( "It is often better to block access to an account rather than to delete it. " "|yAre you sure you want to permanently delete " "account '|n{}|y'|n yes/[no]?".format(username) ) answer = yield (confirm) if answer.lower() not in ("y", "yes"): caller.msg("Canceled deletion.") return # Boot the account then delete it. self.msg("Informing and disconnecting account ...") string = "\nYour account '%s' is being *permanently* deleted.\n" % username if reason: string += " Reason given:\n '%s'" % reason account.msg(string) logger.log_sec( "Account Deleted: %s (Reason: %s, Caller: %s, IP: %s)." % (account, reason, caller, self.session.address) ) account.delete() self.msg("Account %s was successfully deleted." % username) return # No switches, default to displaying a list of accounts. if self.args and self.args.isdigit(): nlim = int(self.args) else: nlim = 10 naccounts = AccountDB.objects.count() # typeclass table dbtotals = AccountDB.objects.object_totals() typetable = self.styled_table( "|wtypeclass|n", "|wcount|n", "|w%%|n", border="cells", align="l" ) for path, count in dbtotals.items(): typetable.add_row(path, count, "%.2f" % ((float(count) / naccounts) * 100)) # last N table plyrs = AccountDB.objects.all().order_by("db_date_created")[max(0, naccounts - nlim) :] latesttable = self.styled_table( "|wcreated|n", "|wdbref|n", "|wname|n", "|wtypeclass|n", border="cells", align="l" ) for ply in plyrs: latesttable.add_row( utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.path ) string = "\n|wAccount typeclass distribution:|n\n%s" % typetable string += "\n|wLast %s Accounts created:|n\n%s" % (min(naccounts, nlim), latesttable) caller.msg(string)
{"connect": r"^[@\s]*[connect]{5,8}\s+(?P<secret>[\w]+)"}, {"create": r"^[^@]?[create]{5,6}\s+(\w+|\".+?\")\s+(?P<secret>[\w]+)"}, {"create": r"^[^@]?[create]{5,6}\s+(?P<secret>[\w]+)"}, {"userpassword": r"^[@\s]*[userpassword]{11,14}\s+(\w+|\".+?\")\s+=*\s*(?P<secret>[\w]+)"}, {"userpassword": r"^.*new password set to '(?P<secret>[^']+)'\."}, {"userpassword": r"^.* has changed your password to '(?P<secret>[^']+)'\."}, {"password": r"^[@\s]*[password]{6,9}\s+(?P<secret>.*)"}, ] + getattr(ev_settings, "AUDIT_MASKS", []) if AUDIT_CALLBACK: try: AUDIT_CALLBACK = getattr( mod_import(".".join(AUDIT_CALLBACK.split(".")[:-1])), AUDIT_CALLBACK.split(".")[-1] ) logger.log_sec("Auditing module online.") logger.log_sec( "Audit record User input: {}, output: {}.\n" "Audit sparse recording: {}, Log callback: {}".format( AUDIT_IN, AUDIT_OUT, AUDIT_ALLOW_SPARSE, AUDIT_CALLBACK ) ) except Exception as e: logger.log_err("Failed to activate Auditing module. %s" % e) class AuditedServerSession(ServerSession): """ This particular implementation parses all server inputs and/or outputs and passes a dict containing the parsed metadata to a callback method of your creation. This is useful for recording player activity where necessary for
{'connect': r"^[@\s]*[connect]{5,8}\s+(\".+?\"|[^\s]+)\s+(?P<secret>.+)"}, {'connect': r"^[@\s]*[connect]{5,8}\s+(?P<secret>[\w]+)"}, {'create': r"^[^@]?[create]{5,6}\s+(\w+|\".+?\")\s+(?P<secret>[\w]+)"}, {'create': r"^[^@]?[create]{5,6}\s+(?P<secret>[\w]+)"}, {'userpassword': r"^[@\s]*[userpassword]{11,14}\s+(\w+|\".+?\")\s+=*\s*(?P<secret>[\w]+)"}, {'userpassword': r"^.*new password set to '(?P<secret>[^']+)'\."}, {'userpassword': r"^.* has changed your password to '(?P<secret>[^']+)'\."}, {'password': r"^[@\s]*[password]{6,9}\s+(?P<secret>.*)"}, ] + getattr(ev_settings, 'AUDIT_MASKS', []) if AUDIT_CALLBACK: try: AUDIT_CALLBACK = getattr( mod_import('.'.join(AUDIT_CALLBACK.split('.')[:-1])), AUDIT_CALLBACK.split('.')[-1]) logger.log_sec("Auditing module online.") logger.log_sec("Audit record User input: {}, output: {}.\n" "Audit sparse recording: {}, Log callback: {}".format( AUDIT_IN, AUDIT_OUT, AUDIT_ALLOW_SPARSE, AUDIT_CALLBACK)) except Exception as e: logger.log_err("Failed to activate Auditing module. %s" % e) class AuditedServerSession(ServerSession): """ This particular implementation parses all server inputs and/or outputs and passes a dict containing the parsed metadata to a callback method of your creation. This is useful for recording player activity where necessary for security auditing, usage analysis or post-incident forensic discovery. *** WARNING ***
def func(self): """create the new character""" # This is the top level menu account = self.account if self.args: self.msg( "Usage: despite the directions above, just type 'charcreate' without arguments" ) return charmax = _MAX_NR_CHARACTERS if not account.is_superuser and ( account.db._playable_characters and len(account.db._playable_characters) >= charmax): self.msg("You may only create a maximum of %i characters." % charmax) return self.msg("About to create a character") from evennia.objects.models import ObjectDB genders = ['f', 'm'] while True: answer = yield ("Please select a gender: [(F)emale/(M)ale] ") answer = answer.strip().lower() if answer.lower() in genders: self.gender = int(genders.index(answer) + 1) break else: self.msg( "I'm afraid that was not an acceptable answer. Please try again." ) clan_list = list(self.gentes.keys()) for index, value in enumerate(clan_list): self.msg(f"{index + 1}) |c{value}|n") self.msg( "\nIn ancient Rome, the 'gens' was the largest kin unit, comprising several families who shared a common name. A Roman's 'nomen,' or primary name, would be based on their 'gens;' for example, a man belonging to the 'gens Sempronia' would be known as 'Sempronius' and a woman from that same gens would be 'Sempronia.'\n" ) while True: answer = yield (f"Select a gens: (1-{len(clan_list)}) ") answer = int(answer) if answer in range(1, len(clan_list) + 1): self.gens = clan_list[answer - 1] if self.gender == 2: self.nomen = self.gens[:-1] + 'us' else: self.nomen = self.gens break else: self.msg( "I'm sorry. That was not an acceptable answer. Please try again." ) self.msg(f"\nYou have selected '{self.gens}' as your gens.") self.msg( "\nThe 'praenomen' was used by intimates and immediate family members. The available 'praenomina' are determined by a person's 'gens' and gender.\n\n" ) temp_names = self.gentes[self.gens]['praenomina'] if self.gender == 1: for index, value in enumerate(temp_names): if value in ['Marcus', 'Titus', 'Tullus']: temp_names[index] = value[:-2] + 'ia' elif value[-2:] == 'us': temp_names[index] = value[:-2] + 'a' elif value == 'Agrippa': temp_names[index] = 'Agrippina' elif value == 'Caeso': temp_names[index] = 'Caesula' elif value == 'Opiter': temp_names[index] = 'Opita' elif value == 'Sertor': temp_names[index] = 'Sertora' elif value == 'Volero': temp_names[index] = 'Volerona' for index, value in enumerate(temp_names): self.msg(f"{index + 1}) |c{value}|n") while True: answer = yield (f"\nSelect a praenomen: (1-{len(temp_names)})") answer = int(answer) if answer in range(1, len(temp_names) + 1): self.praenomen = temp_names[answer - 1].strip() break else: self.msg("I'm sorry. That is not an acceptable answer.") return if self.praenomen[-1] == 'a': self.genitive = self.praenomen + 'e' elif self.praenomen[-1] == 'r': self.genitive = self.praenomen + 'is' elif self.praenomen[-2:] == 'us': self.genitive = self.praenomen[:-2] + 'i' else: self.genitive = self.praenomen + 'nis' self.msg(f"\nYou have selected '{self.praenomen}' as your praenomen.") # # Create stats # def makeStats(): statNums = [0, 0, 0, 0, 0, 0] points = 27 while points > 0: index = random.randint(0, 5) if statNums[index] < 5 and points > 0: statNums[index] += 1 points -= 1 elif statNums[index] in [5, 6] and points > 1: statNums[index] += 1 points -= 2 for index, value in enumerate(statNums): statNums[index] += 9 return statNums # # Roll and/or reroll stats # self.msg( "\nThe following character attributes are based on the point-buy system from the 5th edition of 'Dungeons and Dragons,' which means each group of generated attributes has an equivalent value overall. Still, please feel free to generate a new set if the first does not appeal.\n\n" ) self.msg('The minimum value is 9, the maximum is 16') self.msg('The English equivalents are:\n\n') self.msg('Vires: Strength') self.msg('Celeritas: Speed') self.msg('Valetudo: Constitution') self.msg('Scientia: Intelligence') self.msg('Sapientia: Wisdom') self.msg('Gratia: Charisma\n\n') while True: stats = makeStats() answer = yield ( f"Vires: |c{stats[0]}|n Celeritas: |c{stats[1]}|n Valetudo: |c{stats[2]}|n Scientia: |c{stats[3]}|n Sapientia: |c{stats[4]}|n Gratia: |c{stats[5]}|n; Are these acceptable? [(Y)es/(N)o] " ) while True: if answer.lower() in ['y', 'yes', 'n', 'no']: break else: answer = yield ("Please select: [(Y)es/(N)o] ") if answer in ['y', 'yes']: self.stats = stats break self.name = self.praenomen + ' ' + self.nomen self.msg(f"You are about to create a character named {self.name}") typeclass = settings.BASE_CHARACTER_TYPECLASS if ObjectDB.objects.filter(db_typeclass_path=typeclass, db_key__iexact=self.name): # check if this Character already exists. Note that we are only # searching the base character typeclass here, not any child # classes. self.msg("|rA character named '|w%s|r' already exists.|n" % self.name) return # create the character # start_location = ObjectDB.objects.get_id(settings.START_LOCATION) default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) self.msg("Before assigning permissions") permissions = settings.PERMISSION_ACCOUNT_DEFAULT self.msg("after assigning permissions") nom_list = [self.praenomen] gen_list = [self.genitive] self.msg(f"nom: {nom_list}; gen: {gen_list}") new_character = create.create_object( typeclass, key=self.name, # location=start_location, home=default_home, permissions=permissions, attributes=[('gens', self.gens), ('gender', self.gender), ('praenomen', self.praenomen), ('nomen', self.nomen), ('age', 18), ('nom_sg', nom_list), ('gen_sg', gen_list), ('stats', { 'str': self.stats[0], 'dex': self.stats[1], 'con': self.stats[2], 'int': self.stats[3], 'wis': self.stats[4], 'cha': self.stats[5] })]) # only allow creator (and developers) to puppet this char new_character.locks.add( "puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer);delete:id(%i) or perm(Admin)" % (new_character.id, account.id, account.id)) account.db._playable_characters.append(new_character) if desc: new_character.db.desc = desc else: new_character.db.desc = "This is a character." self.msg( "Created new character %s. Use |wic %s|n to enter the game as this character." % (new_character.key, new_character.key)) logger.log_sec("Character Created: %s (Caller: %s, IP: %s)." % (new_character, account, self.session.address)) self.msg( f"You have successfully created: {praenomen} of the gens {gens}")
def func(self): """Implement function""" caller = self.caller switches = self.switches lhs, rhs = self.lhs, self.rhs if not self.args: string = "Usage: perm[/switch] object [ = permission, permission, ...]" caller.msg(string) return accountmode = 'account' in self.switches or lhs.startswith('*') lhs = lhs.lstrip("*") if accountmode: obj = caller.search_account(lhs) else: obj = caller.search(lhs, global_search=True) if not obj: return if not rhs: if not obj.access(caller, 'examine'): caller.msg("You are not allowed to examine this object.") return string = "Permissions on |w%s|n: " % obj.key if not obj.permissions.all(): string += "<None>" else: string += ", ".join(obj.permissions.all()) if (hasattr(obj, 'account') and hasattr(obj.account, 'is_superuser') and obj.account.is_superuser): string += "\n(... but this object is currently controlled by a SUPERUSER! " string += "All access checks are passed automatically.)" caller.msg(string) return # we supplied an argument on the form obj = perm locktype = "edit" if accountmode else "control" if not obj.access(caller, locktype): caller.msg("You are not allowed to edit this %s's permissions." % ("account" if accountmode else "object")) return caller_result = [] target_result = [] if 'del' in switches: # delete the given permission(s) from object. for perm in self.rhslist: obj.permissions.remove(perm) if obj.permissions.get(perm): caller_result.append( "\nPermissions %s could not be removed from %s." % (perm, obj.name)) else: caller_result.append( "\nPermission %s removed from %s (if they existed)." % (perm, obj.name)) target_result.append( "\n%s revokes the permission(s) %s from you." % (caller.name, perm)) logger.log_sec( 'Permissions Deleted: %s, %s (Caller: %s, IP: %s).' % (perm, obj, caller, self.session.address)) else: # add a new permission permissions = obj.permissions.all() for perm in self.rhslist: # don't allow to set a permission higher in the hierarchy than # the one the caller has (to prevent self-escalation) if (perm.lower() in PERMISSION_HIERARCHY and not obj.locks.check_lockstring( caller, "dummy:perm(%s)" % perm)): caller.msg( "You cannot assign a permission higher than the one you have yourself." ) return if perm in permissions: caller_result.append( "\nPermission '%s' is already defined on %s." % (perm, obj.name)) else: obj.permissions.add(perm) plystring = "the Account" if accountmode else "the Object/Character" caller_result.append( "\nPermission '%s' given to %s (%s)." % (perm, obj.name, plystring)) target_result.append( "\n%s gives you (%s, %s) the permission '%s'." % (caller.name, obj.name, plystring, perm)) logger.log_sec( 'Permissions Added: %s, %s (Caller: %s, IP: %s).' % (obj, perm, caller, self.session.address)) caller.msg("".join(caller_result).strip()) if target_result: obj.msg("".join(target_result).strip())
def func(self): """ Main puppet method """ account = self.account session = self.session new_character = None character_candidates = [] if not self.args: character_candidates = [account.db._last_puppet ] if account.db._last_puppet else [] if not character_candidates: self.msg("Usage: ic <character>") return else: # argument given if account.db._playable_characters: # look at the playable_characters list first character_candidates.extend( account.search( self.args, candidates=account.db._playable_characters, search_object=True, quiet=True, )) if account.locks.check_lockstring(account, "perm(Builder)"): # builders and higher should be able to puppet more than their # playable characters. if session.puppet: # start by local search - this helps to avoid the user # getting locked into their playable characters should one # happen to be named the same as another. We replace the suggestion # from playable_characters here - this allows builders to puppet objects # with the same name as their playable chars should it be necessary # (by going to the same location). character_candidates = [ char for char in session.puppet.search(self.args, quiet=True) if char.access(account, "puppet") ] if not character_candidates: # fall back to global search only if Builder+ has no # playable_characers in list and is not standing in a room # with a matching char. character_candidates.extend([ char for char in search.object_search(self.args) if char.access(account, "puppet") ]) # handle possible candidates if not character_candidates: self.msg("That is not a valid character choice.") return if len(character_candidates) > 1: self.msg("Multiple targets with the same name:\n %s" % ", ".join("%s(#%s)" % (obj.key, obj.id) for obj in character_candidates)) return else: new_character = character_candidates[0] # do the puppet puppet try: account.puppet_object(session, new_character) account.db._last_puppet = new_character logger.log_sec( "Puppet Success: (Caller: %s, Target: %s, IP: %s)." % (account, new_character, self.session.address)) except RuntimeError as exc: self.msg("|rYou cannot become |C%s|n: %s" % (new_character.name, exc)) logger.log_sec( "Puppet Failed: %s (Caller: %s, Target: %s, IP: %s)." % (exc, account, new_character, self.session.address))