def _create_account(session, accountname, password, permissions, typeclass=None, email=None): """ Helper function, creates an account of the specified typeclass. Contrary to the default `evennia.commands.default.unlogged._create_account`, the account isn't connected to the public chaannel. """ try: new_account = create.create_account(accountname, email, password, permissions=permissions, typeclass=typeclass) except Exception as e: session.msg( "There was an error creating the Account:\n%s\n If this problem persists, contact an admin." % e) logger.log_trace() return False # This needs to be set so the engine knows this account is # logging in for the first time. (so it knows to call the right # hooks during login later) new_account.db.FIRST_LOGIN = True return new_account
def _confirm_password(caller, user_input): password = caller.ndb.new_password menutree = caller.ndb._menutree if user_input == password: # Create the new player account playername = menutree.playername from evennia.commands.default import unloggedin # We make use of the helper functions from the default set here. # noinspection PyBroadException try: permissions = settings.PERMISSION_PLAYER_DEFAULT new_player = unloggedin._create_player(caller, playername, password, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg(LOGIN_ERROR) logger.log_trace() else: caller.ndb.passed_account_creation = True caller.msg(LOGIN_ACCOUNT_CREATED.format(playername)) caller.sessionhandler.login(caller, new_player) else: caller.msg(LOGIN_PASSWORD_MISMATCH) caller.ndb.confirm_needed = False
def create_password(caller, string_input): """Ask the user to create a password. This node is at the end of the menu for account creation. If a proper MULTI_SESSION is configured, a character is also created with the same name (we try to login into it). """ menutree = caller.ndb._menutree text = "" options = ( {"key": "", "exec": lambda caller: caller.msg("", options={"echo": True}), "goto": "start"}, {"key": "_default", "goto": "create_password"}) password = string_input.strip() accountname = menutree.accountname if len(password) < LEN_PASSWD: # The password is too short text = dedent(""" |rYour password must be at least {} characters long.|n Enter another password or leave it empty to go back. """.strip("\n")).format(LEN_PASSWD) else: # Everything's OK. Create the new player account and # possibly the character, depending on the multisession mode from evennia.commands.default import unloggedin # We make use of the helper functions from the default set here. try: permissions = settings.PERMISSION_ACCOUNT_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_account = unloggedin._create_account(caller, accountname, password, permissions) if new_account: if settings.MULTISESSION_MODE < 2: default_home = ObjectDB.objects.get_id( settings.DEFAULT_HOME) unloggedin._create_character(caller, new_account, typeclass, default_home, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg(dedent(""" |rAn error occurred.|n Please e-mail an admin if the problem persists. Try another password or leave it empty to go back to the login screen. """.strip("\n"))) logger.log_trace() else: text = "" caller.msg("|gWelcome, your new account has been created!|n") caller.msg("", options={"echo": True}) caller.sessionhandler.login(caller, new_account) return text, options
def confirm_password(caller, input): """Ask the user to confirm the account's password. The account's password has been saved in the session for the time being, as a hashed version. If the hashed version of the retyped password matches, then the account is created. If not, ask for another password. """ text = "" options = ({ "key": "_default", "desc": "Enter your password.", "goto": "create_password", }, ) password = input.strip() accountname = caller.db._accountname first_password = caller.db._password second_password = sha256(password.encode()).hexdigest() if first_password != second_password: text = dedent(""" |rThe password you have specified doesn't match the first one.|n Enter a new password for this account. """.strip("\n")) else: # Creates the new account. caller.msg(echo=True) try: permissions = settings.PERMISSION_ACCOUNT_DEFAULT account = _create_account(caller, accountname, password, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg( dedent(""" |rAn error occurred.|n Please email an admin if the problem persists. Please enter another password. """.strip("\n"))) logger.log_trace() else: caller.db._account = account del caller.db._password text = "Your new account was successfully created!" text += "\n\n" + _text_email_address(account) options = ({ "key": "_default", "desc": "Enter a valid email address.", "goto": "email_address", }, ) return text, options
def get_event_handler(): """Return the event handler or None.""" try: script = ScriptDB.objects.get(db_key="event_handler") except ScriptDB.DoesNotExist: logger.log_trace("Can't get the event handler.") script = None return script
def decorator(*args, **kwargs): try: return method(*args, **kwargs) except Exception: logger.log_trace(f"Error in State {__name__}") self.room.msg_room( None, f"|rThere was an unexpected error in State {__name__}. " "Please |wreport|r this as an issue.|n") raise # TODO
def send(self, event): """Dispatches an event to each open quest. Arguments: event - an arbitrary entity communicating information to the quests""" for quest in self.player.db.quests: try: quest.send(event) except Exception: logger.log_trace()
def goto(self, key): """ Go to a key in the tree. This sets up the cmdsets on the caller so that they match the choices in that node. """ if key == self.endnode: # if we was given the END node key, we clean up immediately. self.caller.cmdset.delete("menucmdset") del self.caller.db._menu_data if self.exec_end is not None: self.caller.execute_cmd(self.exec_end) return # not exiting, look for a valid code. node = self.tree.get(key, None) # make caller available on node node.caller = self.caller if node: # call on-node callback if node.callback: try: node.callback() except Exception: logger.log_trace() self.caller.msg( "{rNode callback could not be executed for node %s. Continuing anyway.{n" % key) if node.code: # Execute eventual code active on this node. self.caller is available at this point. evennia.logger.log_depmsg( "menusystem.code is deprecated. Use menusystem.callback.") try: exec(node.code) except Exception: self.caller.msg( "{rCode could not be executed for node %s. Continuing anyway.{n" % key) # initialize - this creates new cmdset node.init(self) # clean old menu cmdset and replace with the new one self.caller.cmdset.delete("menucmdset") self.caller.cmdset.add(node.cmdset) # set the menu flag data for the default commands self.caller.db._menu_data = { "help": node.helptext, "look": str(node.text) } # display the node self.caller.msg(node.text) else: self.caller.msg( "{rMenu node '%s' does not exist - maybe it's not created yet..{n" % key)
def func(self): "Execute the command" password = self.args self.caller.msg(echo=False) if not hasattr(self.menutree, 'playername'): self.caller.msg( "{rSomething went wrong! Playername not remembered from previous step!{n" ) self.menutree.goto("node2a") return playername = self.menutree.playername if len(password) < 3: # too short password string = "{rYour password must be at least 3 characters or longer." string += "\n\rFor best security, make it at least 8 characters " string += "long, avoid making it a real word and mix numbers " string += "into it.{n" self.caller.msg(string) self.menutree.goto("node2b") return # everything's ok. Create the new player account and possibly the character # depending on the multisession mode from evennia.commands.default import unloggedin # we make use of the helper functions from the default set here. try: permissions = settings.PERMISSION_PLAYER_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_player = unloggedin._create_player(self.caller, playername, password, permissions) if new_player: if MULTISESSION_MODE < 2: default_home = ObjectDB.objects.get_id( settings.DEFAULT_HOME) unloggedin._create_character(self.caller, new_player, typeclass, default_home, permissions) # tell the caller everything went well. string = "{gA new account '%s' was created. Now go log in from the menu!{n" self.caller.msg(string % (playername)) self.menutree.goto("START") except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. self.caller.msg( "An error occurred. Please e-mail an admin if the problem persists." ) logger.log_trace()
def load_state(self, statename): """ Load state without initializing it """ try: mod = utils.mod_import(f"{_ROOMSTATE_PACKAGE}.{statename}") except Exception as err: logger.log_trace() self.room.msg_room( None, f"|rBUG: Could not load state {statename}: {err}!") self.room.msg_room( None, f"|rBUG: Falling back to {self.current_state_name}") return state = mod.State(self, self.room) return state
def func(self): "Execute the command" password = self.args self.caller.msg(echo=False) if not hasattr(self.menutree, 'playername'): self.caller.msg("{rSomething went wrong! Playername not remembered from previous step!{n") self.menutree.goto("node2a") return playername = self.menutree.playername if len(password) < 3: # too short password string = "{rYour password must be at least 3 characters or longer." string += "\n\rFor best security, make it at least 8 characters " string += "long, avoid making it a real word and mix numbers " string += "into it.{n" self.caller.msg(string) self.menutree.goto("node2b") return # everything's ok. Create the new player account and possibly the character # depending on the multisession mode from evennia.commands.default import unloggedin # we make use of the helper functions from the default set here. try: permissions = settings.PERMISSION_PLAYER_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_player = unloggedin._create_player(self.caller, playername, password, permissions) if new_player: if MULTISESSION_MODE < 2: default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) unloggedin._create_character(self.caller, new_player, typeclass, default_home, permissions) # tell the caller everything went well. string = "{gA new account '%s' was created. Now go log in from the menu!{n" self.caller.msg(string % (playername)) self.menutree.goto("START") except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. self.caller.msg("An error occurred. Please e-mail an admin if the problem persists.") logger.log_trace()
def goto(self, key): """ Go to a key in the tree. This sets up the cmdsets on the caller so that they match the choices in that node. """ if key == self.endnode: # if we was given the END node key, we clean up immediately. self.caller.cmdset.delete("menucmdset") del self.caller.db._menu_data if self.exec_end is not None: self.caller.execute_cmd(self.exec_end) return # not exiting, look for a valid code. node = self.tree.get(key, None) # make caller available on node node.caller = self.caller if node: # call on-node callback if node.callback: try: node.callback() except Exception: logger.log_trace() self.caller.msg("{rNode callback could not be executed for node %s. Continuing anyway.{n" % key) if node.code: # Execute eventual code active on this node. self.caller is available at this point. evennia.logger.log_depmsg("menusystem.code is deprecated. Use menusystem.callback.") try: exec(node.code) except Exception: self.caller.msg("{rCode could not be executed for node %s. Continuing anyway.{n" % key) # initialize - this creates new cmdset node.init(self) # clean old menu cmdset and replace with the new one self.caller.cmdset.delete("menucmdset") self.caller.cmdset.add(node.cmdset) # set the menu flag data for the default commands self.caller.db._menu_data = {"help": node.helptext, "look": str(node.text)} # display the node self.caller.msg(node.text) else: self.caller.msg("{rMenu node '%s' does not exist - maybe it's not created yet..{n" % key)
def load(self): """ Takes the provided save data, validates it, and gets this Option ready to use. Returns: Boolean: Whether loading was successful. """ loadfunc = self.handler.loadfunc load_kwargs = self.handler.load_kwargs try: self.value_storage = self.deserialize( loadfunc(self.key, default=self.default_value, **load_kwargs)) except Exception: logger.log_trace() return False self.loaded = True return True
def complete_task(task_id): """ Mark the task in the event handler as complete. Args: task_id (int): the task ID. Note: This function should be called automatically for individual tasks. """ try: script = ScriptDB.objects.get(db_key="event_handler") except ScriptDB.DoesNotExist: logger.log_trace("Can't get the event handler.") return if task_id not in script.db.tasks: logger.log_err("The task #{} was scheduled, but it cannot be " \ "found".format(task_id)) return delta, obj, callback_name, locals = script.db.tasks.pop(task_id) script.call(obj, callback_name, locals=locals)
def confirm_password(caller, input): """Ask the user to confirm the account's password. The account's password has been saved in the session for the time being, as a hashed version. If the hashed version of the retyped password matches, then the player is created. If not, ask for another password. """ text = "" options = ( { "key": "b", "desc": "Go back to the password selection.", "goto": "create_password", }, { "key": "_default", "desc": "Enter your password.", "goto": "confirm_password", }, ) caller.msg(echo=True) password = input.strip() playername = caller.db._playername first_password = caller.db._password second_password = sha256(password).hexdigest() if first_password != second_password: text = dedent(""" |rThe password you have specified doesn't match the first one.|n Type |yb|n to choose a different password. Or type the confirmation password again. """.strip("\n")) else: # Creates the new player. from evennia.commands.default import unloggedin try: permissions = settings.PERMISSION_PLAYER_DEFAULT player = unloggedin._create_player(caller, playername, password, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg( dedent(""" |rAn error occurred.|n Please e-mail an admin if the problem persists. Type |yb|n to go back to the login screen. Or enter another password. """.strip("\n"))) logger.log_trace() else: caller.db._player = player del caller.db._password _login(caller, player) text = "Your new account was successfully created!" text += "\n\n" + text_email_address(player) options = ({ "key": "_default", "desc": "Enter a valid e-mail address.", "goto": "email_address", }, ) return text, options
def confirm_password(caller, input): """Ask the user to confirm the account's password. The account's password has been saved in the session for the time being, as a hashed version. If the hashed version of the retyped password matches, then the player is created. If not, ask for another password. """ text = "" options = ( { "key": "b", "desc": "Go back to the password selection.", "goto": "create_password", }, { "key": "_default", "desc": "Enter your password.", "goto": "confirm_password", }, ) caller.msg(echo=True) password = input.strip() playername = caller.db._playername first_password = caller.db._password second_password = sha256(password).hexdigest() if first_password != second_password: text = dedent(""" |rThe password you have specified doesn't match the first one.|n Type |yb|n to choose a different password. Or type the confirmation password again. """.strip("\n")) else: # Creates the new player. from evennia.commands.default import unloggedin try: permissions = settings.PERMISSION_PLAYER_DEFAULT player = unloggedin._create_player(caller, playername, password, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg(dedent(""" |rAn error occurred.|n Please e-mail an admin if the problem persists. Type |yb|n to go back to the login screen. Or enter another password. """.strip("\n"))) logger.log_trace() else: caller.db._player = player del caller.db._password _login(caller, player) text = "Your new account was successfully created!" text += "\n\n" + text_email_address(player) options = ( { "key": "_default", "desc": "Enter a valid e-mail address.", "goto": "email_address", }, ) return text, options
def call(self, obj, callback_name, *args, **kwargs): """ Call the connected callbacks. Args: obj (Object): the Evennia typeclassed object. callback_name (str): the callback name to call. *args: additional variables for this callback. Kwargs: number (int, optional): call just a specific callback. parameters (str, optional): call a callback with parameters. locals (dict, optional): a locals replacement. Returns: True to report the callback was called without interruption, False otherwise. """ # First, look for the callback type corresponding to this name number = kwargs.get("number") parameters = kwargs.get("parameters") locals = kwargs.get("locals") # Errors should not pass silently allowed = ("number", "parameters", "locals") if any(k for k in kwargs if k not in allowed): raise TypeError("Unknown keyword arguments were specified " \ "to call callbacks: {}".format(kwargs)) event = self.get_events(obj).get(callback_name) if locals is None and not event: logger.log_err("The callback {} for the object {} (typeclass " \ "{}) can't be found".format(callback_name, obj, type(obj))) return False # Prepare the locals if necessary if locals is None: locals = self.ndb.fresh_locals.copy() for i, variable in enumerate(event[0]): try: locals[variable] = args[i] except IndexError: logger.log_trace("callback {} of {} ({}): need variable " \ "{} in position {}".format(callback_name, obj, type(obj), variable, i)) return False else: locals = {key: value for key, value in locals.items()} callbacks = self.get_callbacks(obj).get(callback_name, []) if event: custom_call = event[2] if custom_call: callbacks = custom_call(callbacks, parameters) # Now execute all the valid callbacks linked at this address self.ndb.current_locals = locals for i, callback in enumerate(callbacks): if not callback["valid"]: continue if number is not None and callback["number"] != number: continue try: exec(callback["code"], locals, locals) except InterruptEvent: return False except Exception: etype, evalue, tb = sys.exc_info() trace = traceback.format_exception(etype, evalue, tb) self.handle_error(callback, trace) return True
def create_password(caller, string_input): """Ask the user to create a password. This node is at the end of the menu for account creation. If a proper MULTI_SESSION is configured, a character is also created with the same name (we try to login into it). """ menutree = caller.ndb._menutree text = "" options = ({ "key": "", "exec": lambda caller: caller.msg("", options={"echo": True}), "goto": "start" }, { "key": "_default", "goto": "create_password" }) password = string_input.strip() accountname = menutree.accountname if len(password) < LEN_PASSWD: # The password is too short text = dedent(""" |rYour password must be at least {} characters long.|n Enter another password or leave it empty to go back. """.strip("\n")).format(LEN_PASSWD) else: # Everything's OK. Create the new player account and # possibly the character, depending on the multisession mode from evennia.commands.default import unloggedin # We make use of the helper functions from the default set here. try: permissions = settings.PERMISSION_ACCOUNT_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_account = unloggedin._create_account(caller, accountname, password, permissions) if new_account: if settings.MULTISESSION_MODE < 2: default_home = ObjectDB.objects.get_id( settings.DEFAULT_HOME) unloggedin._create_character(caller, new_account, typeclass, default_home, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg( dedent(""" |rAn error occurred.|n Please e-mail an admin if the problem persists. Try another password or leave it empty to go back to the login screen. """.strip("\n"))) logger.log_trace() else: text = "" caller.msg("|gWelcome, your new account has been created!|n") caller.msg("", options={"echo": True}) caller.sessionhandler.login(caller, new_account) return text, options
def get_next_wait(format): """ Get the length of time in seconds before format. Args: format (str): a time format matching the set calendar. Returns: until (int or float): the number of seconds until the event. usual (int or float): the usual number of seconds between events. format (str): a string format representing the time. Notes: The time format could be something like "2018-01-08 12:00". The number of units set in the calendar affects the way seconds are calculated. """ calendar = getattr(settings, "EVENTS_CALENDAR", None) if calendar is None: logger.log_err("A time-related event has been set whereas " \ "the gametime calendar has not been set in the settings.") return elif calendar == "standard": rsu = standard_rsu units = ["min", "hour", "day", "month", "year"] elif calendar == "custom": rsu = custom_rsu back = dict([(value, name) for name, value in UNITS.items()]) sorted_units = sorted(back.items()) del sorted_units[0] units = [n for v, n in sorted_units] params = {} for delimiter in ("-", ":"): format = format.replace(delimiter, " ") pieces = list(reversed(format.split())) details = [] i = 0 for uname in units: try: piece = pieces[i] except IndexError: break if not piece.isdigit(): logger.log_trace("The time specified '{}' in {} isn't " \ "a valid number".format(piece, format)) return # Convert the piece to int piece = int(piece) params[uname] = piece details.append("{}={}".format(uname, piece)) if i < len(units): next_unit = units[i + 1] else: next_unit = None i += 1 params["sec"] = 0 details = " ".join(details) until = rsu(**params) usual = -1 if next_unit: kwargs = {next_unit: 1} usual = gametime_to_realtime(**kwargs) return until, usual, details
def create_password(caller, string_input): """Ask the user to create a password. This node is at the end of the menu for account creation. If a proper MULTI_SESSION is configured, a character is also created with the same name (we try to login into it). """ menutree = caller.ndb._menutree text = "" options = ( { "key": "r", "exec": lambda caller: caller.msg("", options={"echo": True}), "goto": "start", }, { "key": "_default", "goto": "create_password", }, ) password = string_input.strip() accountname = menutree.accountname if len(password) < LEN_PASSWD: # The password is too short text = dedent(""" |rLe mot de passe doit comporter au moins {} caractères.|n Entrez un nouveau mot de passe ou entrez |yr|n pour revenir à l'écran d'accueil. """.strip("\n")).format(LEN_PASSWD) else: from evennia.commands.default import unloggedin try: permissions = settings.PERMISSION_ACCOUNT_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_account = unloggedin._create_account(caller, accountname, password, permissions) if new_account: new_account.email = "" new_account.save() if settings.MULTISESSION_MODE < 2: default_home = ObjectDB.objects.get_id( settings.DEFAULT_HOME) unloggedin._create_character(caller, new_account, typeclass, default_home, permissions) except Exception: # We are in the middle between logged in and -not, so we have # to handle tracebacks ourselves at this point. If we don't, we # won't see any errors at all. caller.msg( dedent(""" |rUne erreur inattendue s'est produite..|n S'il vous plaît, envoyez un e-mail à [email protected] pour signaler ce problème. """.strip("\n"))) logger.log_trace() else: menutree.account = new_account caller.msg("", options={"echo": True}) text = dedent(""" Le nouvel utilisateur a bien été créé. Pour l'utiliser, il vous faut préciser une adresse e-mail valide. Un code de validation vous sera envoyé par e-mail. Vous devrez entrer ce code de validation dans votre client pour utiliser ce compte. Veuillez entrer une adresse e-mail valide : """).strip() options = ({ "key": "_default", "goto": "create_email_address", }, ) return text, options