622 lines
18 KiB
Python
622 lines
18 KiB
Python
from helpers import passwordHelper
|
|
from constants import gameModes
|
|
from helpers import generalFunctions
|
|
from objects import glob
|
|
from helpers import logHelper as log
|
|
import time
|
|
from constants import privileges
|
|
|
|
def getID(username):
|
|
"""
|
|
Get username's user ID
|
|
|
|
db -- database connection
|
|
username -- user
|
|
return -- user id or False
|
|
"""
|
|
|
|
# Get user ID from db
|
|
userID = glob.db.fetch("SELECT id FROM users WHERE username = %s", [username])
|
|
|
|
# Make sure the query returned something
|
|
if userID == None:
|
|
return False
|
|
|
|
# Return user ID
|
|
return userID["id"]
|
|
|
|
|
|
def checkLogin(userID, password):
|
|
"""
|
|
Check userID's login with specified password
|
|
|
|
db -- database connection
|
|
userID -- user id
|
|
password -- plain md5 password
|
|
return -- True or False
|
|
"""
|
|
|
|
# Get password data
|
|
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = %s", [userID])
|
|
|
|
# Make sure the query returned something
|
|
if passwordData == None:
|
|
return False
|
|
|
|
|
|
# Return valid/invalid based on the password version.
|
|
if passwordData["password_version"] == 2:
|
|
return passwordHelper.checkNewPassword(password, passwordData["password_md5"])
|
|
if passwordData["password_version"] == 1:
|
|
ok = passwordHelper.checkOldPassword(password, passwordData["salt"], passwordData["password_md5"])
|
|
if not ok: return False
|
|
newpass = passwordHelper.genBcrypt(password)
|
|
glob.db.execute("UPDATE users SET password_md5=%s, salt='', password_version='2' WHERE id = %s", [newpass, userID])
|
|
|
|
|
|
def exists(userID):
|
|
"""
|
|
Check if userID exists
|
|
|
|
userID -- user ID to check
|
|
return -- bool
|
|
"""
|
|
|
|
result = glob.db.fetch("SELECT id FROM users WHERE id = %s", [userID])
|
|
if result == None:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def getSilenceEnd(userID):
|
|
"""
|
|
Get userID's **ABSOLUTE** silence end UNIX time
|
|
Remember to subtract time.time() to get the actual silence time
|
|
|
|
userID -- userID
|
|
return -- UNIX time
|
|
"""
|
|
|
|
return glob.db.fetch("SELECT silence_end FROM users WHERE id = %s", [userID])["silence_end"]
|
|
|
|
|
|
def silence(userID, seconds, silenceReason, author = 999):
|
|
"""
|
|
Silence someone
|
|
|
|
userID -- userID
|
|
seconds -- silence length in seconds
|
|
silenceReason -- Silence reason shown on website
|
|
author -- userID of who silenced the user. Default: 999
|
|
"""
|
|
# db qurey
|
|
silenceEndTime = int(time.time())+seconds
|
|
glob.db.execute("UPDATE users SET silence_end = %s, silence_reason = %s WHERE id = %s", [silenceEndTime, silenceReason, userID])
|
|
|
|
# Loh
|
|
targetUsername = getUsername(userID)
|
|
# TODO: exists check im drunk rn i need to sleep (stampa piede ubriaco confirmed)
|
|
if seconds > 0:
|
|
log.rap(author, "has silenced {} for {} seconds for the following reason: \"{}\"".format(targetUsername, seconds, silenceReason), True)
|
|
else:
|
|
log.rap(author, "has removed {}'s silence".format(targetUsername), True)
|
|
|
|
def getRankedScore(userID, gameMode):
|
|
"""
|
|
Get userID's ranked score relative to gameMode
|
|
|
|
userID -- userID
|
|
gameMode -- int value, see gameModes
|
|
return -- ranked score
|
|
"""
|
|
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["ranked_score_"+modeForDB]
|
|
|
|
|
|
def getTotalScore(userID, gameMode):
|
|
"""
|
|
Get userID's total score relative to gameMode
|
|
|
|
userID -- userID
|
|
gameMode -- int value, see gameModes
|
|
return -- total score
|
|
"""
|
|
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["total_score_"+modeForDB]
|
|
|
|
|
|
def getAccuracy(userID, gameMode):
|
|
"""
|
|
Get userID's average accuracy relative to gameMode
|
|
|
|
userID -- userID
|
|
gameMode -- int value, see gameModes
|
|
return -- accuracy
|
|
"""
|
|
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["avg_accuracy_"+modeForDB]
|
|
|
|
|
|
def getGameRank(userID, gameMode):
|
|
"""
|
|
Get userID's **in-game rank** (eg: #1337) relative to gameMode
|
|
|
|
userID -- userID
|
|
gameMode -- int value, see gameModes
|
|
return -- game rank
|
|
"""
|
|
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = %s", [userID])
|
|
if result == None:
|
|
return 0
|
|
else:
|
|
return result["position"]
|
|
|
|
|
|
def getPlaycount(userID, gameMode):
|
|
"""
|
|
Get userID's playcount relative to gameMode
|
|
|
|
userID -- userID
|
|
gameMode -- int value, see gameModes
|
|
return -- playcount
|
|
"""
|
|
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["playcount_"+modeForDB]
|
|
|
|
|
|
def getUsername(userID):
|
|
"""
|
|
Get userID's username
|
|
|
|
userID -- userID
|
|
return -- username
|
|
"""
|
|
|
|
return glob.db.fetch("SELECT username FROM users WHERE id = %s", [userID])["username"]
|
|
|
|
|
|
def getFriendList(userID):
|
|
"""
|
|
Get userID's friendlist
|
|
|
|
userID -- userID
|
|
return -- list with friends userIDs. [0] if no friends.
|
|
"""
|
|
|
|
# Get friends from db
|
|
friends = glob.db.fetchAll("SELECT user2 FROM users_relationships WHERE user1 = %s", [userID])
|
|
|
|
if friends == None or len(friends) == 0:
|
|
# We have no friends, return 0 list
|
|
return [0]
|
|
else:
|
|
# Get only friends
|
|
friends = [i["user2"] for i in friends]
|
|
|
|
# Return friend IDs
|
|
return friends
|
|
|
|
|
|
def addFriend(userID, friendID):
|
|
"""
|
|
Add friendID to userID's friend list
|
|
|
|
userID -- user
|
|
friendID -- new friend
|
|
"""
|
|
|
|
# Make sure we aren't adding us to our friends
|
|
if userID == friendID:
|
|
return
|
|
|
|
# check user isn't already a friend of ours
|
|
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID]) != None:
|
|
return
|
|
|
|
# Set new value
|
|
glob.db.execute("INSERT INTO users_relationships (user1, user2) VALUES (%s, %s)", [userID, friendID])
|
|
|
|
|
|
def removeFriend(userID, friendID):
|
|
"""
|
|
Remove friendID from userID's friend list
|
|
|
|
userID -- user
|
|
friendID -- old friend
|
|
"""
|
|
|
|
# Delete user relationship. We don't need to check if the relationship was there, because who gives a shit,
|
|
# if they were not friends and they don't want to be anymore, be it. ¯\_(ツ)_/¯
|
|
glob.db.execute("DELETE FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID])
|
|
|
|
|
|
def getCountry(userID):
|
|
"""
|
|
Get userID's country **(two letters)**.
|
|
Use countryHelper.getCountryID with what that function returns
|
|
to get osu! country ID relative to that user
|
|
|
|
userID -- user
|
|
return -- country code (two letters)
|
|
"""
|
|
|
|
return glob.db.fetch("SELECT country FROM users_stats WHERE id = %s", [userID])["country"]
|
|
|
|
def getPP(userID, gameMode):
|
|
"""
|
|
Get userID's PP relative to gameMode
|
|
|
|
userID -- user
|
|
return -- PP
|
|
"""
|
|
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = %s".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
|
|
|
|
def setCountry(userID, country):
|
|
"""
|
|
Set userID's country (two letters)
|
|
|
|
userID -- userID
|
|
country -- country letters
|
|
"""
|
|
glob.db.execute("UPDATE users_stats SET country = %s WHERE id = %s", [country, userID])
|
|
|
|
def getShowCountry(userID):
|
|
"""
|
|
Get userID's show country status
|
|
|
|
userID -- userID
|
|
return -- True if country is shown, False if it's hidden
|
|
"""
|
|
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = %s", [userID])
|
|
if country == None:
|
|
return False
|
|
return generalFunctions.stringToBool(country)
|
|
|
|
def logIP(userID, ip):
|
|
"""
|
|
User IP log
|
|
USED FOR MULTIACCOUNT DETECTION
|
|
"""
|
|
glob.db.execute("""INSERT INTO ip_user (userid, ip, occurencies) VALUES (%s, %s, 1)
|
|
ON DUPLICATE KEY UPDATE occurencies = occurencies + 1""", [userID, ip])
|
|
|
|
def saveBanchoSession(userID, ip):
|
|
"""
|
|
Save userid and ip of this token in bancho_sessions table.
|
|
Used to cache logins on LETS requests
|
|
"""
|
|
log.debug("Saving bancho session ({}::{}) in db".format(userID, ip))
|
|
glob.db.execute("INSERT INTO bancho_sessions (id, userid, ip) VALUES (NULL, %s, %s)", [userID, ip])
|
|
|
|
def deleteBanchoSessions(userID, ip):
|
|
"""Delete this bancho session from DB"""
|
|
log.debug("Deleting bancho session ({}::{}) from db".format(userID, ip))
|
|
try:
|
|
glob.db.execute("DELETE FROM bancho_sessions WHERE userid = %s AND ip = %s", [userID, ip])
|
|
except:
|
|
log.warning("Token for user: {} ip: {} doesn't exist".format(userID, ip))
|
|
|
|
def is2FAEnabled(userID):
|
|
"""Returns True if 2FA is enable for this account"""
|
|
result = glob.db.fetch("SELECT id FROM 2fa_telegram WHERE userid = %s LIMIT 1", [userID])
|
|
return True if result is not None else False
|
|
|
|
def check2FA(userID, ip):
|
|
"""Returns True if this IP is untrusted"""
|
|
if is2FAEnabled(userID) == False:
|
|
return False
|
|
|
|
result = glob.db.fetch("SELECT id FROM ip_user WHERE userid = %s AND ip = %s", [userID, ip])
|
|
return True if result is None else False
|
|
|
|
def getUserStats(userID, gameMode):
|
|
"""
|
|
Get all user stats relative to gameMode with only two queries
|
|
|
|
userID --
|
|
gameMode -- gameMode number
|
|
return -- dictionary with results
|
|
"""
|
|
modeForDB = gameModes.getGameModeForDB(gameMode)
|
|
|
|
# Get stats
|
|
stats = glob.db.fetch("""SELECT
|
|
ranked_score_{gm} AS rankedScore,
|
|
avg_accuracy_{gm} AS accuracy,
|
|
playcount_{gm} AS playcount,
|
|
total_score_{gm} AS totalScore,
|
|
pp_{gm} AS pp
|
|
FROM users_stats WHERE id = %s LIMIT 1""".format(gm=modeForDB), [userID])
|
|
|
|
# Get game rank
|
|
result = glob.db.fetch("SELECT position FROM leaderboard_{} WHERE user = %s LIMIT 1".format(modeForDB), [userID])
|
|
if result == None:
|
|
stats["gameRank"] = 0
|
|
else:
|
|
stats["gameRank"] = result["position"]
|
|
|
|
# Return stats + game rank
|
|
return stats
|
|
|
|
def isAllowed(userID):
|
|
"""
|
|
Check if userID is not banned or restricted
|
|
|
|
userID -- id of the user
|
|
return -- True if not banned or restricted, otherwise false.
|
|
"""
|
|
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
|
if result != None:
|
|
return (result["privileges"] & privileges.USER_NORMAL) and (result["privileges"] & privileges.USER_PUBLIC)
|
|
else:
|
|
return False
|
|
|
|
def isRestricted(userID):
|
|
"""
|
|
Check if userID is restricted
|
|
|
|
userID -- id of the user
|
|
return -- True if not restricted, otherwise false.
|
|
"""
|
|
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
|
if result != None:
|
|
return (result["privileges"] & privileges.USER_NORMAL) and not (result["privileges"] & privileges.USER_PUBLIC)
|
|
else:
|
|
return False
|
|
|
|
def isBanned(userID):
|
|
"""
|
|
Check if userID is banned
|
|
|
|
userID -- id of the user
|
|
return -- True if not banned, otherwise false.
|
|
"""
|
|
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
|
if result != None:
|
|
return not (result["privileges"] & 3 > 0)
|
|
else:
|
|
return True
|
|
|
|
def ban(userID):
|
|
"""
|
|
Ban userID
|
|
|
|
userID -- id of user
|
|
"""
|
|
banDateTime = int(time.time())
|
|
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s", [ ~(privileges.USER_NORMAL | privileges.USER_PUBLIC | privileges.USER_PENDING_VERIFICATION) , banDateTime, userID])
|
|
|
|
def unban(userID):
|
|
"""
|
|
Unban userID
|
|
|
|
userID -- id of user
|
|
"""
|
|
glob.db.execute("UPDATE users SET privileges = privileges | %s, ban_datetime = 0 WHERE id = %s", [ (privileges.USER_NORMAL | privileges.USER_PUBLIC) , userID])
|
|
|
|
def restrict(userID):
|
|
"""
|
|
Put userID in restricted mode
|
|
|
|
userID -- id of user
|
|
"""
|
|
banDateTime = int(time.time())
|
|
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s", [~privileges.USER_PUBLIC, banDateTime, userID])
|
|
|
|
def unrestrict(userID):
|
|
"""
|
|
Remove restricted mode from userID.
|
|
Same as unban().
|
|
|
|
userID -- id of user
|
|
"""
|
|
unban(userID)
|
|
|
|
def getPrivileges(userID):
|
|
"""
|
|
Return privileges for userID
|
|
|
|
userID -- id of user
|
|
return -- privileges number
|
|
"""
|
|
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
|
if result != None:
|
|
return result["privileges"]
|
|
else:
|
|
return 0
|
|
|
|
def setPrivileges(userID, priv):
|
|
"""
|
|
Set userID's privileges in db
|
|
|
|
userID -- id of user
|
|
priv -- privileges number
|
|
"""
|
|
glob.db.execute("UPDATE users SET privileges = %s WHERE id = %s", [priv, userID])
|
|
|
|
def isInPrivilegeGroup(userID, groupName):
|
|
groupPrivileges = glob.db.fetch("SELECT privileges FROM privileges_groups WHERE name = %s", [groupName])
|
|
if groupPrivileges == None:
|
|
return False
|
|
groupPrivileges = groupPrivileges["privileges"]
|
|
userToken = glob.tokens.getTokenFromUserID(userID)
|
|
if userToken != None:
|
|
userPrivileges = userToken.privileges
|
|
else:
|
|
userPrivileges = getPrivileges(userID)
|
|
return (userPrivileges == groupPrivileges) or (userPrivileges == (groupPrivileges | privileges.USER_DONOR))
|
|
|
|
|
|
def appendNotes(userID, notes, addNl = True):
|
|
"""
|
|
Append "notes" to current userID's "notes for CM"
|
|
|
|
userID -- id of user
|
|
notes -- text to append
|
|
addNl -- if True, prepend \n to notes. Optional. Default: True.
|
|
"""
|
|
if addNl == True:
|
|
notes = "\n"+notes
|
|
glob.db.execute("UPDATE users SET notes=CONCAT(COALESCE(notes, ''),%s) WHERE id = %s", [notes, userID])
|
|
|
|
|
|
def logHardware(userID, hashes, activation = False):
|
|
"""
|
|
Hardware log
|
|
USED FOR MULTIACCOUNT DETECTION
|
|
|
|
Peppy's botnet (client data) structure (new line = "|", already split)
|
|
[0] osu! version
|
|
[1] plain mac addressed, separated by "."
|
|
[2] mac addresses hash set
|
|
[3] unique ID
|
|
[4] disk ID
|
|
|
|
return -- True if hw is not banned, otherwise false
|
|
"""
|
|
# Make sure the strings are not empty
|
|
for i in hashes[2:5]:
|
|
if i == "":
|
|
log.warning("Invalid hash set ({}) for user {} in HWID check".format(hashes, userID), "bunk")
|
|
return False
|
|
|
|
# Run some HWID checks on that user if he is not restricted
|
|
if isRestricted(userID) == False:
|
|
# Get username
|
|
username = getUsername(userID)
|
|
|
|
# Get the list of banned or restricted users that have logged in from this or similar HWID hash set
|
|
banned = glob.db.fetchAll("""SELECT users.id as userid, hw_user.occurencies, users.username FROM hw_user
|
|
LEFT JOIN users ON users.id = hw_user.userid
|
|
WHERE hw_user.userid != %(userid)s
|
|
AND (IF(%(mac)s!='b4ec3c4334a0249dae95c284ec5983df', hw_user.mac = %(mac)s, 1) AND hw_user.unique_id = %(uid)s AND hw_user.disk_id = %(diskid)s)
|
|
AND (users.privileges & 3 != 3)""", {
|
|
"userid": userID,
|
|
"mac": hashes[2],
|
|
"uid": hashes[3],
|
|
"diskid": hashes[4],
|
|
})
|
|
|
|
for i in banned:
|
|
# Get the total numbers of logins
|
|
total = glob.db.fetch("SELECT COUNT(*) AS count FROM hw_user WHERE userid = %s LIMIT 1", [userID])
|
|
# and make sure it is valid
|
|
if total == None:
|
|
continue
|
|
total = total["count"]
|
|
|
|
# Calculate 10% of total
|
|
perc = (total*10)/100
|
|
|
|
if i["occurencies"] >= perc:
|
|
# If the banned user has logged in more than 10% of the times from this user, restrict this user
|
|
restrict(userID)
|
|
appendNotes(userID, "-- Logged in from HWID ({hwid}) used more than 10% from user {banned} ({bannedUserID}), who is banned/restricted.".format(
|
|
hwid=hashes[2:5],
|
|
banned=i["username"],
|
|
bannedUserID=i["userid"]
|
|
))
|
|
log.warning("**{user}** ({userID}) has been restricted because he has logged in from HWID _({hwid})_ used more than 10% from banned/restricted user **{banned}** ({bannedUserID}), **possible multiaccount**.".format(
|
|
user=username,
|
|
userID=userID,
|
|
hwid=hashes[2:5],
|
|
banned=i["username"],
|
|
bannedUserID=i["userid"]
|
|
), "cm")
|
|
|
|
# Update hash set occurencies
|
|
glob.db.execute("""
|
|
INSERT INTO hw_user (id, userid, mac, unique_id, disk_id, occurencies) VALUES (NULL, %s, %s, %s, %s, 1)
|
|
ON DUPLICATE KEY UPDATE occurencies = occurencies + 1
|
|
""", [userID, hashes[2], hashes[3], hashes[4]])
|
|
|
|
# Optionally, set this hash as 'used for activation'
|
|
if activation == True:
|
|
glob.db.execute("UPDATE hw_user SET activated = 1 WHERE userid = %s AND mac = %s AND unique_id = %s AND disk_id = %s", [userID, hashes[2], hashes[3], hashes[4]])
|
|
|
|
# Access granted, abbiamo impiegato 3 giorni
|
|
# We grant access even in case of login from banned HWID
|
|
# because we call restrict() above so there's no need to deny the access.
|
|
return True
|
|
|
|
|
|
def resetPendingFlag(userID, success=True):
|
|
"""
|
|
Remove pending flag from an user.
|
|
|
|
userID -- ID of the user
|
|
success -- if True, set USER_PUBLIC and USER_NORMAL flags too
|
|
"""
|
|
glob.db.execute("UPDATE users SET privileges = privileges & %s WHERE id = %s LIMIT 1", [~privileges.USER_PENDING_VERIFICATION, userID])
|
|
if success == True:
|
|
glob.db.execute("UPDATE users SET privileges = privileges | %s WHERE id = %s LIMIT 1", [(privileges.USER_PUBLIC | privileges.USER_NORMAL), userID])
|
|
|
|
def verifyUser(userID, hashes):
|
|
# Check for valid hash set
|
|
for i in hashes[2:5]:
|
|
if i == "":
|
|
log.warning("Invalid hash set ({}) for user {} while verifying the account".format(str(hashes), userID), "bunk")
|
|
return False
|
|
|
|
# Get username
|
|
username = getUsername(userID)
|
|
|
|
# Make sure there are no other accounts activated with this exact mac/unique id/hwid
|
|
match = glob.db.fetchAll("SELECT userid FROM hw_user WHERE (IF(%(mac)s != 'b4ec3c4334a0249dae95c284ec5983df', mac = %(mac)s, 1) AND unique_id = %(uid)s AND disk_id = %(diskid)s) AND userid != %(userid)s AND activated = 1 LIMIT 1", {
|
|
"mac": hashes[2],
|
|
"uid": hashes[3],
|
|
"diskid": hashes[4],
|
|
"userid": userID
|
|
})
|
|
|
|
if match:
|
|
# This is a multiaccount, restrict other account and ban this account
|
|
|
|
# Get original userID and username (lowest ID)
|
|
originalUserID = match[0]["userid"]
|
|
originalUsername = getUsername(originalUserID)
|
|
|
|
# Ban this user and append notes
|
|
ban(userID) # this removes the USER_PENDING_VERIFICATION flag too
|
|
appendNotes(userID, "-- {}'s multiaccount ({}), found HWID match while verifying account ({})".format(originalUsername, originalUserID, hashes[2:5]))
|
|
appendNotes(originalUserID, "-- Has created multiaccount {} ({})".format(username, userID))
|
|
|
|
# Restrict the original
|
|
restrict(originalUserID)
|
|
|
|
# Discord message
|
|
log.warning("User **{originalUsername}** ({originalUserID}) has been restricted because he has created multiaccount **{username}** ({userID}). The multiaccount has been banned.".format(
|
|
originalUsername=originalUsername,
|
|
originalUserID=originalUserID,
|
|
username=username,
|
|
userID=userID
|
|
), "cm")
|
|
|
|
# Disallow login
|
|
return False
|
|
else:
|
|
# No matches found, set USER_PUBLIC and USER_NORMAL flags and reset USER_PENDING_VERIFICATION flag
|
|
resetPendingFlag(userID)
|
|
log.info("User **{}** ({}) has verified his account with hash set _{}_".format(username, userID, hashes[2:5]), "cm")
|
|
|
|
# Allow login
|
|
return True
|
|
|
|
def hasVerifiedHardware(userID):
|
|
"""
|
|
userID -- id of the user
|
|
return -- True if hwid activation data is in db, otherwise false
|
|
"""
|
|
data = glob.db.fetch("SELECT id FROM hw_user WHERE userid = %s AND activated = 1 LIMIT 1", [userID])
|
|
if data != None:
|
|
return True
|
|
return False
|