oldschool-toontown/otp/namepanel/NameCheck.py
Little Cat 1801d2b9fb
all: replace pandac.PandaModules imports.
UD/AI + Client boots up.
2022-12-16 20:40:57 -04:00

335 lines
11 KiB
Python

import string
from otp.otpbase import OTPLocalizer
from direct.directnotify import DirectNotifyGlobal
from panda3d.core import NSError
from panda3d.core import TextEncoder, TextNode
notify = DirectNotifyGlobal.directNotify.newCategory('NameCheck')
def filterString(str, filter):
result = ''
for char in str:
if char in filter:
result = result + char
return result
def justLetters(str):
letters = ''
for c in str:
if c.isalpha():
letters = letters + c
return letters
def justUpper(str):
upperCaseLetters = ''
for c in str:
if c.upper() != c.lower():
if c == c.upper():
upperCaseLetters = upperCaseLetters + c
return upperCaseLetters
def wordList(str):
words = str.split()
result = []
for word in words:
subWords = word.split('-')
for sw in subWords:
if sw:
result.append(sw)
return result
def checkName(name, otherCheckFuncs = [], font = None):
def longEnough(name):
if len(name) < 2:
notify.info('name is too short')
return OTPLocalizer.NCTooShort
def emptyName(name):
if name.strip() == '':
notify.info('name is empty')
return OTPLocalizer.NCTooShort
def printableChars(name):
for char in name:
if ord(char) < 128 and char not in string.printable:
notify.info('name contains non-printable char #%s' % ord(char))
return OTPLocalizer.NCGeneric
validAsciiChars = set(".,'-" + string.ascii_letters + string.whitespace)
def _validCharacter(c, validAsciiChars = validAsciiChars, font = font):
if c in validAsciiChars:
return True
if c.isalpha() or c.isspace():
return True
return False
def badCharacters(name, _validCharacter = _validCharacter):
for char in name:
if not _validCharacter(char):
if char in string.digits:
notify.info('name contains digits')
return OTPLocalizer.NCNoDigits
else:
notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(char))
return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(char)
def fontHasCharacters(name, font = font):
if font:
tn = TextNode('NameCheck')
tn.setFont(font)
for c in name:
if not tn.hasCharacter(str(c)):
notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(c))
return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(c)
def hasLetters(name):
words = wordList(name)
for word in words:
letters = justLetters(word)
if len(letters) == 0:
notify.info('word "%s" has no letters' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCNeedLetters
def hasVowels(name):
def perWord(word):
if '.' in word:
return None
for char in word:
if ord(char) >= 128:
return None
letters = filterString(word, string.ascii_letters)
if len(letters) > 2:
vowels = filterString(letters, 'aeiouyAEIOUY')
if len(vowels) == 0:
notify.info('word "%s" has no vowels' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCNeedVowels
return None
for word in wordList(name):
problem = perWord(word)
if problem:
return problem
def monoLetter(name):
def perWord(word):
word = word
letters = justLetters(word)
if len(letters) > 2:
letters = TextEncoder().decodeText(TextEncoder.lower(TextEncoder().encodeWtext(letters).decode('utf-8')).encode('utf-8'))
filtered = filterString(letters, letters[0])
if filtered == letters:
notify.info('word "%s" uses only one letter' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCGeneric
for word in wordList(name):
problem = perWord(word)
if problem:
return problem
def checkDashes(name):
def validDash(index, name=name):
if index == 0 or i == len(name)-1:
return 0
if not name[i-1].isalpha():
return 0
if not name[i+1].isalpha():
return 0
return 1
i=0
while 1:
i = name.find('-', i, len(name))
if i < 0:
return None
if not validDash(i):
notify.info('name makes invalid use of dashes')
return OTPLocalizer.NCDashUsage
i += 1
def checkCommas(name):
def validComma(index, name=name):
if index == 0 or i == len(name)-1:
return OTPLocalizer.NCCommaEdge
if name[i-1].isspace():
return OTPLocalizer.NCCommaAfterWord
if not name[i+1].isspace():
return OTPLocalizer.NCCommaUsage
return None
i=0
while 1:
i = name.find(',', i, len(name))
if i < 0:
return None
problem = validComma(i)
if problem:
notify.info('name makes invalid use of commas')
return problem
i += 1
def checkPeriods(name):
words = wordList(name)
for word in words:
if word[-1] == ',':
word = word[:-1]
numPeriods = word.count('.')
if not numPeriods:
continue
letters = justLetters(word)
numLetters = len(letters)
if word[-1] != '.':
notify.info('word "%s" does not end in a period' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCPeriodUsage
if numPeriods > 2:
notify.info('word "%s" has too many periods' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCPeriodUsage
if numPeriods == 2:
if not (word[1] == '.' and word[3] == '.'):
notify.info('word "%s" does not fit the J.T. pattern' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCPeriodUsage
return None
def checkApostrophes(name):
words = wordList(name)
for word in words:
numApos = word.count("'")
if numApos > 2:
notify.info('word "%s" has too many apostrophes.' % TextEncoder().encodeWtext(word))
return OTPLocalizer.NCApostrophes
numApos = name.count("'")
if numApos > 3:
notify.info('name has too many apostrophes.')
return OTPLocalizer.NCApostrophes
def tooManyWords(name):
if len(wordList(name)) > 4:
notify.info('name has too many words')
return OTPLocalizer.NCTooManyWords
def allCaps(name):
letters = justLetters(name)
if len(letters) > 2:
upperLetters = TextEncoder().decodeText(TextEncoder.upper(TextEncoder().encodeWtext(letters).decode('utf-8')).encode('utf-8'))
for i in range(len(upperLetters)):
if not upperLetters[0].isupper():
return
if upperLetters == letters:
notify.info('name is all caps')
return OTPLocalizer.NCAllCaps
def mixedCase(name):
words = wordList(name)
for word in words:
if len(word) > 2:
capitals = justUpper(word)
if len(capitals) > 2:
notify.info('name has mixed case')
return OTPLocalizer.NCMixedCase
def checkJapanese(name):
asciiSpace = list(range(32, 33))
asciiDigits = list(range(48, 64))
hiragana = list(range(12353, 12448))
katakana = list(range(12449, 12544))
halfwidthKatakana = list(range(65381, 65440))
halfwidthCharacter = set(asciiSpace + halfwidthKatakana)
allowedUtf8 = set(asciiSpace + hiragana + katakana + halfwidthKatakana)
te = TextEncoder()
dc = 0.0
for char in (ord(char) for char in te.decodeText(name)):
if char not in allowedUtf8:
if char in asciiDigits:
notify.info('name contains not allowed ascii digits')
return OTPLocalizer.NCNoDigits
else:
notify.info('name contains not allowed utf8 char: 0x%04x' % char)
return OTPLocalizer.NCBadCharacter % te.encodeWtext(chr(char))
elif char in halfwidthCharacter:
dc += 0.5
else:
dc += 1
if dc < 2:
notify.info('name is too short: %0.1f' % dc)
return OTPLocalizer.NCTooShort
elif dc > 8:
notify.info('name has been occupied more than eight display cells: %0.1f' % dc)
return OTPLocalizer.NCGeneric
def repeatedChars(name):
count = 1
lastChar = None
i = 0
while i < len(name):
char = name[i]
i += 1
if char == lastChar:
count += 1
else:
count = 1
lastChar = char
if count > 2:
notify.info('character %s is repeated too many times' % TextEncoder().encodeWtext(char))
return OTPLocalizer.NCRepeatedChar % TextEncoder().encodeWtext(char)
return
checks = [printableChars,
badCharacters,
fontHasCharacters,
longEnough,
emptyName,
hasLetters,
hasVowels,
monoLetter,
checkDashes,
checkCommas,
checkPeriods,
checkApostrophes,
tooManyWords,
allCaps,
mixedCase,
repeatedChars] + otherCheckFuncs
symmetricChecks = []
name = TextEncoder().decodeText(name.encode('utf-8'))
notify.info('checking name "%s"...' % TextEncoder().encodeWtext(name).decode('utf-8'))
for check in checks:
problem = check(name[:])
if not problem and check in symmetricChecks:
nName = name[:]
bName.reverse()
problem = check(bName)
print('problem = %s' % problem)
if problem:
return problem
return None
severity = notify.getSeverity()
notify.setSeverity(NSError)
for i in range(32):
pass
for c in '!"#$%&()*+/:;<=>?@[\\]^_`{|}~':
pass
notify.setSeverity(severity)
del severity