Advertisement
Guest User

my menu_login.py

a guest
Apr 10th, 2017
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.86 KB | None | 0 0
  1. import re
  2. from textwrap import dedent
  3.  
  4. from django.conf import settings
  5.  
  6. from evennia import Command, CmdSet
  7. from evennia import logger
  8. from evennia import managers
  9. from evennia import ObjectDB
  10. from evennia.server.models import ServerConfig
  11. from evennia import syscmdkeys
  12. from evennia.utils.evmenu import EvMenu
  13. from evennia.utils.utils import random_string_from_module
  14.  
  15. ## Constants
  16. RE_VALID_USERNAME = re.compile(r"^[a-z]{3,}$", re.I)
  17. LEN_PASSWD = 6
  18. CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
  19.  
  20. ## Menu notes (top-level functions)
  21.  
  22. def start(caller):
  23.     """The user should enter his/her username or NEW to create one.
  24.  
  25.    This node is called at the very beginning of the menu, when
  26.    a session has been created OR if an error occurs further
  27.    down the menu tree.  From there, users can either enter a
  28.    username (if this username exists) or type NEW (capitalized
  29.    or not) to create a new player.
  30.  
  31.    """
  32.     text = random_string_from_module(CONNECTION_SCREEN_MODULE)
  33.     text += "\n\nEnter your character name:."
  34.     options = (
  35.         {   "key": "",
  36.             "goto": "start",
  37.         },
  38.         {   "key": "quit",
  39.             "goto": "quit"
  40.         },
  41.         {
  42.             "key": "_default",
  43.             "goto": "username",
  44.         },
  45.     )
  46.     return text, options
  47.  
  48. def username(caller, string_input):
  49.     """Check that the username leads to an existing player.
  50.  
  51.    Check that the specified username exists.  If the username doesn't
  52.    exist, display an error message and ask the user to try again.  If
  53.    entering an empty string, return to start node.  If user exists,
  54.    move to the next node (enter password).
  55.  
  56.    """
  57.     string_input = string_input.strip()
  58.     player = managers.players.get_player_from_name(string_input)
  59.     if player is None:
  60.         text = dedent("""
  61.        |rLiving Worlds is an Intense Roleplaying environment and as such it is
  62. expected that your name complies with our genre. The following rules apply:
  63.  
  64. 1. Modern names like Robert, David, Julie, or Angela are not allowed.
  65. 2. Names that are well known in books, games, or movies are not allowed.
  66. 3. Names that are dictionary words like Blade, Death, Wild are not allowed.
  67. 4. Names of Mythological characters or Gods are not allowed.
  68.        
  69. Try to choose a name that is unique for your character and unique to this world.
  70.        
  71. Did you enter the name correctly and does this name comply with the rules (y/n)?|n
  72.        """.strip("\n")).format(string_input)
  73.         options = (
  74.             {
  75.                 "key": "N",
  76.                 "goto": "start",
  77.             },
  78.             {
  79.                 "key": "Y",
  80.                 "goto": "create_account",
  81.             },
  82.         )
  83.     else:
  84.         caller.ndb._menutree.player = player
  85.         text = "Enter the password for the {} account.".format(player.name)
  86.         # Disables echo for the password
  87.         caller.msg("", options={"echo": False})
  88.         options = (
  89.             {
  90.                 "key": "",
  91.                 "exec": lambda caller: caller.msg("", options={"echo": True}),
  92.                 "goto": "start",
  93.             },
  94.             {
  95.                 "key": "_default",
  96.                 "goto": "ask_password",
  97.             },
  98.         )
  99.  
  100.     return text, options
  101.  
  102. def ask_password(caller, string_input):
  103.     """Ask the user to enter the password to this player.
  104.  
  105.    This is assuming the user exists (see 'create_username' and
  106.    'create_password').  This node "loops" if needed:  if the
  107.    user specifies a wrong password, offers the user to try
  108.    again or to go back by entering 'b'.
  109.    If the password is correct, then login.
  110.  
  111.    """
  112.     menutree = caller.ndb._menutree
  113.     string_input = string_input.strip()
  114.  
  115.     # Check the password and login is correct; also check for bans
  116.  
  117.     player = menutree.player
  118.     password_attempts = menutree.password_attempts \
  119.                             if hasattr(menutree, "password_attempts") else 0
  120.     bans = ServerConfig.objects.conf("server_bans")
  121.     banned = bans and (any(tup[0] == player.name.lower() for tup in bans) \
  122.             or any(tup[2].match(caller.address) for tup in bans if tup[2]))
  123.  
  124.     if not player.check_password(string_input):
  125.         # Didn't enter a correct password
  126.         password_attempts += 1
  127.         if password_attempts > 2:
  128.             # Too many tries
  129.             caller.sessionhandler.disconnect(
  130.                     caller, "|rToo many failed attempts. Disconnecting.|n")
  131.             text = ""
  132.             options = {}
  133.         else:
  134.             menutree.password_attempts = password_attempts
  135.             text = dedent("""
  136.                |rIncorrect password.|n
  137.                Try again or leave empty to go back.
  138.            """.strip("\n"))
  139.             # Loops on the same node
  140.             options = (
  141.                 {
  142.                     "key": "",
  143.                     "exec": lambda caller: caller.msg("", options={"echo": True}),
  144.                     "goto": "start",
  145.                 },
  146.                 {
  147.                     "key": "_default",
  148.                     "goto": "ask_password",
  149.                 },
  150.             )
  151.     elif banned:
  152.         # This is a banned IP or name!
  153.         string = dedent("""
  154.            |rYou have been banned and cannot continue from here.
  155.            If you feel this ban is in error, please email an admin.|n
  156.            Disconnecting.
  157.        """.strip("\n"))
  158.         caller.sessionhandler.disconnect(caller, string)
  159.         text = ""
  160.         options = {}
  161.     else:
  162.         # We are OK, log us in.
  163.         text = ""
  164.         options = {}
  165.         caller.msg("", options={"echo": True})
  166.         caller.sessionhandler.login(caller, player)
  167.  
  168.     return text, options
  169.  
  170. def create_account(caller):
  171.     """Create a new account.
  172.  
  173.    This node simply prompts the user to entere a username.
  174.    The input is redirected to 'create_username'.
  175.  
  176.    """
  177.     text = "Enter your new account name."
  178.     options = (
  179.         {
  180.             "key": "_default",
  181.             "goto": "create_username",
  182.         },
  183.     )
  184.     return text, options
  185.  
  186. def create_username(caller, string_input):
  187.     """Prompt to enter a valid username (one that doesnt exist).
  188.  
  189.    'string_input' contains the new username.  If it exists, prompt
  190.    the username to retry or go back to the login screen.
  191.  
  192.    """
  193.     menutree = caller.ndb._menutree
  194.     string_input = start.string_input.strip()
  195.     player = managers.players.get_player_from_name(string_input)
  196.  
  197.     # If a player with that name exists, a new one will not be created
  198.     if player:
  199.         text = dedent("""
  200.            |rThe account {} already exists.|n
  201.            Enter another username or leave blank to go back.
  202.        """.strip("\n")).format(string_input)
  203.         # Loops on the same node
  204.         options = (
  205.             {
  206.                 "key": "",
  207.                 "goto": "start",
  208.             },
  209.             {
  210.                 "key": "_default",
  211.                 "goto": "create_username",
  212.             },
  213.         )
  214.     elif not RE_VALID_USERNAME.search(string_input):
  215.         text = dedent("""
  216.            |rThis username isn't valid.|n
  217.            Only letters are accepted, without special characters.
  218.            The username must be at least 3 characters long.
  219.            Enter another username or leave blank to go back.
  220.        """.strip("\n"))
  221.         options = (
  222.             {
  223.                 "key": "",
  224.                 "goto": "start",
  225.             },
  226.             {
  227.                 "key": "_default",
  228.                 "goto": "create_username",
  229.             },
  230.         )
  231.     else:
  232.         # a valid username - continue getting the password
  233.         menutree.playername = string_input
  234.         # Disables echo for entering password
  235.         caller.msg("", options={"echo": False})
  236.         # Redirects to the creation of a password
  237.         text = "Enter this account's new password."
  238.         options = (
  239.             {
  240.                 "key": "_default",
  241.                 "goto": "create_password",
  242.             },
  243.         )
  244.  
  245.     return text, options
  246.  
  247. def create_password(caller, string_input):
  248.     """Ask the user to create a password.
  249.  
  250.    This node is at the end of the menu for account creation.  If
  251.    a proper MULTI_SESSION is configured, a character is also
  252.    created with the same name (we try to login into it).
  253.  
  254.    """
  255.     menutree = caller.ndb._menutree
  256.     text = ""
  257.     options = (
  258.         {
  259.             "key": "",
  260.             "exec": lambda caller: caller.msg("", options={"echo": True}),
  261.             "goto": "start",
  262.         },
  263.         {
  264.             "key": "_default",
  265.             "goto": "create_password",
  266.         },
  267.     )
  268.  
  269.     password = string_input.strip()
  270.     playername = menutree.playername
  271.  
  272.     if len(password) < LEN_PASSWD:
  273.         # The password is too short
  274.         text = dedent("""
  275.            |rYour password must be at least {} characters long.|n
  276.            Enter another password or leave it empty to go back.
  277.        """.strip("\n")).format(LEN_PASSWD)
  278.     else:
  279.         # Everything's OK.  Create the new player account and
  280.         # possibly the character, depending on the multisession mode
  281.         from evennia.commands.default import unloggedin
  282.         # We make use of the helper functions from the default set here.
  283.         try:
  284.             permissions = settings.PERMISSION_PLAYER_DEFAULT
  285.             typeclass = settings.BASE_CHARACTER_TYPECLASS
  286.             new_player = unloggedin._create_player(caller, playername,
  287.                     password, permissions)
  288.             if new_player:
  289.                 if settings.MULTISESSION_MODE < 2:
  290.                     default_home = ObjectDB.objects.get_id(
  291.                             settings.DEFAULT_HOME)
  292.                     unloggedin._create_character(caller, new_player,
  293.                             typeclass, default_home, permissions)
  294.         except Exception:
  295.             # We are in the middle between logged in and -not, so we have
  296.             # to handle tracebacks ourselves at this point. If we don't, we
  297.             # won't see any errors at all.
  298.             caller.msg(dedent("""
  299.                |rAn error occurred.|n  Please e-mail an admin if
  300.                the problem persists. Try another password or leave
  301.                it empty to go back to the login screen.
  302.            """.strip("\n")))
  303.             logger.log_trace()
  304.         else:
  305.             text = ""
  306.             caller.msg("|gWelcome, your new account has been created!|n")
  307.             caller.msg("", options={"echo": True})
  308.             caller.sessionhandler.login(caller, new_player)
  309.  
  310.     return text, options
  311.  
  312. def quit(caller):
  313.     caller.sessionhandler.disconnect(caller, "Goodbye! Logging off.")
  314.     return "", {}
  315.  
  316. ## Other functions
  317.  
  318. def _formatter(nodetext, optionstext, caller=None):
  319.     """Do not display the options, only the text.
  320.  
  321.    This function is used by EvMenu to format the text of nodes.
  322.    Options are not displayed for this menu, where it doesn't often
  323.    make much sense to do so.  Thus, only the node text is displayed.
  324.  
  325.    """
  326.     return nodetext
  327.  
  328. ## Commands and CmdSets
  329.  
  330. class UnloggedinCmdSet(CmdSet):
  331.     "Cmdset for the unloggedin state"
  332.     key = "DefaultUnloggedin"
  333.     priority = 0
  334.  
  335.     def at_cmdset_creation(self):
  336.         "Called when cmdset is first created."
  337.         self.add(CmdUnloggedinLook())
  338.  
  339.  
  340. class CmdUnloggedinLook(Command):
  341.     """
  342.    An unloggedin version of the look command. This is called by the server
  343.    when the player first connects. It sets up the menu before handing off
  344.    to the menu's own look command.
  345.    """
  346.     key = syscmdkeys.CMD_LOGINSTART
  347.     locks = "cmd:all()"
  348.     arg_regex = r"^$"
  349.  
  350.     def func(self):
  351.         "Execute the menu"
  352.         EvMenu(self.caller, "server.conf.menu_login",
  353.                startnode="start", auto_look=False, auto_quit=False,
  354.                cmd_on_exit=None, node_formatter=_formatter)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement