Format code with black. Add makefile to run fmt/tests
This commit is contained in:
parent
d00bce9288
commit
c2c7fa311a
299 changed files with 19037 additions and 11611 deletions
|
|
@ -26,11 +26,12 @@ from evennia.commands import cmdhandler
|
|||
from evennia.server.models import ServerConfig
|
||||
from evennia.server.throttle import Throttle
|
||||
from evennia.utils import class_from_module, create, logger
|
||||
from evennia.utils.utils import (lazy_property, to_str,
|
||||
make_iter, is_iter,
|
||||
variable_from_module)
|
||||
from evennia.server.signals import (SIGNAL_ACCOUNT_POST_CREATE, SIGNAL_OBJECT_POST_PUPPET,
|
||||
SIGNAL_OBJECT_POST_UNPUPPET)
|
||||
from evennia.utils.utils import lazy_property, to_str, make_iter, is_iter, variable_from_module
|
||||
from evennia.server.signals import (
|
||||
SIGNAL_ACCOUNT_POST_CREATE,
|
||||
SIGNAL_OBJECT_POST_PUPPET,
|
||||
SIGNAL_OBJECT_POST_UNPUPPET,
|
||||
)
|
||||
from evennia.typeclasses.attributes import NickHandler
|
||||
from evennia.scripts.scripthandler import ScriptHandler
|
||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||
|
|
@ -43,7 +44,7 @@ __all__ = ("DefaultAccount",)
|
|||
|
||||
_SESSIONS = None
|
||||
|
||||
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit(".", 1))
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
_MAX_NR_CHARACTERS = settings.MAX_NR_CHARACTERS
|
||||
_CMDSET_ACCOUNT = settings.CMDSET_ACCOUNT
|
||||
|
|
@ -202,12 +203,14 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
@lazy_property
|
||||
def options(self):
|
||||
return OptionHandler(self,
|
||||
options_dict=settings.OPTIONS_ACCOUNT_DEFAULT,
|
||||
savefunc=self.attributes.add,
|
||||
loadfunc=self.attributes.get,
|
||||
save_kwargs={"category": 'option'},
|
||||
load_kwargs={"category": 'option'})
|
||||
return OptionHandler(
|
||||
self,
|
||||
options_dict=settings.OPTIONS_ACCOUNT_DEFAULT,
|
||||
savefunc=self.attributes.add,
|
||||
loadfunc=self.attributes.get,
|
||||
save_kwargs={"category": "option"},
|
||||
load_kwargs={"category": "option"},
|
||||
)
|
||||
|
||||
# Do not make this a lazy property; the web UI will not refresh it!
|
||||
@property
|
||||
|
|
@ -266,7 +269,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
# already puppeting this object
|
||||
self.msg("You are already puppeting this object.")
|
||||
return
|
||||
if not obj.access(self, 'puppet'):
|
||||
if not obj.access(self, "puppet"):
|
||||
# no access
|
||||
self.msg(f"You don't have permission to puppet '{obj.key}'.")
|
||||
return
|
||||
|
|
@ -389,6 +392,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
if _MULTISESSION_MODE in (0, 1):
|
||||
return puppets and puppets[0] or None
|
||||
return puppets
|
||||
|
||||
character = property(__get_single_puppet)
|
||||
puppet = property(__get_single_puppet)
|
||||
|
||||
|
|
@ -407,21 +411,23 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
"""
|
||||
|
||||
ip = kwargs.get('ip', '').strip()
|
||||
username = kwargs.get('username', '').lower().strip()
|
||||
ip = kwargs.get("ip", "").strip()
|
||||
username = kwargs.get("username", "").lower().strip()
|
||||
|
||||
# Check IP and/or name bans
|
||||
bans = ServerConfig.objects.conf("server_bans")
|
||||
if bans and (any(tup[0] == username for tup in bans if username) or
|
||||
any(tup[2].match(ip) for tup in bans if ip and tup[2])):
|
||||
if bans and (
|
||||
any(tup[0] == username for tup in bans if username)
|
||||
or any(tup[2].match(ip) for tup in bans if ip and tup[2])
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_username_validators(
|
||||
cls, validator_config=getattr(
|
||||
settings, 'AUTH_USERNAME_VALIDATORS', [])):
|
||||
cls, validator_config=getattr(settings, "AUTH_USERNAME_VALIDATORS", [])
|
||||
):
|
||||
"""
|
||||
Retrieves and instantiates validators for usernames.
|
||||
|
||||
|
|
@ -436,16 +442,18 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
objs = []
|
||||
for validator in validator_config:
|
||||
try:
|
||||
klass = import_string(validator['NAME'])
|
||||
klass = import_string(validator["NAME"])
|
||||
except ImportError:
|
||||
msg = (f"The module in NAME could not be imported: {validator['NAME']}. "
|
||||
"Check your AUTH_USERNAME_VALIDATORS setting.")
|
||||
msg = (
|
||||
f"The module in NAME could not be imported: {validator['NAME']}. "
|
||||
"Check your AUTH_USERNAME_VALIDATORS setting."
|
||||
)
|
||||
raise ImproperlyConfigured(msg)
|
||||
objs.append(klass(**validator.get('OPTIONS', {})))
|
||||
objs.append(klass(**validator.get("OPTIONS", {})))
|
||||
return objs
|
||||
|
||||
@classmethod
|
||||
def authenticate(cls, username, password, ip='', **kwargs):
|
||||
def authenticate(cls, username, password, ip="", **kwargs):
|
||||
"""
|
||||
Checks the given username/password against the database to see if the
|
||||
credentials are valid.
|
||||
|
|
@ -480,7 +488,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
# See if authentication is currently being throttled
|
||||
if ip and LOGIN_THROTTLE.check(ip):
|
||||
errors.append('Too many login failures; please try again in a few minutes.')
|
||||
errors.append("Too many login failures; please try again in a few minutes.")
|
||||
|
||||
# With throttle active, do not log continued hits-- it is a
|
||||
# waste of storage and can be abused to make your logs harder to
|
||||
|
|
@ -491,27 +499,29 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
banned = cls.is_banned(username=username, ip=ip)
|
||||
if banned:
|
||||
# this is a banned IP or name!
|
||||
errors.append("|rYou have been banned and cannot continue from here."
|
||||
"\nIf you feel this ban is in error, please email an admin.|x")
|
||||
logger.log_sec(f'Authentication Denied (Banned): {username} (IP: {ip}).')
|
||||
LOGIN_THROTTLE.update(ip, 'Too many sightings of banned artifact.')
|
||||
errors.append(
|
||||
"|rYou have been banned and cannot continue from here."
|
||||
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||
)
|
||||
logger.log_sec(f"Authentication Denied (Banned): {username} (IP: {ip}).")
|
||||
LOGIN_THROTTLE.update(ip, "Too many sightings of banned artifact.")
|
||||
return None, errors
|
||||
|
||||
# Authenticate and get Account object
|
||||
account = authenticate(username=username, password=password)
|
||||
if not account:
|
||||
# User-facing message
|
||||
errors.append('Username and/or password is incorrect.')
|
||||
errors.append("Username and/or password is incorrect.")
|
||||
|
||||
# Log auth failures while throttle is inactive
|
||||
logger.log_sec(f'Authentication Failure: {username} (IP: {ip}).')
|
||||
logger.log_sec(f"Authentication Failure: {username} (IP: {ip}).")
|
||||
|
||||
# Update throttle
|
||||
if ip:
|
||||
LOGIN_THROTTLE.update(ip, 'Too many authentication failures.')
|
||||
LOGIN_THROTTLE.update(ip, "Too many authentication failures.")
|
||||
|
||||
# Try to call post-failure hook
|
||||
session = kwargs.get('session', None)
|
||||
session = kwargs.get("session", None)
|
||||
if session:
|
||||
account = AccountDB.objects.get_account_from_name(username)
|
||||
if account:
|
||||
|
|
@ -520,7 +530,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
return None, errors
|
||||
|
||||
# Account successfully authenticated
|
||||
logger.log_sec(f'Authentication Success: {account} (IP: {ip}).')
|
||||
logger.log_sec(f"Authentication Success: {account} (IP: {ip}).")
|
||||
return account, errors
|
||||
|
||||
@classmethod
|
||||
|
|
@ -659,17 +669,19 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
account = None
|
||||
errors = []
|
||||
|
||||
username = kwargs.get('username')
|
||||
password = kwargs.get('password')
|
||||
email = kwargs.get('email', '').strip()
|
||||
guest = kwargs.get('guest', False)
|
||||
username = kwargs.get("username")
|
||||
password = kwargs.get("password")
|
||||
email = kwargs.get("email", "").strip()
|
||||
guest = kwargs.get("guest", False)
|
||||
|
||||
permissions = kwargs.get('permissions', settings.PERMISSION_ACCOUNT_DEFAULT)
|
||||
typeclass = kwargs.get('typeclass', cls)
|
||||
permissions = kwargs.get("permissions", settings.PERMISSION_ACCOUNT_DEFAULT)
|
||||
typeclass = kwargs.get("typeclass", cls)
|
||||
|
||||
ip = kwargs.get('ip', '')
|
||||
ip = kwargs.get("ip", "")
|
||||
if ip and CREATION_THROTTLE.check(ip):
|
||||
errors.append("You are creating too many accounts. Please log into an existing account.")
|
||||
errors.append(
|
||||
"You are creating too many accounts. Please log into an existing account."
|
||||
)
|
||||
return None, errors
|
||||
|
||||
# Normalize username
|
||||
|
|
@ -696,19 +708,25 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
banned = cls.is_banned(username=username, ip=ip)
|
||||
if banned:
|
||||
# this is a banned IP or name!
|
||||
string = "|rYou have been banned and cannot continue from here." \
|
||||
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||
string = (
|
||||
"|rYou have been banned and cannot continue from here."
|
||||
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||
)
|
||||
errors.append(string)
|
||||
return None, errors
|
||||
|
||||
# everything's ok. Create the new account.
|
||||
try:
|
||||
try:
|
||||
account = create.create_account(username, email, password, permissions=permissions, typeclass=typeclass)
|
||||
logger.log_sec(f'Account Created: {account} (IP: {ip}).')
|
||||
account = create.create_account(
|
||||
username, email, password, permissions=permissions, typeclass=typeclass
|
||||
)
|
||||
logger.log_sec(f"Account Created: {account} (IP: {ip}).")
|
||||
|
||||
except Exception as e:
|
||||
errors.append("There was an error creating the Account. If this problem persists, contact an admin.")
|
||||
errors.append(
|
||||
"There was an error creating the Account. If this problem persists, contact an admin."
|
||||
)
|
||||
logger.log_trace()
|
||||
return None, errors
|
||||
|
||||
|
|
@ -730,14 +748,20 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
if account and settings.MULTISESSION_MODE < 2:
|
||||
# Load the appropriate Character class
|
||||
character_typeclass = kwargs.get('character_typeclass', settings.BASE_CHARACTER_TYPECLASS)
|
||||
character_home = kwargs.get('home')
|
||||
character_typeclass = kwargs.get(
|
||||
"character_typeclass", settings.BASE_CHARACTER_TYPECLASS
|
||||
)
|
||||
character_home = kwargs.get("home")
|
||||
Character = class_from_module(character_typeclass)
|
||||
|
||||
# Create the character
|
||||
character, errs = Character.create(
|
||||
account.key, account, ip=ip, typeclass=character_typeclass,
|
||||
permissions=permissions, home=character_home
|
||||
account.key,
|
||||
account,
|
||||
ip=ip,
|
||||
typeclass=character_typeclass,
|
||||
permissions=permissions,
|
||||
home=character_home,
|
||||
)
|
||||
errors.extend(errs)
|
||||
|
||||
|
|
@ -758,7 +782,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
# Update the throttle to indicate a new account was created from this IP
|
||||
if ip and not guest:
|
||||
CREATION_THROTTLE.update(ip, 'Too many accounts being created.')
|
||||
CREATION_THROTTLE.update(ip, "Too many accounts being created.")
|
||||
SIGNAL_ACCOUNT_POST_CREATE.send(sender=account, ip=ip)
|
||||
return account, errors
|
||||
|
||||
|
|
@ -786,6 +810,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
self.nicks.clear()
|
||||
self.aliases.clear()
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
# methods inherited from database model
|
||||
|
||||
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
||||
|
|
@ -826,7 +851,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
kwargs["options"] = options
|
||||
|
||||
if text is not None:
|
||||
kwargs['text'] = to_str(text)
|
||||
kwargs["text"] = to_str(text)
|
||||
|
||||
# session relay
|
||||
sessions = make_iter(session) if session else self.sessions.all()
|
||||
|
|
@ -853,17 +878,29 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
commands at run-time.
|
||||
|
||||
"""
|
||||
raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_account=False)
|
||||
raw_string = self.nicks.nickreplace(
|
||||
raw_string, categories=("inputline", "channel"), include_account=False
|
||||
)
|
||||
if not session and _MULTISESSION_MODE in (0, 1):
|
||||
# for these modes we use the first/only session
|
||||
sessions = self.sessions.get()
|
||||
session = sessions[0] if sessions else None
|
||||
|
||||
return cmdhandler.cmdhandler(self, raw_string,
|
||||
callertype="account", session=session, **kwargs)
|
||||
return cmdhandler.cmdhandler(
|
||||
self, raw_string, callertype="account", session=session, **kwargs
|
||||
)
|
||||
|
||||
def search(self, searchdata, return_puppet=False, search_object=False,
|
||||
typeclass=None, nofound_string=None, multimatch_string=None, use_nicks=True, **kwargs):
|
||||
def search(
|
||||
self,
|
||||
searchdata,
|
||||
return_puppet=False,
|
||||
search_object=False,
|
||||
typeclass=None,
|
||||
nofound_string=None,
|
||||
multimatch_string=None,
|
||||
use_nicks=True,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
This is similar to `DefaultObject.search` but defaults to searching
|
||||
for Accounts only.
|
||||
|
|
@ -900,17 +937,25 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
# handle me, self and *me, *self
|
||||
if isinstance(searchdata, str):
|
||||
# handle wrapping of common terms
|
||||
if searchdata.lower() in ("me", "*me", "self", "*self",):
|
||||
if searchdata.lower() in ("me", "*me", "self", "*self"):
|
||||
return self
|
||||
if search_object:
|
||||
matches = ObjectDB.objects.object_search(searchdata, typeclass=typeclass, use_nicks=use_nicks)
|
||||
matches = ObjectDB.objects.object_search(
|
||||
searchdata, typeclass=typeclass, use_nicks=use_nicks
|
||||
)
|
||||
else:
|
||||
searchdata = self.nicks.nickreplace(searchdata, categories=("account", ), include_account=False)
|
||||
searchdata = self.nicks.nickreplace(
|
||||
searchdata, categories=("account",), include_account=False
|
||||
)
|
||||
|
||||
matches = AccountDB.objects.account_search(searchdata, typeclass=typeclass)
|
||||
matches = _AT_SEARCH_RESULT(matches, self, query=searchdata,
|
||||
nofound_string=nofound_string,
|
||||
multimatch_string=multimatch_string)
|
||||
matches = _AT_SEARCH_RESULT(
|
||||
matches,
|
||||
self,
|
||||
query=searchdata,
|
||||
nofound_string=nofound_string,
|
||||
multimatch_string=multimatch_string,
|
||||
)
|
||||
if matches and return_puppet:
|
||||
try:
|
||||
return matches.puppet
|
||||
|
|
@ -918,7 +963,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
return None
|
||||
return matches
|
||||
|
||||
def access(self, accessing_obj, access_type='read', default=False, no_superuser_bypass=False, **kwargs):
|
||||
def access(
|
||||
self, accessing_obj, access_type="read", default=False, no_superuser_bypass=False, **kwargs
|
||||
):
|
||||
"""
|
||||
Determines if another object has permission to access this
|
||||
object in whatever way.
|
||||
|
|
@ -938,8 +985,12 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
result (bool): Result of access check.
|
||||
|
||||
"""
|
||||
result = super().access(accessing_obj, access_type=access_type,
|
||||
default=default, no_superuser_bypass=no_superuser_bypass)
|
||||
result = super().access(
|
||||
accessing_obj,
|
||||
access_type=access_type,
|
||||
default=default,
|
||||
no_superuser_bypass=no_superuser_bypass,
|
||||
)
|
||||
self.at_access(result, accessing_obj, access_type, **kwargs)
|
||||
return result
|
||||
|
||||
|
|
@ -974,9 +1025,11 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
"""
|
||||
# A basic security setup
|
||||
lockstring = "examine:perm(Admin);edit:perm(Admin);" \
|
||||
"delete:perm(Admin);boot:perm(Admin);msg:all();" \
|
||||
"noidletimeout:perm(Builder) or perm(noidletimeout)"
|
||||
lockstring = (
|
||||
"examine:perm(Admin);edit:perm(Admin);"
|
||||
"delete:perm(Admin);boot:perm(Admin);msg:all();"
|
||||
"noidletimeout:perm(Builder) or perm(noidletimeout)"
|
||||
)
|
||||
self.locks.add(lockstring)
|
||||
|
||||
# The ooc account cmdset
|
||||
|
|
@ -991,8 +1044,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
"""
|
||||
# set an (empty) attribute holding the characters this account has
|
||||
lockstring = "attrread:perm(Admins);attredit:perm(Admins);" \
|
||||
"attrcreate:perm(Admins);"
|
||||
lockstring = "attrread:perm(Admins);attredit:perm(Admins);" "attrcreate:perm(Admins);"
|
||||
self.attributes.add("_playable_characters", [], lockstring=lockstring)
|
||||
self.attributes.add("_saved_protocol_flags", {}, lockstring=lockstring)
|
||||
|
||||
|
|
@ -1147,13 +1199,13 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
global _MUDINFO_CHANNEL
|
||||
if not _MUDINFO_CHANNEL:
|
||||
try:
|
||||
_MUDINFO_CHANNEL = ChannelDB.objects.filter(
|
||||
db_key=settings.CHANNEL_MUDINFO["key"])[0]
|
||||
_MUDINFO_CHANNEL = ChannelDB.objects.filter(db_key=settings.CHANNEL_MUDINFO["key"])[
|
||||
0
|
||||
]
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
now = timezone.now()
|
||||
now = "%02i-%02i-%02i(%02i:%02i)" % (now.year, now.month,
|
||||
now.day, now.hour, now.minute)
|
||||
now = "%02i-%02i-%02i(%02i:%02i)" % (now.year, now.month, now.day, now.hour, now.minute)
|
||||
if _MUDINFO_CHANNEL:
|
||||
_MUDINFO_CHANNEL.tempmsg(f"[{_MUDINFO_CHANNEL.key}, {now}]: {message}")
|
||||
else:
|
||||
|
|
@ -1206,8 +1258,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
# we make sure to clean up the _playable_characters list in case
|
||||
# any was deleted in the interim.
|
||||
self.db._playable_characters = [char for char in self.db._playable_characters if char]
|
||||
self.msg(self.at_look(target=self.db._playable_characters,
|
||||
session=session), session=session)
|
||||
self.msg(
|
||||
self.at_look(target=self.db._playable_characters, session=session), session=session
|
||||
)
|
||||
|
||||
def at_failed_login(self, session, **kwargs):
|
||||
"""
|
||||
|
|
@ -1355,19 +1408,27 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
result = [f"Account |g{self.key}|n (you are Out-of-Character)"]
|
||||
|
||||
nsess = len(sessions)
|
||||
result.append(nsess == 1 and
|
||||
"\n\n|wConnected session:|n" or
|
||||
f"\n\n|wConnected sessions ({nsess}):|n")
|
||||
result.append(
|
||||
nsess == 1
|
||||
and "\n\n|wConnected session:|n"
|
||||
or f"\n\n|wConnected sessions ({nsess}):|n"
|
||||
)
|
||||
for isess, sess in enumerate(sessions):
|
||||
csessid = sess.sessid
|
||||
addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and
|
||||
str(sess.address[0]) or
|
||||
str(sess.address))
|
||||
result.append("\n %s %s" % (
|
||||
session and
|
||||
session.sessid == csessid and
|
||||
"|w* %s|n" % (isess + 1) or
|
||||
" %s" % (isess + 1), addr))
|
||||
addr = "%s (%s)" % (
|
||||
sess.protocol_key,
|
||||
isinstance(sess.address, tuple) and str(sess.address[0]) or str(sess.address),
|
||||
)
|
||||
result.append(
|
||||
"\n %s %s"
|
||||
% (
|
||||
session
|
||||
and session.sessid == csessid
|
||||
and "|w* %s|n" % (isess + 1)
|
||||
or " %s" % (isess + 1),
|
||||
addr,
|
||||
)
|
||||
)
|
||||
result.append("\n\n |whelp|n - more commands")
|
||||
result.append("\n |wooc <Text>|n - talk on public channel")
|
||||
|
||||
|
|
@ -1375,19 +1436,30 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
|
||||
if is_su or len(characters) < charmax:
|
||||
if not characters:
|
||||
result.append("\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one.")
|
||||
result.append(
|
||||
"\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one."
|
||||
)
|
||||
else:
|
||||
result.append("\n |w@charcreate <name> [=description]|n - create new character")
|
||||
result.append("\n |w@chardelete <name>|n - delete a character (cannot be undone!)")
|
||||
result.append(
|
||||
"\n |w@chardelete <name>|n - delete a character (cannot be undone!)"
|
||||
)
|
||||
|
||||
if characters:
|
||||
string_s_ending = len(characters) > 1 and "s" or ""
|
||||
result.append("\n |w@ic <character>|n - enter the game (|w@ooc|n to get back here)")
|
||||
if is_su:
|
||||
result.append(f"\n\nAvailable character{string_s_ending} ({len(characters)}/unlimited):")
|
||||
result.append(
|
||||
f"\n\nAvailable character{string_s_ending} ({len(characters)}/unlimited):"
|
||||
)
|
||||
else:
|
||||
result.append("\n\nAvailable character%s%s:"
|
||||
% (string_s_ending, charmax > 1 and " (%i/%i)" % (len(characters), charmax) or ""))
|
||||
result.append(
|
||||
"\n\nAvailable character%s%s:"
|
||||
% (
|
||||
string_s_ending,
|
||||
charmax > 1 and " (%i/%i)" % (len(characters), charmax) or "",
|
||||
)
|
||||
)
|
||||
|
||||
for char in characters:
|
||||
csessions = char.sessions.all()
|
||||
|
|
@ -1396,9 +1468,13 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
|||
# character is already puppeted
|
||||
sid = sess in sessions and sessions.index(sess) + 1
|
||||
if sess and sid:
|
||||
result.append(f"\n - |G{char.key}|n [{', '.join(char.permissions.all())}] (played by you in session {sid})")
|
||||
result.append(
|
||||
f"\n - |G{char.key}|n [{', '.join(char.permissions.all())}] (played by you in session {sid})"
|
||||
)
|
||||
else:
|
||||
result.append(f"\n - |R{char.key}|n [{', '.join(char.permissions.all())}] (played by someone else)")
|
||||
result.append(
|
||||
f"\n - |R{char.key}|n [{', '.join(char.permissions.all())}] (played by someone else)"
|
||||
)
|
||||
else:
|
||||
# character is "free to puppet"
|
||||
result.append(f"\n - {char.key} [{', '.join(char.permissions.all())}]")
|
||||
|
|
@ -1437,11 +1513,11 @@ class DefaultGuest(DefaultAccount):
|
|||
errors = []
|
||||
account = None
|
||||
username = None
|
||||
ip = kwargs.get('ip', '').strip()
|
||||
ip = kwargs.get("ip", "").strip()
|
||||
|
||||
# check if guests are enabled.
|
||||
if not settings.GUEST_ENABLED:
|
||||
errors.append('Guest accounts are not enabled on this server.')
|
||||
errors.append("Guest accounts are not enabled on this server.")
|
||||
return None, errors
|
||||
|
||||
try:
|
||||
|
|
@ -1453,7 +1529,7 @@ class DefaultGuest(DefaultAccount):
|
|||
if not username:
|
||||
errors.append("All guest accounts are in use. Please try again later.")
|
||||
if ip:
|
||||
LOGIN_THROTTLE.update(ip, 'Too many requests for Guest access.')
|
||||
LOGIN_THROTTLE.update(ip, "Too many requests for Guest access.")
|
||||
return None, errors
|
||||
else:
|
||||
# build a new account with the found guest username
|
||||
|
|
|
|||
|
|
@ -18,34 +18,34 @@ class AccountDBChangeForm(UserChangeForm):
|
|||
Modify the accountdb class.
|
||||
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
model = AccountDB
|
||||
fields = '__all__'
|
||||
fields = "__all__"
|
||||
|
||||
username = forms.RegexField(
|
||||
label="Username",
|
||||
max_length=30,
|
||||
regex=r'^[\w. @+-]+$',
|
||||
widget=forms.TextInput(
|
||||
attrs={'size': '30'}),
|
||||
regex=r"^[\w. @+-]+$",
|
||||
widget=forms.TextInput(attrs={"size": "30"}),
|
||||
error_messages={
|
||||
'invalid': "This value may contain only letters, spaces, numbers "
|
||||
"and @/./+/-/_ characters."},
|
||||
help_text="30 characters or fewer. Letters, spaces, digits and "
|
||||
"@/./+/-/_ only.")
|
||||
"invalid": "This value may contain only letters, spaces, numbers "
|
||||
"and @/./+/-/_ characters."
|
||||
},
|
||||
help_text="30 characters or fewer. Letters, spaces, digits and " "@/./+/-/_ only.",
|
||||
)
|
||||
|
||||
def clean_username(self):
|
||||
"""
|
||||
Clean the username and check its existence.
|
||||
|
||||
"""
|
||||
username = self.cleaned_data['username']
|
||||
username = self.cleaned_data["username"]
|
||||
if username.upper() == self.instance.username.upper():
|
||||
return username
|
||||
elif AccountDB.objects.filter(username__iexact=username):
|
||||
raise forms.ValidationError('An account with that name '
|
||||
'already exists.')
|
||||
return self.cleaned_data['username']
|
||||
raise forms.ValidationError("An account with that name " "already exists.")
|
||||
return self.cleaned_data["username"]
|
||||
|
||||
|
||||
class AccountDBCreationForm(UserCreationForm):
|
||||
|
|
@ -55,28 +55,27 @@ class AccountDBCreationForm(UserCreationForm):
|
|||
|
||||
class Meta(object):
|
||||
model = AccountDB
|
||||
fields = '__all__'
|
||||
fields = "__all__"
|
||||
|
||||
username = forms.RegexField(
|
||||
label="Username",
|
||||
max_length=30,
|
||||
regex=r'^[\w. @+-]+$',
|
||||
widget=forms.TextInput(
|
||||
attrs={'size': '30'}),
|
||||
regex=r"^[\w. @+-]+$",
|
||||
widget=forms.TextInput(attrs={"size": "30"}),
|
||||
error_messages={
|
||||
'invalid': "This value may contain only letters, spaces, numbers "
|
||||
"and @/./+/-/_ characters."},
|
||||
help_text="30 characters or fewer. Letters, spaces, digits and "
|
||||
"@/./+/-/_ only.")
|
||||
"invalid": "This value may contain only letters, spaces, numbers "
|
||||
"and @/./+/-/_ characters."
|
||||
},
|
||||
help_text="30 characters or fewer. Letters, spaces, digits and " "@/./+/-/_ only.",
|
||||
)
|
||||
|
||||
def clean_username(self):
|
||||
"""
|
||||
Cleanup username.
|
||||
"""
|
||||
username = self.cleaned_data['username']
|
||||
username = self.cleaned_data["username"]
|
||||
if AccountDB.objects.filter(username__iexact=username):
|
||||
raise forms.ValidationError('An account with that name already '
|
||||
'exists.')
|
||||
raise forms.ValidationError("An account with that name already " "exists.")
|
||||
return username
|
||||
|
||||
|
||||
|
|
@ -85,61 +84,66 @@ class AccountForm(forms.ModelForm):
|
|||
Defines how to display Accounts
|
||||
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
model = AccountDB
|
||||
fields = '__all__'
|
||||
fields = "__all__"
|
||||
|
||||
db_key = forms.RegexField(
|
||||
label="Username",
|
||||
initial="AccountDummy",
|
||||
max_length=30,
|
||||
regex=r'^[\w. @+-]+$',
|
||||
regex=r"^[\w. @+-]+$",
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'size': '30'}),
|
||||
widget=forms.TextInput(attrs={"size": "30"}),
|
||||
error_messages={
|
||||
'invalid': "This value may contain only letters, spaces, numbers"
|
||||
" and @/./+/-/_ characters."},
|
||||
"invalid": "This value may contain only letters, spaces, numbers"
|
||||
" and @/./+/-/_ characters."
|
||||
},
|
||||
help_text="This should be the same as the connected Account's key "
|
||||
"name. 30 characters or fewer. Letters, spaces, digits and "
|
||||
"@/./+/-/_ only.")
|
||||
"name. 30 characters or fewer. Letters, spaces, digits and "
|
||||
"@/./+/-/_ only.",
|
||||
)
|
||||
|
||||
db_typeclass_path = forms.CharField(
|
||||
label="Typeclass",
|
||||
initial=settings.BASE_ACCOUNT_TYPECLASS,
|
||||
widget=forms.TextInput(
|
||||
attrs={'size': '78'}),
|
||||
widget=forms.TextInput(attrs={"size": "78"}),
|
||||
help_text="Required. Defines what 'type' of entity this is. This "
|
||||
"variable holds a Python path to a module with a valid "
|
||||
"Evennia Typeclass. Defaults to "
|
||||
"settings.BASE_ACCOUNT_TYPECLASS.")
|
||||
"variable holds a Python path to a module with a valid "
|
||||
"Evennia Typeclass. Defaults to "
|
||||
"settings.BASE_ACCOUNT_TYPECLASS.",
|
||||
)
|
||||
|
||||
db_permissions = forms.CharField(
|
||||
label="Permissions",
|
||||
initial=settings.PERMISSION_ACCOUNT_DEFAULT,
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={'size': '78'}),
|
||||
widget=forms.TextInput(attrs={"size": "78"}),
|
||||
help_text="In-game permissions. A comma-separated list of text "
|
||||
"strings checked by certain locks. They are often used for "
|
||||
"hierarchies, such as letting an Account have permission "
|
||||
"'Admin', 'Builder' etc. An Account permission can be "
|
||||
"overloaded by the permissions of a controlled Character. "
|
||||
"Normal accounts use 'Accounts' by default.")
|
||||
"strings checked by certain locks. They are often used for "
|
||||
"hierarchies, such as letting an Account have permission "
|
||||
"'Admin', 'Builder' etc. An Account permission can be "
|
||||
"overloaded by the permissions of a controlled Character. "
|
||||
"Normal accounts use 'Accounts' by default.",
|
||||
)
|
||||
|
||||
db_lock_storage = forms.CharField(
|
||||
label="Locks",
|
||||
widget=forms.Textarea(attrs={'cols': '100', 'rows': '2'}),
|
||||
widget=forms.Textarea(attrs={"cols": "100", "rows": "2"}),
|
||||
required=False,
|
||||
help_text="In-game lock definition string. If not given, defaults "
|
||||
"will be used. This string should be on the form "
|
||||
"<i>type:lockfunction(args);type2:lockfunction2(args);...")
|
||||
"will be used. This string should be on the form "
|
||||
"<i>type:lockfunction(args);type2:lockfunction2(args);...",
|
||||
)
|
||||
db_cmdset_storage = forms.CharField(
|
||||
label="cmdset",
|
||||
initial=settings.CMDSET_ACCOUNT,
|
||||
widget=forms.TextInput(attrs={'size': '78'}),
|
||||
widget=forms.TextInput(attrs={"size": "78"}),
|
||||
required=False,
|
||||
help_text="python path to account cmdset class (set in "
|
||||
"settings.CMDSET_ACCOUNT by default)")
|
||||
"settings.CMDSET_ACCOUNT by default)",
|
||||
)
|
||||
|
||||
|
||||
class AccountInline(admin.StackedInline):
|
||||
|
|
@ -147,19 +151,29 @@ class AccountInline(admin.StackedInline):
|
|||
Inline creation of Account
|
||||
|
||||
"""
|
||||
|
||||
model = AccountDB
|
||||
template = "admin/accounts/stacked.html"
|
||||
form = AccountForm
|
||||
fieldsets = (
|
||||
("In-game Permissions and Locks",
|
||||
{'fields': ('db_lock_storage',),
|
||||
#{'fields': ('db_permissions', 'db_lock_storage'),
|
||||
'description': "<i>These are permissions/locks for in-game use. "
|
||||
"They are unrelated to website access rights.</i>"}),
|
||||
("In-game Account data",
|
||||
{'fields': ('db_typeclass_path', 'db_cmdset_storage'),
|
||||
'description': "<i>These fields define in-game-specific properties "
|
||||
"for the Account object in-game.</i>"}))
|
||||
(
|
||||
"In-game Permissions and Locks",
|
||||
{
|
||||
"fields": ("db_lock_storage",),
|
||||
# {'fields': ('db_permissions', 'db_lock_storage'),
|
||||
"description": "<i>These are permissions/locks for in-game use. "
|
||||
"They are unrelated to website access rights.</i>",
|
||||
},
|
||||
),
|
||||
(
|
||||
"In-game Account data",
|
||||
{
|
||||
"fields": ("db_typeclass_path", "db_cmdset_storage"),
|
||||
"description": "<i>These fields define in-game-specific properties "
|
||||
"for the Account object in-game.</i>",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
extra = 1
|
||||
max_num = 1
|
||||
|
|
@ -170,6 +184,7 @@ class AccountTagInline(TagInline):
|
|||
Inline Account Tags.
|
||||
|
||||
"""
|
||||
|
||||
model = AccountDB.db_tags.through
|
||||
related_field = "accountdb"
|
||||
|
||||
|
|
@ -179,6 +194,7 @@ class AccountAttributeInline(AttributeInline):
|
|||
Inline Account Attributes.
|
||||
|
||||
"""
|
||||
|
||||
model = AccountDB.db_attributes.through
|
||||
related_field = "accountdb"
|
||||
|
||||
|
|
@ -189,30 +205,43 @@ class AccountDBAdmin(BaseUserAdmin):
|
|||
|
||||
"""
|
||||
|
||||
list_display = ('username', 'email', 'is_staff', 'is_superuser')
|
||||
list_display = ("username", "email", "is_staff", "is_superuser")
|
||||
form = AccountDBChangeForm
|
||||
add_form = AccountDBCreationForm
|
||||
inlines = [AccountTagInline, AccountAttributeInline]
|
||||
fieldsets = (
|
||||
(None, {'fields': ('username', 'password', 'email')}),
|
||||
('Website profile', {
|
||||
'fields': ('first_name', 'last_name'),
|
||||
'description': "<i>These are not used "
|
||||
"in the default system.</i>"}),
|
||||
('Website dates', {
|
||||
'fields': ('last_login', 'date_joined'),
|
||||
'description': '<i>Relevant only to the website.</i>'}),
|
||||
('Website Permissions', {
|
||||
'fields': ('is_active', 'is_staff', 'is_superuser',
|
||||
'user_permissions', 'groups'),
|
||||
'description': "<i>These are permissions/permission groups for "
|
||||
"accessing the admin site. They are unrelated to "
|
||||
"in-game access rights.</i>"}),
|
||||
('Game Options', {
|
||||
'fields': ('db_typeclass_path', 'db_cmdset_storage',
|
||||
'db_lock_storage'),
|
||||
'description': '<i>These are attributes that are more relevant '
|
||||
'to gameplay.</i>'}))
|
||||
(None, {"fields": ("username", "password", "email")}),
|
||||
(
|
||||
"Website profile",
|
||||
{
|
||||
"fields": ("first_name", "last_name"),
|
||||
"description": "<i>These are not used " "in the default system.</i>",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Website dates",
|
||||
{
|
||||
"fields": ("last_login", "date_joined"),
|
||||
"description": "<i>Relevant only to the website.</i>",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Website Permissions",
|
||||
{
|
||||
"fields": ("is_active", "is_staff", "is_superuser", "user_permissions", "groups"),
|
||||
"description": "<i>These are permissions/permission groups for "
|
||||
"accessing the admin site. They are unrelated to "
|
||||
"in-game access rights.</i>",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Game Options",
|
||||
{
|
||||
"fields": ("db_typeclass_path", "db_cmdset_storage", "db_lock_storage"),
|
||||
"description": "<i>These are attributes that are more relevant " "to gameplay.</i>",
|
||||
},
|
||||
),
|
||||
)
|
||||
# ('Game Options', {'fields': (
|
||||
# 'db_typeclass_path', 'db_cmdset_storage',
|
||||
# 'db_permissions', 'db_lock_storage'),
|
||||
|
|
@ -220,10 +249,15 @@ class AccountDBAdmin(BaseUserAdmin):
|
|||
# 'more relevant to gameplay.</i>'}))
|
||||
|
||||
add_fieldsets = (
|
||||
(None,
|
||||
{'fields': ('username', 'password1', 'password2', 'email'),
|
||||
'description': "<i>These account details are shared by the admin "
|
||||
"system and the game.</i>"},),)
|
||||
(
|
||||
None,
|
||||
{
|
||||
"fields": ("username", "password1", "password2", "email"),
|
||||
"description": "<i>These account details are shared by the admin "
|
||||
"system and the game.</i>",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
"""
|
||||
|
|
@ -246,6 +280,7 @@ class AccountDBAdmin(BaseUserAdmin):
|
|||
def response_add(self, request, obj, post_url_continue=None):
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
|
||||
return HttpResponseRedirect(reverse("admin:accounts_accountdb_change", args=[obj.id]))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ _SESSIONS = None
|
|||
|
||||
# Bot helper utilities
|
||||
|
||||
|
||||
class BotStarter(DefaultScript):
|
||||
"""
|
||||
This non-repeating script has the
|
||||
|
|
@ -80,6 +81,7 @@ class BotStarter(DefaultScript):
|
|||
"""
|
||||
self.db.started = False
|
||||
|
||||
|
||||
#
|
||||
# Bot base class
|
||||
|
||||
|
|
@ -100,8 +102,10 @@ class Bot(DefaultAccount):
|
|||
# the text encoding to use.
|
||||
self.db.encoding = "utf-8"
|
||||
# A basic security setup (also avoid idle disconnects)
|
||||
lockstring = "examine:perm(Admin);edit:perm(Admin);delete:perm(Admin);" \
|
||||
"boot:perm(Admin);msg:false();noidletimeout:true()"
|
||||
lockstring = (
|
||||
"examine:perm(Admin);edit:perm(Admin);delete:perm(Admin);"
|
||||
"boot:perm(Admin);msg:false();noidletimeout:true()"
|
||||
)
|
||||
self.locks.add(lockstring)
|
||||
# set the basics of being a bot
|
||||
script_key = str(self.key)
|
||||
|
|
@ -143,16 +147,25 @@ class Bot(DefaultAccount):
|
|||
|
||||
# IRC
|
||||
|
||||
|
||||
class IRCBot(Bot):
|
||||
"""
|
||||
Bot for handling IRC connections.
|
||||
|
||||
"""
|
||||
|
||||
# override this on a child class to use custom factory
|
||||
factory_path = "evennia.server.portal.irc.IRCBotFactory"
|
||||
|
||||
def start(self, ev_channel=None, irc_botname=None, irc_channel=None,
|
||||
irc_network=None, irc_port=None, irc_ssl=None):
|
||||
def start(
|
||||
self,
|
||||
ev_channel=None,
|
||||
irc_botname=None,
|
||||
irc_channel=None,
|
||||
irc_network=None,
|
||||
irc_port=None,
|
||||
irc_ssl=None,
|
||||
):
|
||||
"""
|
||||
Start by telling the portal to start a new session.
|
||||
|
||||
|
|
@ -202,12 +215,14 @@ class IRCBot(Bot):
|
|||
|
||||
# instruct the server and portal to create a new session with
|
||||
# the stored configuration
|
||||
configdict = {"uid": self.dbid,
|
||||
"botname": self.db.irc_botname,
|
||||
"channel": self.db.irc_channel,
|
||||
"network": self.db.irc_network,
|
||||
"port": self.db.irc_port,
|
||||
"ssl": self.db.irc_ssl}
|
||||
configdict = {
|
||||
"uid": self.dbid,
|
||||
"botname": self.db.irc_botname,
|
||||
"channel": self.db.irc_channel,
|
||||
"network": self.db.irc_network,
|
||||
"port": self.db.irc_port,
|
||||
"ssl": self.db.irc_ssl,
|
||||
}
|
||||
_SESSIONS.start_bot_session(self.factory_path, configdict)
|
||||
|
||||
def at_msg_send(self, **kwargs):
|
||||
|
|
@ -275,8 +290,11 @@ class IRCBot(Bot):
|
|||
# cache channel lookup
|
||||
self.ndb.ev_channel = self.db.ev_channel
|
||||
|
||||
if ("from_channel" in options and text and
|
||||
self.ndb.ev_channel.dbid == options["from_channel"]):
|
||||
if (
|
||||
"from_channel" in options
|
||||
and text
|
||||
and self.ndb.ev_channel.dbid == options["from_channel"]
|
||||
):
|
||||
if not from_obj or from_obj != [self]:
|
||||
super().msg(channel=text)
|
||||
|
||||
|
|
@ -338,9 +356,14 @@ class IRCBot(Bot):
|
|||
delta_cmd = t0 - sess.cmd_last_visible
|
||||
delta_conn = t0 - session.conn_time
|
||||
account = sess.get_account()
|
||||
whos.append("%s (%s/%s)" % (utils.crop("|w%s|n" % account.name, width=25),
|
||||
utils.time_format(delta_conn, 0),
|
||||
utils.time_format(delta_cmd, 1)))
|
||||
whos.append(
|
||||
"%s (%s/%s)"
|
||||
% (
|
||||
utils.crop("|w%s|n" % account.name, width=25),
|
||||
utils.time_format(delta_conn, 0),
|
||||
utils.time_format(delta_cmd, 1),
|
||||
)
|
||||
)
|
||||
text = f"Who list (online/idle): {', '.join(sorted(whos, key=lambda w: w.lower()))}"
|
||||
elif txt.lower().startswith("about"):
|
||||
# some bot info
|
||||
|
|
@ -364,6 +387,7 @@ class IRCBot(Bot):
|
|||
if self.ndb.ev_channel:
|
||||
self.ndb.ev_channel.msg(text, senders=self)
|
||||
|
||||
|
||||
#
|
||||
# RSS
|
||||
|
||||
|
|
@ -410,9 +434,7 @@ class RSSBot(Bot):
|
|||
self.db.rss_rate = rss_rate
|
||||
# instruct the server and portal to create a new session with
|
||||
# the stored configuration
|
||||
configdict = {"uid": self.dbid,
|
||||
"url": self.db.rss_url,
|
||||
"rate": self.db.rss_rate}
|
||||
configdict = {"uid": self.dbid, "url": self.db.rss_url, "rate": self.db.rss_rate}
|
||||
_SESSIONS.start_bot_session("evennia.server.portal.rss.RSSBotFactory", configdict)
|
||||
|
||||
def execute_cmd(self, txt=None, session=None, **kwargs):
|
||||
|
|
@ -437,12 +459,14 @@ class RSSBot(Bot):
|
|||
|
||||
# Grapevine bot
|
||||
|
||||
|
||||
class GrapevineBot(Bot):
|
||||
"""
|
||||
g Grapevine (https://grapevine.haus) relayer. The channel to connect to is the first
|
||||
name in the settings.GRAPEVINE_CHANNELS list.
|
||||
|
||||
"""
|
||||
|
||||
factory_path = "evennia.server.portal.grapevine.RestartingWebsocketServerFactory"
|
||||
|
||||
def start(self, ev_channel=None, grapevine_channel=None):
|
||||
|
|
@ -472,8 +496,7 @@ class GrapevineBot(Bot):
|
|||
self.db.grapevine_channel = grapevine_channel
|
||||
|
||||
# these will be made available as properties on the protocol factory
|
||||
configdict = {"uid": self.dbid,
|
||||
"grapevine_channel": self.db.grapevine_channel}
|
||||
configdict = {"uid": self.dbid, "grapevine_channel": self.db.grapevine_channel}
|
||||
|
||||
_SESSIONS.start_bot_session(self.factory_path, configdict)
|
||||
|
||||
|
|
@ -501,8 +524,11 @@ class GrapevineBot(Bot):
|
|||
# cache channel lookup
|
||||
self.ndb.ev_channel = self.db.ev_channel
|
||||
|
||||
if ("from_channel" in options and text and
|
||||
self.ndb.ev_channel.dbid == options["from_channel"]):
|
||||
if (
|
||||
"from_channel" in options
|
||||
and text
|
||||
and self.ndb.ev_channel.dbid == options["from_channel"]
|
||||
):
|
||||
if not from_obj or from_obj != [self]:
|
||||
# send outputfunc channel(msg, chan, sender)
|
||||
|
||||
|
|
@ -511,11 +537,25 @@ class GrapevineBot(Bot):
|
|||
# prefix since we pass that explicitly anyway
|
||||
prefix, text = text.split(":", 1)
|
||||
|
||||
super().msg(channel=(text.strip(), self.db.grapevine_channel,
|
||||
", ".join(obj.key for obj in from_obj), {}))
|
||||
super().msg(
|
||||
channel=(
|
||||
text.strip(),
|
||||
self.db.grapevine_channel,
|
||||
", ".join(obj.key for obj in from_obj),
|
||||
{},
|
||||
)
|
||||
)
|
||||
|
||||
def execute_cmd(self, txt=None, session=None, event=None, grapevine_channel=None,
|
||||
sender=None, game=None, **kwargs):
|
||||
def execute_cmd(
|
||||
self,
|
||||
txt=None,
|
||||
session=None,
|
||||
event=None,
|
||||
grapevine_channel=None,
|
||||
sender=None,
|
||||
game=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Take incoming data from protocol and send it to connected channel. This is
|
||||
triggered by the bot_data_in Inputfunc.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ The managers for the custom Account object and permissions.
|
|||
import datetime
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import UserManager
|
||||
from evennia.typeclasses.managers import (TypedObjectManager, TypeclassManager)
|
||||
from evennia.typeclasses.managers import TypedObjectManager, TypeclassManager
|
||||
|
||||
__all__ = ("AccountManager",)
|
||||
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ __all__ = ("AccountManager",)
|
|||
# Account Manager
|
||||
#
|
||||
|
||||
|
||||
class AccountDBManager(TypedObjectManager, UserManager):
|
||||
"""
|
||||
This AccountManager implements methods for searching
|
||||
|
|
@ -90,8 +92,7 @@ class AccountDBManager(TypedObjectManager, UserManager):
|
|||
end_date = timezone.now()
|
||||
tdelta = datetime.timedelta(days)
|
||||
start_date = end_date - tdelta
|
||||
return self.filter(last_login__range=(
|
||||
start_date, end_date)).order_by('-last_login')
|
||||
return self.filter(last_login__range=(start_date, end_date)).order_by("-last_login")
|
||||
|
||||
def get_account_from_email(self, uemail):
|
||||
"""
|
||||
|
|
@ -176,7 +177,8 @@ class AccountDBManager(TypedObjectManager, UserManager):
|
|||
# try alias match
|
||||
matches = self.filter(
|
||||
db_tags__db_tagtype__iexact="alias",
|
||||
**{"db_tags__db_key__iexact" if exact else "db_tags__db_key__icontains": ostring})
|
||||
**{"db_tags__db_key__iexact" if exact else "db_tags__db_key__icontains": ostring},
|
||||
)
|
||||
return matches
|
||||
|
||||
# back-compatibility alias
|
||||
|
|
|
|||
|
|
@ -8,42 +8,168 @@ import django.core.validators
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0001_initial'),
|
||||
('typeclasses', '0001_initial'),
|
||||
]
|
||||
dependencies = [("auth", "0001_initial"), ("typeclasses", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AccountDB',
|
||||
name="AccountDB",
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, max_length=30, verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')])),
|
||||
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
|
||||
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
|
||||
('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('db_key', models.CharField(max_length=255, verbose_name='key', db_index=True)),
|
||||
('db_typeclass_path', models.CharField(help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.", max_length=255, null=True, verbose_name='typeclass')),
|
||||
('db_date_created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
|
||||
('db_lock_storage', models.TextField(help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.", verbose_name='locks', blank=True)),
|
||||
('db_is_connected', models.BooleanField(default=False, help_text='If account is connected to game or not', verbose_name='is_connected')),
|
||||
('db_cmdset_storage', models.CharField(help_text='optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.', max_length=255, null=True, verbose_name='cmdset')),
|
||||
('db_is_bot', models.BooleanField(default=False, help_text='Used to identify irc/rss bots', verbose_name='is_bot')),
|
||||
('db_attributes', models.ManyToManyField(help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute', null=True)),
|
||||
('db_tags', models.ManyToManyField(help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag', null=True)),
|
||||
('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID", serialize=False, auto_created=True, primary_key=True
|
||||
),
|
||||
),
|
||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||
(
|
||||
"last_login",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="last login"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_superuser",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
||||
verbose_name="superuser status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
help_text="Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
unique=True,
|
||||
max_length=30,
|
||||
verbose_name="username",
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
"^[\\w.@+-]+$", "Enter a valid username.", "invalid"
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.CharField(max_length=30, verbose_name="first name", blank=True),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.CharField(max_length=30, verbose_name="last name", blank=True),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(max_length=75, verbose_name="email address", blank=True),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
),
|
||||
),
|
||||
("db_key", models.CharField(max_length=255, verbose_name="key", db_index=True)),
|
||||
(
|
||||
"db_typeclass_path",
|
||||
models.CharField(
|
||||
help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.",
|
||||
max_length=255,
|
||||
null=True,
|
||||
verbose_name="typeclass",
|
||||
),
|
||||
),
|
||||
(
|
||||
"db_date_created",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="creation date"),
|
||||
),
|
||||
(
|
||||
"db_lock_storage",
|
||||
models.TextField(
|
||||
help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.",
|
||||
verbose_name="locks",
|
||||
blank=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"db_is_connected",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="If account is connected to game or not",
|
||||
verbose_name="is_connected",
|
||||
),
|
||||
),
|
||||
(
|
||||
"db_cmdset_storage",
|
||||
models.CharField(
|
||||
help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.",
|
||||
max_length=255,
|
||||
null=True,
|
||||
verbose_name="cmdset",
|
||||
),
|
||||
),
|
||||
(
|
||||
"db_is_bot",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Used to identify irc/rss bots",
|
||||
verbose_name="is_bot",
|
||||
),
|
||||
),
|
||||
(
|
||||
"db_attributes",
|
||||
models.ManyToManyField(
|
||||
help_text="attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).",
|
||||
to="typeclasses.Attribute",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"db_tags",
|
||||
models.ManyToManyField(
|
||||
help_text="tags on this object. Tags are simple string markers to identify, group and alias objects.",
|
||||
to="typeclasses.Tag",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"groups",
|
||||
models.ManyToManyField(
|
||||
related_query_name="user",
|
||||
related_name="user_set",
|
||||
to="auth.Group",
|
||||
blank=True,
|
||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of his/her group.",
|
||||
verbose_name="groups",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_permissions",
|
||||
models.ManyToManyField(
|
||||
related_query_name="user",
|
||||
related_name="user_set",
|
||||
to="auth.Permission",
|
||||
blank=True,
|
||||
help_text="Specific permissions for this user.",
|
||||
verbose_name="user permissions",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Account',
|
||||
'verbose_name_plural': 'Accounts',
|
||||
},
|
||||
options={"verbose_name": "Account", "verbose_name_plural": "Accounts"},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ def convert_defaults(apps, schema_editor):
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0001_initial'),
|
||||
]
|
||||
dependencies = [("accounts", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(convert_defaults),
|
||||
]
|
||||
operations = [migrations.RunPython(convert_defaults)]
|
||||
|
|
|
|||
|
|
@ -6,31 +6,17 @@ from django.db import models, migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0002_move_defaults'),
|
||||
]
|
||||
dependencies = [("accounts", "0002_move_defaults")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DefaultAccount',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('accounts.accountdb',),
|
||||
name="DefaultAccount", fields=[], options={"proxy": True}, bases=("accounts.accountdb",)
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DefaultGuest',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('accounts.defaultaccount',),
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='accountdb',
|
||||
options={'verbose_name': 'Account'},
|
||||
name="DefaultGuest",
|
||||
fields=[],
|
||||
options={"proxy": True},
|
||||
bases=("accounts.defaultaccount",),
|
||||
),
|
||||
migrations.AlterModelOptions(name="accountdb", options={"verbose_name": "Account"}),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,41 +8,52 @@ import django.core.validators
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0003_auto_20150209_2234'),
|
||||
]
|
||||
dependencies = [("accounts", "0003_auto_20150209_2234")]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='DefaultGuest',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='DefaultAccount',
|
||||
),
|
||||
migrations.DeleteModel(name="DefaultGuest"),
|
||||
migrations.DeleteModel(name="DefaultAccount"),
|
||||
migrations.AlterModelManagers(
|
||||
name='accountdb',
|
||||
managers=[
|
||||
('objects', evennia.accounts.manager.AccountDBManager()),
|
||||
],
|
||||
name="accountdb", managers=[("objects", evennia.accounts.manager.AccountDBManager())]
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, verbose_name='email address', blank=True),
|
||||
model_name="accountdb",
|
||||
name="email",
|
||||
field=models.EmailField(max_length=254, verbose_name="email address", blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'),
|
||||
model_name="accountdb",
|
||||
name="groups",
|
||||
field=models.ManyToManyField(
|
||||
related_query_name="user",
|
||||
related_name="user_set",
|
||||
to="auth.Group",
|
||||
blank=True,
|
||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
||||
verbose_name="groups",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='last_login',
|
||||
field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
|
||||
model_name="accountdb",
|
||||
name="last_login",
|
||||
field=models.DateTimeField(null=True, verbose_name="last login", blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username'),
|
||||
model_name="accountdb",
|
||||
name="username",
|
||||
field=models.CharField(
|
||||
error_messages={"unique": "A user with that username already exists."},
|
||||
max_length=30,
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
"^[\\w.@+-]+$",
|
||||
"Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.",
|
||||
"invalid",
|
||||
)
|
||||
],
|
||||
help_text="Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
unique=True,
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,14 +8,24 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0004_auto_20150403_2339'),
|
||||
]
|
||||
dependencies = [("accounts", "0004_auto_20150403_2339")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username'),
|
||||
),
|
||||
model_name="accountdb",
|
||||
name="username",
|
||||
field=models.CharField(
|
||||
error_messages={"unique": "A user with that username already exists."},
|
||||
help_text="Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=30,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
"^[\\w.@+-]+$",
|
||||
"Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.",
|
||||
)
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,24 +8,35 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0005_auto_20160905_0902'),
|
||||
]
|
||||
dependencies = [("accounts", "0005_auto_20160905_0902")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_attributes',
|
||||
field=models.ManyToManyField(help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute'),
|
||||
model_name="accountdb",
|
||||
name="db_attributes",
|
||||
field=models.ManyToManyField(
|
||||
help_text="attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).",
|
||||
to="typeclasses.Attribute",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_tags',
|
||||
field=models.ManyToManyField(help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag'),
|
||||
model_name="accountdb",
|
||||
name="db_tags",
|
||||
field=models.ManyToManyField(
|
||||
help_text="tags on this object. Tags are simple string markers to identify, group and alias objects.",
|
||||
to="typeclasses.Tag",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.ASCIIUsernameValidator()], verbose_name='username'),
|
||||
model_name="accountdb",
|
||||
name="username",
|
||||
field=models.CharField(
|
||||
error_messages={"unique": "A user with that username already exists."},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[django.contrib.auth.validators.ASCIIUsernameValidator()],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,31 +8,33 @@ from django.db import migrations
|
|||
|
||||
def forwards(apps, schema_editor):
|
||||
try:
|
||||
PlayerDB = apps.get_model('players', 'PlayerDB')
|
||||
PlayerDB = apps.get_model("players", "PlayerDB")
|
||||
except LookupError:
|
||||
# playerdb not available. Skip.
|
||||
return
|
||||
|
||||
AccountDB = apps.get_model('accounts', 'AccountDB')
|
||||
AccountDB = apps.get_model("accounts", "AccountDB")
|
||||
for player in PlayerDB.objects.all():
|
||||
account = AccountDB(id=player.id,
|
||||
password=player.password,
|
||||
is_superuser=player.is_superuser,
|
||||
last_login=player.last_login,
|
||||
username=player.username,
|
||||
first_name=player.first_name,
|
||||
last_name=player.last_name,
|
||||
email=player.email,
|
||||
is_staff=player.is_staff,
|
||||
is_active=player.is_active,
|
||||
date_joined=player.date_joined,
|
||||
db_key=player.db_key,
|
||||
db_typeclass_path=player.db_typeclass_path,
|
||||
db_date_created=player.db_date_created,
|
||||
db_lock_storage=player.db_lock_storage,
|
||||
db_is_connected=player.db_is_connected,
|
||||
db_cmdset_storage=player.db_cmdset_storage,
|
||||
db_is_bot=player.db_is_bot)
|
||||
account = AccountDB(
|
||||
id=player.id,
|
||||
password=player.password,
|
||||
is_superuser=player.is_superuser,
|
||||
last_login=player.last_login,
|
||||
username=player.username,
|
||||
first_name=player.first_name,
|
||||
last_name=player.last_name,
|
||||
email=player.email,
|
||||
is_staff=player.is_staff,
|
||||
is_active=player.is_active,
|
||||
date_joined=player.date_joined,
|
||||
db_key=player.db_key,
|
||||
db_typeclass_path=player.db_typeclass_path,
|
||||
db_date_created=player.db_date_created,
|
||||
db_lock_storage=player.db_lock_storage,
|
||||
db_is_connected=player.db_is_connected,
|
||||
db_cmdset_storage=player.db_cmdset_storage,
|
||||
db_is_bot=player.db_is_bot,
|
||||
)
|
||||
account.save()
|
||||
for group in player.groups.all():
|
||||
account.groups.add(group)
|
||||
|
|
@ -46,13 +48,9 @@ def forwards(apps, schema_editor):
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0006_auto_20170606_1731'),
|
||||
]
|
||||
dependencies = [("accounts", "0006_auto_20170606_1731")]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forwards, migrations.RunPython.noop)
|
||||
]
|
||||
operations = [migrations.RunPython(forwards, migrations.RunPython.noop)]
|
||||
|
||||
if global_apps.is_installed('players'):
|
||||
dependencies.append(('players', '0006_auto_20170606_1731'))
|
||||
if global_apps.is_installed("players"):
|
||||
dependencies.append(("players", "0006_auto_20170606_1731"))
|
||||
|
|
|
|||
|
|
@ -8,65 +8,93 @@ import evennia.accounts.manager
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0007_copy_player_to_account'),
|
||||
]
|
||||
dependencies = [("accounts", "0007_copy_player_to_account")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name='accountdb',
|
||||
managers=[
|
||||
('objects', evennia.accounts.manager.AccountDBManager()),
|
||||
],
|
||||
name="accountdb", managers=[("objects", evennia.accounts.manager.AccountDBManager())]
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_attributes',
|
||||
field=models.ManyToManyField(help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute'),
|
||||
model_name="accountdb",
|
||||
name="db_attributes",
|
||||
field=models.ManyToManyField(
|
||||
help_text="attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).",
|
||||
to="typeclasses.Attribute",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_cmdset_storage',
|
||||
field=models.CharField(help_text='optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.', max_length=255, null=True, verbose_name='cmdset'),
|
||||
model_name="accountdb",
|
||||
name="db_cmdset_storage",
|
||||
field=models.CharField(
|
||||
help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.",
|
||||
max_length=255,
|
||||
null=True,
|
||||
verbose_name="cmdset",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_date_created',
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name='creation date'),
|
||||
model_name="accountdb",
|
||||
name="db_date_created",
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name="creation date"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_is_bot',
|
||||
field=models.BooleanField(default=False, help_text='Used to identify irc/rss bots', verbose_name='is_bot'),
|
||||
model_name="accountdb",
|
||||
name="db_is_bot",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="Used to identify irc/rss bots", verbose_name="is_bot"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_is_connected',
|
||||
field=models.BooleanField(default=False, help_text='If player is connected to game or not', verbose_name='is_connected'),
|
||||
model_name="accountdb",
|
||||
name="db_is_connected",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="If player is connected to game or not",
|
||||
verbose_name="is_connected",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_key',
|
||||
field=models.CharField(db_index=True, max_length=255, verbose_name='key'),
|
||||
model_name="accountdb",
|
||||
name="db_key",
|
||||
field=models.CharField(db_index=True, max_length=255, verbose_name="key"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_lock_storage',
|
||||
field=models.TextField(blank=True, help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.", verbose_name='locks'),
|
||||
model_name="accountdb",
|
||||
name="db_lock_storage",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.",
|
||||
verbose_name="locks",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_tags',
|
||||
field=models.ManyToManyField(help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag'),
|
||||
model_name="accountdb",
|
||||
name="db_tags",
|
||||
field=models.ManyToManyField(
|
||||
help_text="tags on this object. Tags are simple string markers to identify, group and alias objects.",
|
||||
to="typeclasses.Tag",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='db_typeclass_path',
|
||||
field=models.CharField(help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.", max_length=255, null=True, verbose_name='typeclass'),
|
||||
model_name="accountdb",
|
||||
name="db_typeclass_path",
|
||||
field=models.CharField(
|
||||
help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.",
|
||||
max_length=255,
|
||||
null=True,
|
||||
verbose_name="typeclass",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='accountdb',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
|
||||
model_name="accountdb",
|
||||
name="username",
|
||||
field=models.CharField(
|
||||
error_messages={"unique": "A user with that username already exists."},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ from evennia.server.signals import SIGNAL_ACCOUNT_POST_RENAME
|
|||
|
||||
__all__ = ("AccountDB",)
|
||||
|
||||
#_ME = _("me")
|
||||
#_SELF = _("self")
|
||||
# _ME = _("me")
|
||||
# _SELF = _("self")
|
||||
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
|
||||
|
|
@ -39,11 +39,12 @@ _DA = object.__delattr__
|
|||
_TYPECLASS = None
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
#
|
||||
# AccountDB
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
class AccountDB(TypedObject, AbstractUser):
|
||||
"""
|
||||
|
|
@ -83,14 +84,22 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
|
||||
# store a connected flag here too, not just in sessionhandler.
|
||||
# This makes it easier to track from various out-of-process locations
|
||||
db_is_connected = models.BooleanField(default=False,
|
||||
verbose_name="is_connected",
|
||||
help_text="If player is connected to game or not")
|
||||
db_is_connected = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="is_connected",
|
||||
help_text="If player is connected to game or not",
|
||||
)
|
||||
# database storage of persistant cmdsets.
|
||||
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True,
|
||||
help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.")
|
||||
db_cmdset_storage = models.CharField(
|
||||
"cmdset",
|
||||
max_length=255,
|
||||
null=True,
|
||||
help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.",
|
||||
)
|
||||
# marks if this is a "virtual" bot account object
|
||||
db_is_bot = models.BooleanField(default=False, verbose_name="is_bot", help_text="Used to identify irc/rss bots")
|
||||
db_is_bot = models.BooleanField(
|
||||
default=False, verbose_name="is_bot", help_text="Used to identify irc/rss bots"
|
||||
)
|
||||
|
||||
# Database manager
|
||||
objects = AccountDBManager()
|
||||
|
|
@ -101,20 +110,20 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
__applabel__ = "accounts"
|
||||
|
||||
class Meta(object):
|
||||
verbose_name = 'Account'
|
||||
verbose_name = "Account"
|
||||
|
||||
# cmdset_storage property
|
||||
# This seems very sensitive to caching, so leaving it be for now /Griatch
|
||||
#@property
|
||||
# @property
|
||||
def __cmdset_storage_get(self):
|
||||
"""
|
||||
Getter. Allows for value = self.name. Returns a list of cmdset_storage.
|
||||
"""
|
||||
storage = self.db_cmdset_storage
|
||||
# we need to check so storage is not None
|
||||
return [path.strip() for path in storage.split(',')] if storage else []
|
||||
return [path.strip() for path in storage.split(",")] if storage else []
|
||||
|
||||
#@cmdset_storage.setter
|
||||
# @cmdset_storage.setter
|
||||
def __cmdset_storage_set(self, value):
|
||||
"""
|
||||
Setter. Allows for self.name = value. Stores as a comma-separated
|
||||
|
|
@ -123,11 +132,12 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
_SA(self, "db_cmdset_storage", ",".join(str(val).strip() for val in make_iter(value)))
|
||||
_GA(self, "save")()
|
||||
|
||||
#@cmdset_storage.deleter
|
||||
# @cmdset_storage.deleter
|
||||
def __cmdset_storage_del(self):
|
||||
"Deleter. Allows for del self.name"
|
||||
_SA(self, "db_cmdset_storage", None)
|
||||
_GA(self, "save")()
|
||||
|
||||
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del)
|
||||
|
||||
#
|
||||
|
|
@ -140,7 +150,7 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
def __repr__(self):
|
||||
return f"{self.name}(account#{self.dbid})"
|
||||
|
||||
#@property
|
||||
# @property
|
||||
def __username_get(self):
|
||||
return self.username
|
||||
|
||||
|
|
@ -157,7 +167,7 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
name = property(__username_get, __username_set, __username_del)
|
||||
key = property(__username_get, __username_set, __username_del)
|
||||
|
||||
#@property
|
||||
# @property
|
||||
def __uid_get(self):
|
||||
"Getter. Retrieves the user id"
|
||||
return self.id
|
||||
|
|
@ -167,4 +177,5 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
|
||||
def __uid_del(self):
|
||||
raise Exception("User id cannot be deleted!")
|
||||
|
||||
uid = property(__uid_get, __uid_set, __uid_del)
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@ class TestAccountSessionHandler(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.account = create.create_account(
|
||||
f"TestAccount{randint(0, 999999)}", email="test@test.com",
|
||||
password="testpassword", typeclass=DefaultAccount)
|
||||
f"TestAccount{randint(0, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
self.handler = AccountSessionHandler(self.account)
|
||||
|
||||
def tearDown(self):
|
||||
if hasattr(self, 'account'):
|
||||
if hasattr(self, "account"):
|
||||
self.account.delete()
|
||||
|
||||
def test_get(self):
|
||||
|
|
@ -67,22 +70,24 @@ class TestAccountSessionHandler(TestCase):
|
|||
class TestDefaultGuest(EvenniaTest):
|
||||
"Check DefaultGuest class"
|
||||
|
||||
ip = '212.216.134.22'
|
||||
|
||||
ip = "212.216.134.22"
|
||||
|
||||
@override_settings(GUEST_ENABLED=False)
|
||||
def test_create_not_enabled(self):
|
||||
# Guest account should not be permitted
|
||||
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
||||
self.assertFalse(account, 'Guest account was created despite being disabled.')
|
||||
self.assertFalse(account, "Guest account was created despite being disabled.")
|
||||
|
||||
def test_authenticate(self):
|
||||
# Create a guest account
|
||||
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
||||
self.assertTrue(account, 'Guest account should have been created.')
|
||||
self.assertTrue(account, "Guest account should have been created.")
|
||||
|
||||
# Create a second guest account
|
||||
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
||||
self.assertFalse(account, 'Two guest accounts were created with a single entry on the guest list!')
|
||||
self.assertFalse(
|
||||
account, "Two guest accounts were created with a single entry on the guest list!"
|
||||
)
|
||||
|
||||
@patch("evennia.accounts.accounts.ChannelDB.objects.get_channel")
|
||||
def test_create(self, get_channel):
|
||||
|
|
@ -112,42 +117,52 @@ class TestDefaultGuest(EvenniaTest):
|
|||
|
||||
|
||||
class TestDefaultAccountAuth(EvenniaTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDefaultAccountAuth, self).setUp()
|
||||
|
||||
self.password = "testpassword"
|
||||
self.account.delete()
|
||||
self.account = create.create_account(f"TestAccount{randint(100000, 999999)}", email="test@test.com", password=self.password, typeclass=DefaultAccount)
|
||||
self.account = create.create_account(
|
||||
f"TestAccount{randint(100000, 999999)}",
|
||||
email="test@test.com",
|
||||
password=self.password,
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
|
||||
def test_authentication(self):
|
||||
"Confirm Account authentication method is authenticating/denying users."
|
||||
# Valid credentials
|
||||
obj, errors = DefaultAccount.authenticate(self.account.name, self.password)
|
||||
self.assertTrue(obj, 'Account did not authenticate given valid credentials.')
|
||||
self.assertTrue(obj, "Account did not authenticate given valid credentials.")
|
||||
|
||||
# Invalid credentials
|
||||
obj, errors = DefaultAccount.authenticate(self.account.name, 'xyzzy')
|
||||
self.assertFalse(obj, 'Account authenticated using invalid credentials.')
|
||||
obj, errors = DefaultAccount.authenticate(self.account.name, "xyzzy")
|
||||
self.assertFalse(obj, "Account authenticated using invalid credentials.")
|
||||
|
||||
def test_create(self):
|
||||
"Confirm Account creation is working as expected."
|
||||
# Create a normal account
|
||||
account, errors = DefaultAccount.create(username='ziggy', password='stardust11')
|
||||
self.assertTrue(account, 'New account should have been created.')
|
||||
account, errors = DefaultAccount.create(username="ziggy", password="stardust11")
|
||||
self.assertTrue(account, "New account should have been created.")
|
||||
|
||||
# Try creating a duplicate account
|
||||
account2, errors = DefaultAccount.create(username='Ziggy', password='starman11')
|
||||
self.assertFalse(account2, 'Duplicate account name should not have been allowed.')
|
||||
account2, errors = DefaultAccount.create(username="Ziggy", password="starman11")
|
||||
self.assertFalse(account2, "Duplicate account name should not have been allowed.")
|
||||
account.delete()
|
||||
|
||||
def test_throttle(self):
|
||||
"Confirm throttle activates on too many failures."
|
||||
for x in range(20):
|
||||
obj, errors = DefaultAccount.authenticate(self.account.name, 'xyzzy', ip='12.24.36.48')
|
||||
self.assertFalse(obj, 'Authentication was provided a bogus password; this should NOT have returned an account!')
|
||||
obj, errors = DefaultAccount.authenticate(self.account.name, "xyzzy", ip="12.24.36.48")
|
||||
self.assertFalse(
|
||||
obj,
|
||||
"Authentication was provided a bogus password; this should NOT have returned an account!",
|
||||
)
|
||||
|
||||
self.assertTrue('too many login failures' in errors[-1].lower(), 'Failed logins should have been throttled.')
|
||||
self.assertTrue(
|
||||
"too many login failures" in errors[-1].lower(),
|
||||
"Failed logins should have been throttled.",
|
||||
)
|
||||
|
||||
def test_username_validation(self):
|
||||
"Check username validators deny relevant usernames"
|
||||
|
|
@ -156,7 +171,7 @@ class TestDefaultAccountAuth(EvenniaTest):
|
|||
if not uses_database("mysql"):
|
||||
# TODO As of Mar 2019, mysql does not pass this test due to collation problems
|
||||
# that has not been possible to resolve
|
||||
result, error = DefaultAccount.validate_username('¯\_(ツ)_/¯')
|
||||
result, error = DefaultAccount.validate_username("¯\_(ツ)_/¯")
|
||||
self.assertFalse(result, "Validator allowed kanji in username.")
|
||||
|
||||
# Should not allow duplicate username
|
||||
|
|
@ -164,35 +179,44 @@ class TestDefaultAccountAuth(EvenniaTest):
|
|||
self.assertFalse(result, "Duplicate username should not have passed validation.")
|
||||
|
||||
# Should not allow username too short
|
||||
result, error = DefaultAccount.validate_username('xx')
|
||||
result, error = DefaultAccount.validate_username("xx")
|
||||
self.assertFalse(result, "2-character username passed validation.")
|
||||
|
||||
def test_password_validation(self):
|
||||
"Check password validators deny bad passwords"
|
||||
|
||||
account = create.create_account(f"TestAccount{randint(100000, 999999)}",
|
||||
email="test@test.com", password="testpassword", typeclass=DefaultAccount)
|
||||
for bad in ('', '123', 'password', 'TestAccount', '#', 'xyzzy'):
|
||||
account = create.create_account(
|
||||
f"TestAccount{randint(100000, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
for bad in ("", "123", "password", "TestAccount", "#", "xyzzy"):
|
||||
self.assertFalse(account.validate_password(bad, account=self.account)[0])
|
||||
|
||||
"Check validators allow sufficiently complex passwords"
|
||||
for better in ('Mxyzptlk', "j0hn, i'M 0n1y d4nc1nG"):
|
||||
for better in ("Mxyzptlk", "j0hn, i'M 0n1y d4nc1nG"):
|
||||
self.assertTrue(account.validate_password(better, account=self.account)[0])
|
||||
account.delete()
|
||||
|
||||
def test_password_change(self):
|
||||
"Check password setting and validation is working as expected"
|
||||
account = create.create_account(f"TestAccount{randint(100000, 999999)}",
|
||||
email="test@test.com", password="testpassword", typeclass=DefaultAccount)
|
||||
account = create.create_account(
|
||||
f"TestAccount{randint(100000, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
# Try setting some bad passwords
|
||||
for bad in ('', '#', 'TestAccount', 'password'):
|
||||
for bad in ("", "#", "TestAccount", "password"):
|
||||
valid, error = account.validate_password(bad, account)
|
||||
self.assertFalse(valid)
|
||||
|
||||
# Try setting a better password (test for False; returns None on success)
|
||||
self.assertFalse(account.set_password('Mxyzptlk'))
|
||||
self.assertFalse(account.set_password("Mxyzptlk"))
|
||||
account.delete()
|
||||
|
||||
|
||||
|
|
@ -228,8 +252,11 @@ class TestDefaultAccount(TestCase):
|
|||
import evennia.server.sessionhandler
|
||||
|
||||
account = create.create_account(
|
||||
f"TestAccount{randint(0, 999999)}", email="test@test.com",
|
||||
password="testpassword", typeclass=DefaultAccount)
|
||||
f"TestAccount{randint(0, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
self.s1.uid = account.uid
|
||||
evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1
|
||||
|
||||
|
|
@ -239,7 +266,9 @@ class TestDefaultAccount(TestCase):
|
|||
obj = Mock()
|
||||
self.s1.puppet = obj
|
||||
account.puppet_object(self.s1, obj)
|
||||
self.s1.data_out.assert_called_with(options=None, text="You are already puppeting this object.")
|
||||
self.s1.data_out.assert_called_with(
|
||||
options=None, text="You are already puppeting this object."
|
||||
)
|
||||
self.assertIsNone(obj.at_post_puppet.call_args)
|
||||
|
||||
def test_puppet_object_no_permission(self):
|
||||
|
|
@ -247,7 +276,12 @@ class TestDefaultAccount(TestCase):
|
|||
|
||||
import evennia.server.sessionhandler
|
||||
|
||||
account = create.create_account(f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount)
|
||||
account = create.create_account(
|
||||
f"TestAccount{randint(0, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
self.s1.uid = account.uid
|
||||
evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1
|
||||
|
||||
|
|
@ -257,7 +291,9 @@ class TestDefaultAccount(TestCase):
|
|||
|
||||
account.puppet_object(self.s1, obj)
|
||||
|
||||
self.assertTrue(self.s1.data_out.call_args[1]['text'].startswith("You don't have permission to puppet"))
|
||||
self.assertTrue(
|
||||
self.s1.data_out.call_args[1]["text"].startswith("You don't have permission to puppet")
|
||||
)
|
||||
self.assertIsNone(obj.at_post_puppet.call_args)
|
||||
|
||||
@override_settings(MULTISESSION_MODE=0)
|
||||
|
|
@ -266,7 +302,12 @@ class TestDefaultAccount(TestCase):
|
|||
|
||||
import evennia.server.sessionhandler
|
||||
|
||||
account = create.create_account(f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount)
|
||||
account = create.create_account(
|
||||
f"TestAccount{randint(0, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
self.s1.uid = account.uid
|
||||
evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1
|
||||
|
||||
|
|
@ -281,7 +322,9 @@ class TestDefaultAccount(TestCase):
|
|||
|
||||
account.puppet_object(self.s1, obj)
|
||||
# works because django.conf.settings.MULTISESSION_MODE is not in (1, 3)
|
||||
self.assertTrue(self.s1.data_out.call_args[1]['text'].endswith("from another of your sessions.|n"))
|
||||
self.assertTrue(
|
||||
self.s1.data_out.call_args[1]["text"].endswith("from another of your sessions.|n")
|
||||
)
|
||||
self.assertTrue(obj.at_post_puppet.call_args[1] == {})
|
||||
|
||||
def test_puppet_object_already_puppeted(self):
|
||||
|
|
@ -289,7 +332,12 @@ class TestDefaultAccount(TestCase):
|
|||
|
||||
import evennia.server.sessionhandler
|
||||
|
||||
account = create.create_account(f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount)
|
||||
account = create.create_account(
|
||||
f"TestAccount{randint(0, 999999)}",
|
||||
email="test@test.com",
|
||||
password="testpassword",
|
||||
typeclass=DefaultAccount,
|
||||
)
|
||||
self.account = account
|
||||
self.s1.uid = account.uid
|
||||
evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1
|
||||
|
|
@ -304,28 +352,33 @@ class TestDefaultAccount(TestCase):
|
|||
obj.at_post_puppet = Mock()
|
||||
|
||||
account.puppet_object(self.s1, obj)
|
||||
self.assertTrue(self.s1.data_out.call_args[1]['text'].endswith("is already puppeted by another Account."))
|
||||
self.assertTrue(
|
||||
self.s1.data_out.call_args[1]["text"].endswith(
|
||||
"is already puppeted by another Account."
|
||||
)
|
||||
)
|
||||
self.assertIsNone(obj.at_post_puppet.call_args)
|
||||
|
||||
|
||||
class TestAccountPuppetDeletion(EvenniaTest):
|
||||
|
||||
@override_settings(MULTISESSION_MODE=2)
|
||||
def test_puppet_deletion(self):
|
||||
# Check for existing chars
|
||||
self.assertFalse(self.account.db._playable_characters,
|
||||
'Account should not have any chars by default.')
|
||||
self.assertFalse(
|
||||
self.account.db._playable_characters, "Account should not have any chars by default."
|
||||
)
|
||||
|
||||
# Add char1 to account's playable characters
|
||||
self.account.db._playable_characters.append(self.char1)
|
||||
self.assertTrue(self.account.db._playable_characters,
|
||||
'Char was not added to account.')
|
||||
self.assertTrue(self.account.db._playable_characters, "Char was not added to account.")
|
||||
|
||||
# See what happens when we delete char1.
|
||||
self.char1.delete()
|
||||
# Playable char list should be empty.
|
||||
self.assertFalse(self.account.db._playable_characters,
|
||||
f'Playable character list is not empty! {self.account.db._playable_characters}')
|
||||
self.assertFalse(
|
||||
self.account.db._playable_characters,
|
||||
f"Playable character list is not empty! {self.account.db._playable_characters}",
|
||||
)
|
||||
|
||||
|
||||
class TestDefaultAccountEv(EvenniaTest):
|
||||
|
|
@ -333,6 +386,7 @@ class TestDefaultAccountEv(EvenniaTest):
|
|||
Testing using the EvenniaTest parent
|
||||
|
||||
"""
|
||||
|
||||
def test_characters_property(self):
|
||||
"test existence of None in _playable_characters Attr"
|
||||
self.account.db._playable_characters = [self.char1, None]
|
||||
|
|
@ -353,7 +407,9 @@ class TestDefaultAccountEv(EvenniaTest):
|
|||
self.assertEqual(idle, 10)
|
||||
|
||||
# test no sessions
|
||||
with patch("evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[]) as mock_sessh:
|
||||
with patch(
|
||||
"evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[]
|
||||
) as mock_sessh:
|
||||
idle = self.account.idle_time
|
||||
self.assertEqual(idle, None)
|
||||
|
||||
|
|
@ -364,18 +420,21 @@ class TestDefaultAccountEv(EvenniaTest):
|
|||
self.assertEqual(conn, 10)
|
||||
|
||||
# test no sessions
|
||||
with patch("evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[]) as mock_sessh:
|
||||
with patch(
|
||||
"evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[]
|
||||
) as mock_sessh:
|
||||
idle = self.account.connection_time
|
||||
self.assertEqual(idle, None)
|
||||
|
||||
def test_create_account(self):
|
||||
acct = create.account(
|
||||
"TestAccount3", "test@test.com", "testpassword123",
|
||||
"TestAccount3",
|
||||
"test@test.com",
|
||||
"testpassword123",
|
||||
locks="test:all()",
|
||||
tags=[("tag1", "category1"), ("tag2", "category2", "data1"), ("tag3", None)],
|
||||
attributes=[("key1", "value1", "category1",
|
||||
"edit:false()", True),
|
||||
("key2", "value2")])
|
||||
attributes=[("key1", "value1", "category1", "edit:false()", True), ("key2", "value2")],
|
||||
)
|
||||
acct.save()
|
||||
self.assertTrue(acct.pk)
|
||||
|
||||
|
|
@ -389,7 +448,7 @@ class TestDefaultAccountEv(EvenniaTest):
|
|||
ret = self.account.at_look(target=self.obj1, session=self.session)
|
||||
self.assertTrue("Obj" in ret)
|
||||
ret = self.account.at_look(target="Invalid", session=self.session)
|
||||
self.assertEqual(ret, 'Invalid has no in-game appearance.')
|
||||
self.assertEqual(ret, "Invalid has no in-game appearance.")
|
||||
|
||||
def test_msg(self):
|
||||
self.account.msg
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue