pep.py/objects/osuToken.py

364 lines
9.0 KiB
Python
Raw Normal View History

2016-10-02 20:48:14 +00:00
import threading
import time
import uuid
from common.constants import gameModes, actions
from common.log import logUtils as log
from common.ripple import userUtils
2016-05-18 17:12:46 +00:00
from constants import serverPackets
from events import logoutEvent
from helpers import chatHelper as chat
2016-10-02 20:48:14 +00:00
from objects import glob
2016-04-19 17:40:59 +00:00
2016-09-02 15:45:10 +00:00
class token:
2016-09-04 08:56:10 +00:00
2016-09-02 15:45:10 +00:00
def __init__(self, userID, token_ = None, ip ="", irc = False, timeOffset = 0):
2016-04-19 17:40:59 +00:00
"""
Create a token object and set userID and token
userID -- user associated to this token
2016-06-10 11:15:42 +00:00
token -- if passed, set token to that value
2016-04-19 17:40:59 +00:00
if not passed, token will be generated
2016-06-10 11:15:42 +00:00
ip -- client ip. optional.
irc -- if True, set this token as IRC client. optional.
timeOffset -- the time offset from UTC for this user. optional.
2016-04-19 17:40:59 +00:00
"""
# Set stuff
self.userID = userID
2016-10-02 20:48:14 +00:00
self.username = userUtils.getUsername(self.userID)
self.privileges = userUtils.getPrivileges(self.userID)
self.admin = userUtils.isInPrivilegeGroup(self.userID, "developer") or userUtils.isInPrivilegeGroup(self.userID, "community manager")
self.irc = irc
2016-10-02 20:48:14 +00:00
self.restricted = userUtils.isRestricted(self.userID)
2016-04-19 17:40:59 +00:00
self.loginTime = int(time.time())
self.pingTime = self.loginTime
2016-08-01 18:38:26 +00:00
self.timeOffset = timeOffset
2016-06-10 11:15:42 +00:00
self.lock = threading.Lock() # Sync primitive
self.streams = []
2016-04-19 17:40:59 +00:00
# Default variables
self.spectators = []
self.spectating = 0
self.location = [0,0]
self.joinedChannels = []
2016-06-10 11:15:42 +00:00
self.ip = ip
self.country = 0
self.location = [0,0]
2016-04-19 17:40:59 +00:00
self.awayMessage = ""
self.matchID = -1
self.tillerino = [0,0,-1.0] # beatmap, mods, acc
self.silenceEndTime = 0
self.queue = bytes()
2016-04-19 17:40:59 +00:00
# Spam protection
self.spamRate = 0
# Stats cache
2016-09-02 15:16:22 +00:00
self.actionID = actions.IDLE
self.actionText = ""
self.actionMd5 = ""
self.actionMods = 0
2016-10-02 20:48:14 +00:00
self.gameMode = gameModes.STD
self.beatmapID = 0
self.rankedScore = 0
self.accuracy = 0.0
self.playcount = 0
self.totalScore = 0
self.gameRank = 0
self.pp = 0
2016-04-19 17:40:59 +00:00
# Generate/set token
2016-09-02 15:45:10 +00:00
if token_ is not None:
self.token = token_
2016-04-19 17:40:59 +00:00
else:
self.token = str(uuid.uuid4())
# Set stats
self.updateCachedStats()
2016-06-10 11:15:42 +00:00
# If we have a valid ip, save bancho session in DB so we can cache LETS logins
if ip != "":
2016-10-02 20:48:14 +00:00
userUtils.saveBanchoSession(self.userID, self.ip)
2016-06-10 11:15:42 +00:00
# Join main stream
self.joinStream("main")
2016-09-02 15:45:10 +00:00
def enqueue(self, bytes_):
2016-04-19 17:40:59 +00:00
"""
Add bytes (packets) to queue
bytes -- (packet) bytes to enqueue
2016-04-19 17:40:59 +00:00
"""
2016-09-02 15:45:10 +00:00
if not self.irc:
self.queue += bytes_
2016-04-19 17:40:59 +00:00
def resetQueue(self):
"""Resets the queue. Call when enqueued packets have been sent"""
self.queue = bytes()
def joinChannel(self, channel):
"""
Add channel to joined channels list
2016-04-19 17:40:59 +00:00
channel -- channel name
"""
if channel not in self.joinedChannels:
self.joinedChannels.append(channel)
2016-04-19 17:40:59 +00:00
def partChannel(self, channel):
"""
Remove channel from joined channels list
2016-04-19 17:40:59 +00:00
channel -- channel name
"""
if channel in self.joinedChannels:
self.joinedChannels.remove(channel)
2016-04-19 17:40:59 +00:00
def setLocation(self, location):
"""
Set location (latitude and longitude)
2016-04-19 17:40:59 +00:00
location -- [latitude, longitude]
"""
self.location = location
2016-04-19 17:40:59 +00:00
def getLatitude(self):
"""
Get latitude
2016-04-19 17:40:59 +00:00
return -- latitude
"""
2016-04-19 17:40:59 +00:00
return self.location[0]
def getLongitude(self):
"""
Get longitude
2016-04-19 17:40:59 +00:00
return -- longitude
"""
2016-04-19 17:40:59 +00:00
return self.location[1]
def startSpectating(self, userID):
"""
Set the spectating user to userID
2016-04-19 17:40:59 +00:00
userID -- target userID
"""
self.spectating = userID
2016-04-19 17:40:59 +00:00
def stopSpectating(self):
# Remove our userID from host's spectators
target = self.spectating
if self.spectating == 0:
return
targetToken = glob.tokens.getTokenFromUserID(target)
2016-09-02 15:45:10 +00:00
if targetToken is not None:
# Remove us from host's spectators list
targetToken.removeSpectator(self.userID)
# Send the spectator left packet to host
targetToken.enqueue(serverPackets.removeSpectator(self.userID))
for c in targetToken.spectators:
spec = glob.tokens.getTokenFromUserID(c)
spec.enqueue(serverPackets.fellowSpectatorLeft(self.userID))
# If nobody is spectating the host anymore, close #spectator channel
if len(targetToken.spectators) == 0:
chat.partChannel(token=targetToken, channel="#spect_{}".format(target), kick=True)
# Part #spectator channel
chat.partChannel(token=self, channel="#spect_{}".format(target), kick=True)
# Set our spectating user to 0
2016-04-19 17:40:59 +00:00
self.spectating = 0
# Console output
log.info("{} are no longer spectating {}".format(self.username, target))
2016-04-19 17:40:59 +00:00
def partMatch(self):
# Make sure we are in a match
if self.matchID == -1:
return
2016-04-19 17:40:59 +00:00
# Part #multiplayer channel
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True)
2016-04-19 17:40:59 +00:00
# Make sure the match exists
if self.matchID not in glob.matches.matches:
return
# The match exists, get object
match = glob.matches.matches[self.matchID]
# Set slot to free
match.userLeft(self.userID)
# Set usertoken match to -1
self.matchID = -1
def addSpectator(self, userID):
"""
Add userID to our spectators
userID -- new spectator userID
"""
2016-04-19 17:40:59 +00:00
# Add userID to spectators if not already in
if userID not in self.spectators:
self.spectators.append(userID)
2016-04-19 17:40:59 +00:00
def removeSpectator(self, userID):
"""
Remove userID from our spectators
2016-04-19 17:40:59 +00:00
userID -- old spectator userID
"""
2016-04-19 17:40:59 +00:00
# Remove spectator
if userID in self.spectators:
self.spectators.remove(userID)
2016-04-19 17:40:59 +00:00
def setCountry(self, countryID):
"""
Set country to countryID
2016-04-19 17:40:59 +00:00
countryID -- numeric country ID. See countryHelper.py
"""
self.country = countryID
2016-04-19 17:40:59 +00:00
def getCountry(self):
"""
Get numeric country ID
2016-04-19 17:40:59 +00:00
return -- numeric country ID. See countryHelper.py
"""
2016-04-19 17:40:59 +00:00
return self.country
def updatePingTime(self):
"""Update latest ping time"""
self.pingTime = int(time.time())
def setAwayMessage(self, __awayMessage):
"""Set a new away message"""
self.awayMessage = __awayMessage
def joinMatch(self, matchID):
2016-04-19 17:40:59 +00:00
"""
Set match to matchID
matchID -- new match ID
2016-04-19 17:40:59 +00:00
"""
self.matchID = matchID
2016-04-19 17:40:59 +00:00
def kick(self, message="You have been kicked from the server. Please login again."):
"""
Kick this user from the server
message -- Notification message to send to this user. Optional.
"""
2016-04-19 17:40:59 +00:00
# Send packet to target
log.info("{} has been disconnected. (kick)".format(self.username))
if message != "":
self.enqueue(serverPackets.notification(message))
2016-04-19 17:40:59 +00:00
self.enqueue(serverPackets.loginFailed())
# Logout event
logoutEvent.handle(self, None)
def silence(self, seconds, reason, author = 999):
"""
Silences this user (db, packet and token)
seconds -- silence length in seconds
reason -- silence reason
author -- userID of who has silenced the target. Optional. Default: 999 (fokabot)
"""
# Silence in db and token
self.silenceEndTime = int(time.time())+seconds
2016-10-02 20:48:14 +00:00
userUtils.silence(self.userID, seconds, reason, author)
# Send silence packet to target
self.enqueue(serverPackets.silenceEndTime(seconds))
# Send silenced packet to everyone else
glob.streams.broadcast("main", serverPackets.userSilenced(self.userID))
def spamProtection(self, increaseSpamRate = True):
"""
Silences the user if is spamming.
increaseSpamRate -- pass True if the user has sent a new message. Optional. Default: True
"""
2016-06-10 14:22:14 +00:00
# Increase the spam rate if needed
2016-09-02 15:45:10 +00:00
if increaseSpamRate:
2016-06-10 14:22:14 +00:00
self.spamRate += 1
# Silence the user if needed
if self.spamRate > 10:
self.silence(1800, "Spamming (auto spam protection)")
def isSilenced(self):
"""
Returns True if this user is silenced, otherwise False
return -- True/False
"""
return self.silenceEndTime-int(time.time()) > 0
def getSilenceSecondsLeft(self):
"""
Returns the seconds left for this user's silence
(0 if user is not silenced)
return -- silence seconds left
"""
return max(0, self.silenceEndTime-int(time.time()))
def updateCachedStats(self):
"""Update all cached stats for this token"""
2016-10-02 20:48:14 +00:00
stats = userUtils.getUserStats(self.userID, self.gameMode)
log.debug(str(stats))
2016-09-02 15:45:10 +00:00
if stats is None:
log.warning("Stats query returned None")
return
self.rankedScore = stats["rankedScore"]
self.accuracy = stats["accuracy"]/100
self.playcount = stats["playcount"]
self.totalScore = stats["totalScore"]
self.gameRank = stats["gameRank"]
self.pp = stats["pp"]
def checkRestricted(self, force=False):
"""
Check if this token is restricted. If so, send fokabot message
force -- If True, get restricted value from db.
If false, get the cached one. Optional. Default: False
"""
2016-09-02 15:45:10 +00:00
if force:
2016-10-02 20:48:14 +00:00
self.restricted = userUtils.isRestricted(self.userID)
2016-09-02 15:45:10 +00:00
if self.restricted:
self.setRestricted()
def setRestricted(self):
"""
Set this token as restricted, send FokaBot message to user
and send offline packet to everyone
"""
self.restricted = True
chat.sendMessage("FokaBot",self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
def joinStream(self, name):
glob.streams.join(name, self)
if name not in self.streams:
self.streams.append(name)
def leaveStream(self, name):
glob.streams.leave(name, self)
if name in self.streams:
self.streams.remove(name)
def leaveAllStreams(self):
for i in self.streams:
self.leaveStream(i)