mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-25 20:52:26 -06:00
159 lines
5.1 KiB
Python
159 lines
5.1 KiB
Python
|
from direct.showbase import PythonUtil
|
||
|
|
||
|
class MagicError(Exception): pass
|
||
|
|
||
|
def ensureAccess(access, msg='Insufficient access'):
|
||
|
if spellbook.getInvokerAccess() < access:
|
||
|
raise MagicError(msg)
|
||
|
|
||
|
class Spellbook:
|
||
|
"""
|
||
|
The Spellbook manages the list of all Magic Words that have been registered
|
||
|
anywhere in the system. When the MagicWordManager(AI) wants to process a
|
||
|
Magic Word, it is passed off to the Spellbook, which performs the operation.
|
||
|
|
||
|
To add Magic Words to the Spellbook, use the @magicWord() decorator.
|
||
|
"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.words = {}
|
||
|
|
||
|
self.currentInvoker = None
|
||
|
self.currentTarget = None
|
||
|
|
||
|
def addWord(self, word):
|
||
|
self.words[word.name.lower()] = word # lets make this stuff case insensitive
|
||
|
|
||
|
def process(self, invoker, target, incantation):
|
||
|
self.currentInvoker = invoker
|
||
|
self.currentTarget = target
|
||
|
word, args = (incantation.split(' ', 1) + [''])[:2]
|
||
|
|
||
|
try:
|
||
|
return self.doWord(word, args)
|
||
|
except MagicError as e:
|
||
|
return e.message
|
||
|
except Exception:
|
||
|
return PythonUtil.describeException(backTrace=1)
|
||
|
finally:
|
||
|
self.currentInvoker = None
|
||
|
self.currentTarget = None
|
||
|
|
||
|
def doWord(self, wordName, args):
|
||
|
word = self.words.get(wordName.lower()) # look it up by its lower case value
|
||
|
|
||
|
if not word:
|
||
|
if process == 'ai':
|
||
|
wname = wordName.lower()
|
||
|
for key in self.words:
|
||
|
if self.words.get(key).access <= self.getInvokerAccess():
|
||
|
if wname in key:
|
||
|
return 'Did you mean %s' % (self.words.get(key).name)
|
||
|
if not word:
|
||
|
return
|
||
|
|
||
|
ensureAccess(word.access)
|
||
|
if self.getTarget() and self.getTarget() != self.getInvoker():
|
||
|
if self.getInvokerAccess() <= self.getTarget().getAdminAccess():
|
||
|
raise MagicError('Target must have lower access')
|
||
|
|
||
|
result = word.run(args)
|
||
|
if result is not None:
|
||
|
return str(result)
|
||
|
|
||
|
def getInvoker(self):
|
||
|
return self.currentInvoker
|
||
|
|
||
|
def getTarget(self):
|
||
|
return self.currentTarget
|
||
|
|
||
|
def getInvokerAccess(self):
|
||
|
if not self.currentInvoker:
|
||
|
return 0
|
||
|
return self.currentInvoker.getAdminAccess()
|
||
|
|
||
|
spellbook = Spellbook()
|
||
|
|
||
|
|
||
|
# CATEGORIES
|
||
|
class MagicWordCategory:
|
||
|
def __init__(self, name, defaultAccess=600):
|
||
|
self.name = name
|
||
|
self.defaultAccess = defaultAccess
|
||
|
|
||
|
CATEGORY_UNKNOWN = MagicWordCategory('Unknown')
|
||
|
CATEGORY_USER = MagicWordCategory('Community manager', defaultAccess=100)
|
||
|
CATEGORY_COMMUNITY_MANAGER = MagicWordCategory('Community manager', defaultAccess=200)
|
||
|
CATEGORY_MODERATOR = MagicWordCategory('Moderator', defaultAccess=300)
|
||
|
CATEGORY_CREATIVE = MagicWordCategory('Creative', defaultAccess=400)
|
||
|
CATEGORY_PROGRAMMER = MagicWordCategory('Programmer', defaultAccess=500)
|
||
|
CATEGORY_ADMINISTRATOR = MagicWordCategory('Administrator', defaultAccess=600)
|
||
|
CATEGORY_SYSTEM_ADMINISTRATOR = MagicWordCategory('System administrator', defaultAccess=700)
|
||
|
|
||
|
MINIMUM_MAGICWORD_ACCESS = CATEGORY_COMMUNITY_MANAGER.defaultAccess
|
||
|
|
||
|
|
||
|
class MagicWord:
|
||
|
def __init__(self, name, func, types, access, doc):
|
||
|
self.name = name
|
||
|
self.func = func
|
||
|
self.types = types
|
||
|
self.access = access
|
||
|
self.doc = doc
|
||
|
|
||
|
def parseArgs(self, string):
|
||
|
maxArgs = self.func.func_code.co_argcount
|
||
|
minArgs = maxArgs - (len(self.func.func_defaults) if self.func.func_defaults else 0)
|
||
|
|
||
|
args = string.split(None, maxArgs-1)[:maxArgs]
|
||
|
if len(args) < minArgs:
|
||
|
raise MagicError('Magic word %s requires at least %d arguments' % (self.name, minArgs))
|
||
|
|
||
|
output = []
|
||
|
for i, (type, arg) in enumerate(zip(self.types, args)):
|
||
|
try:
|
||
|
targ = type(arg)
|
||
|
except (TypeError, ValueError):
|
||
|
raise MagicError('Argument %d of magic word %s must be %s' % (i, self.name, type.__name__))
|
||
|
|
||
|
output.append(targ)
|
||
|
|
||
|
return output
|
||
|
|
||
|
def run(self, rawArgs):
|
||
|
args = self.parseArgs(rawArgs)
|
||
|
return self.func(*args)
|
||
|
|
||
|
|
||
|
class MagicWordDecorator:
|
||
|
"""
|
||
|
This class manages Magic Word decoration. It is aliased as magicWord, so that
|
||
|
the @magicWord(...) construct instantiates this class and has the resulting
|
||
|
object process the Magic Word's construction.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, name=None, types=[str], access=None, category=CATEGORY_UNKNOWN):
|
||
|
self.name = name
|
||
|
self.types = types
|
||
|
self.category = category
|
||
|
if access is not None:
|
||
|
self.access = access
|
||
|
else:
|
||
|
self.access = self.category.defaultAccess
|
||
|
|
||
|
def __call__(self, mw):
|
||
|
# This is the actual decoration routine. We add the function 'mw' as a
|
||
|
# Magic Word to the Spellbook, using the attributes specified at construction
|
||
|
# time.
|
||
|
|
||
|
name = self.name
|
||
|
if name is None:
|
||
|
name = mw.func_name
|
||
|
|
||
|
word = MagicWord(name, mw, self.types, self.access, mw.__doc__)
|
||
|
spellbook.addWord(word)
|
||
|
|
||
|
return mw
|
||
|
|
||
|
magicWord = MagicWordDecorator
|