def readSocket(self): """ Read data coming from this client socket :return: """ try: # Try to read incoming data from socket data = self.socket.recv(2**10) log.debug("[IRC] [{}:{}] -> {}".format(self.ip, self.port, data)) quitmsg = "EOT" except socket.error as x: # Error while reading data, this client will be disconnected data = bytes() quitmsg = x if data: # Parse received data if needed self.__readbuffer += data.decode("latin_1") self.parseBuffer() self.__timestamp = time.time() self.__sentPing = False else: # No data, disconnect this socket self.disconnect(quitmsg)
def returnReplayFileName(scoreID=None, scoreData=None): if scoreID == None and scoreData == None: raise AttributeError( "Either scoreID or scoreData must be provided, not neither or both" ) if scoreID == None: scoreID = scoreData['id'] if scoreData == None: scoreData = glob.db.fetch( "SELECT scores.*, users.username FROM scores LEFT JOIN users ON scores.userid = users.id " "WHERE scores.id = %s", [scoreID]) try: username = scoreData["username"] beatmapData = glob.db.fetch( "SELECT beatmap_id, song_name FROM beatmaps WHERE beatmap_md5 = %s", [scoreData["beatmap_md5"]]) date = datetime.datetime.fromtimestamp(int( scoreData["time"])) - datetime.timedelta( microseconds=int(scoreData["time"]) / 10) fileName = "{} - {} [{}] ({})".format(username, beatmapData["song_name"], beatmapData['beatmap_id'], date.strftime("%Y-%m-%d")) except Exception as err: log.debug("ERROR WHEN: get replay fileName ({})".format(str(err))) fileName = scoreID return fileName
def cacheMap(mapFile, _beatmap): # Check if we have to download the .osu file download = False if not os.path.isfile(mapFile): # .osu file doesn't exist. We must download it download = True else: # File exists, check md5 if generalUtils.fileMd5(mapFile) != _beatmap.fileMD5 or not isBeatmap( mapFile): # MD5 don't match, redownload .osu file download = True # Download .osu file if needed if download: log.debug("maps ~> Downloading {} osu file".format(_beatmap.beatmapID)) # Get .osu file from osu servers fileContent = osuapiHelper.getOsuFileFromID(_beatmap.beatmapID) # Make sure osu servers returned something if fileContent is None or not isBeatmap(content=fileContent): raise exceptions.osuApiFailException("maps") # Delete old .osu file if it exists if os.path.isfile(mapFile): os.remove(mapFile) # Save .osu file with open(mapFile, "wb+") as f: f.write(fileContent) else: # Map file is already in folder log.debug("maps ~> Beatmap found in cache!")
def getRawReplayS3(scoreID): scoreID = int(scoreID) if not glob.conf.s3_enabled: log.warning("S3 is disabled! Using failed local") return _getRawReplayFailedLocal(scoreID) fileName = "replay_{}.osr".format(scoreID) log.debug("Downloading {} from s3".format(fileName)) with io.BytesIO() as f: bucket = s3.getReadReplayBucketName(scoreID) try: glob.threadScope.s3.download_fileobj(bucket, fileName, f) except ClientError as e: # 404 -> no such key # 400 -> no such bucket code = e.response["Error"]["Code"] if code in ("404", "400"): log.warning( "S3 replay returned {}, trying to get from failed replays". format(code)) if code == "400": sentry.captureMessage( "Invalid S3 replays bucket ({})! (got error 400)". format(bucket)) return _getRawReplayFailedLocal(scoreID) raise f.seek(0) return f.read()
def checkOppaiErrors(self): log.debug("oppai ~> Checking oppai errors...") err = pyoppai.err(self._oppai_ctx) if err: log.error(str(err)) raise OppaiError(err) log.debug("oppai ~> No errors!")
def __init__(self, r, handlers): """ Initialize a set of redis pubSub listeners :param r: redis instance (usually glob.redis) :param handlers: dictionary with the following structure: ``` { "redis_channel_name": handler, ... } ``` Where handler is: - An object of a class that inherits common.redis.generalPubSubHandler. You can create custom behaviors for your handlers by overwriting the `handle(self, data)` method, that will be called when that handler receives some data. - A function *object (not call)* that accepts one argument, that'll be the data received through the channel. This is useful if you want to make some simple handlers through a lambda, without having to create a class. """ threading.Thread.__init__(self) self.redis = r self.pubSub = self.redis.pubsub() self.handlers = handlers channels = [] for k, v in self.handlers.items(): channels.append(k) self.pubSub.subscribe(channels) log.debug("Subscribed to redis pubsub channels: {}".format(channels))
def updateCachedStats(self): """ Update all cached stats for this token :return: """ stats = userUtils.getUserStats(self.userID, self.gameMode) stats_relax = userUtils.getUserStatsRx(self.userID, self.gameMode) log.debug(str(stats)) if stats is None: log.warning("Stats query returned None") return if self.relaxing: self.gameRank = stats_relax["gameRank"] self.pp = stats_relax["pp"] self.rankedScore = stats_relax["rankedScore"] self.accuracy = stats_relax["accuracy"] / 100 self.playcount = stats_relax["playcount"] self.totalScore = stats_relax["totalScore"] else: self.gameRank = stats["gameRank"] self.pp = stats["pp"] self.rankedScore = stats["rankedScore"] self.accuracy = stats["accuracy"] / 100 self.playcount = stats["playcount"] self.totalScore = stats["totalScore"]
def set(self, userID, rank, fileMd5, country=False, friends=False, mods=-1, relax=False): """ Set userID's redis personal best cache :param userID: userID :param rank: leaderboard rank :param fileMd5: beatmap md5 :param country: True if country leaderboard, otherwise False :param friends: True if friends leaderboard, otherwise False :param mods: leaderboard mods :param relax: True if relax leaderboard, False if classic :return: """ glob.redis.set( "lets:personal_best_cache:{}".format(userID), "{}|{}|{}|{}|{}|{}".format(rank, fileMd5, country, friends, mods, relax), 1800) log.debug("personalBestCache set")
def updateCountry(userID, newScore, gameMode, mode='normal'): """ Update gamemode's country leaderboard. Doesn't do anything if userID is banned/restricted. :param userID: user, country is determined by the user :param newScore: new score or pp :param gameMode: gameMode number :return: """ if userUtils.isAllowed(userID): COUNTRYLESS = 'xx' if features.GLOBAL_COUNTRY_RANKS == 'clan': country = userUtils.getUserCountryClan(userID) else: country = userUtils.getCountry(userID) if country is not None and len( country) > 0 and country.lower() != COUNTRYLESS: log.debug("Updating {} country leaderboard...".format(country)) if features.RANKING_SCOREV2 and mode == 'alternative': k = "ripple:leaderboard_alt:{}:{}" elif mode == 'relax': k = "ripple:leaderboard_relax:{}:{}" else: k = "ripple:leaderboard:{}:{}" k = k.format(scoreUtils.readableGameMode(gameMode), country.lower()) glob.redis.zadd(k, str(userID), str(newScore)) else: log.debug( "Clan leaderboard update for user {} skipped (not allowed)".format( userID))
def osuApiRequest(request, params, getFirst=True): """ Send a request to osu!api. request -- request type, string (es: get_beatmaps) params -- GET parameters, without api key or trailing ?/& (es: h=a5b99395a42bd55bc5eb1d2411cbdf8b&limit=10) return -- dictionary with json response if success, None if failed or empty response. """ # Make sure osuapi is enabled if not generalUtils.stringToBool(glob.conf.config["osuapi"]["enable"]): log.warning("osu!api is disabled") return None # Api request resp = None try: finalURL = "{}/api/{}?k={}&{}".format(glob.conf.config["osuapi"]["apiurl"], request, glob.conf.config["osuapi"]["apikey"], params) log.debug(finalURL) resp = requests.get(finalURL, timeout=5).text data = json.loads(resp) if getFirst: if len(data) >= 1: resp = data[0] else: resp = None else: resp = data finally: glob.dog.increment(glob.DATADOG_PREFIX+".osu_api.requests") log.debug(str(resp).encode("utf-8")) return resp
def usersTimeoutCheckLoop(self): """ Start timed out users disconnect loop. This function will be called every `checkTime` seconds and so on, forever. CALL THIS FUNCTION ONLY ONCE! :return: """ log.debug("Checking timed out clients") timedOutTokens = [] # timed out users timeoutLimit = int(time.time()) - 100 for key, value in self.tokens.items(): # Check timeout (fokabot is ignored) if value.pingTime < timeoutLimit and value.userID != 999 and value.irc == False and value.tournament == False: # That user has timed out, add to disconnected tokens # We can't delete it while iterating or items() throws an error timedOutTokens.append(key) # Delete timed out users from self.tokens # i is token string (dictionary key) for i in timedOutTokens: log.debug("{} timed out!!".format(self.tokens[i].username)) self.tokens[i].enqueue( serverPackets.notification( "Your connection to the server timed out.")) logoutEvent.handle(self.tokens[i], None) del timedOutTokens # Schedule a new check (endless loop) threading.Timer(100, self.usersTimeoutCheckLoop).start()
def cleanupLoop(self): """ Start match cleanup loop. Empty matches that have been created more than 60 seconds ago will get deleted. Useful when people create useless lobbies with `!mp make`. The check is done every 30 seconds. This method starts an infinite loop, call it only once! :return: """ log.debug("Checking empty matches") t = int(time.time()) emptyMatches = [] # Collect all empty matches for key, m in self.matches.items(): if [x for x in m.slots if x.user is not None]: continue if t - m.createTime >= 120: log.debug("Match #{} marked for cleanup".format(m.matchID)) emptyMatches.append(m.matchID) # Dispose all empty matches for matchID in emptyMatches: self.disposeMatch(matchID) # Schedule a new check (endless loop) threading.Timer(30, self.cleanupLoop).start()
def get(self, userID, fileMd5, country=False, friends=False, mods=-1): """ Get cached personal best rank :param userID: userID :param fileMd5: beatmap md5 :param country: True if country leaderboard, otherwise False :param friends: True if friends leaderboard, otherwise False :param mods: leaderboard mods :return: 0 if cache miss, otherwise rank number """ try: # Make sure the value is in cache data = glob.redis.get("lets:personal_best_cache_relax:{}".format(userID)) if data is None: raise cacheMiss() # Unpack cached data data = data.decode("utf-8").split("|") cachedpersonalBestRankRX = int(data[0]) cachedfileMd5 = str(data[1]) cachedCountry = generalUtils.stringToBool(data[2]) cachedFriends = generalUtils.stringToBool(data[3]) cachedMods = int(data[4]) # Check if everything matches if fileMd5 != cachedfileMd5 or country != cachedCountry or friends != cachedFriends or mods != cachedMods: raise cacheMiss() # Cache hit log.debug("personalBestCacheRX hit") return cachedpersonalBestRankRX except cacheMiss: log.debug("personalBestCacheRX miss") return 0
def getRankInfo(userID, gameMode): """ Get userID's current rank, user above us and pp/score difference :param userID: user :param gameMode: gameMode number :return: {"nextUsername": "", "difference": 0, "currentRank": 0} """ data = {"nextUsername": "", "difference": 0, "currentRank": 0} k = "ripple:leaderboard_auto:{}".format( scoreUtils.readableGameMode(gameMode)) position = userUtils.getGameRankAP(userID, gameMode) - 1 log.debug("Our position is {}".format(position)) if position is not None and position > 0: aboveUs = glob.redis.zrevrange(k, position - 1, position) log.debug("{} is above us".format(aboveUs)) if aboveUs is not None and len(aboveUs) > 0 and aboveUs[0].isdigit(): # Get our rank, next rank username and pp/score difference myScore = glob.redis.zscore(k, userID) otherScore = glob.redis.zscore(k, aboveUs[0]) nextUsername = userUtils.getUsername(aboveUs[0]) if nextUsername is not None and myScore is not None and otherScore is not None: data["nextUsername"] = nextUsername data["difference"] = int(myScore) - int(otherScore) else: position = 0 data["currentRank"] = position + 1 return data
def handle(userToken, _=None, deleteToken=True): # get usertoken data userID = userToken.userID username = userToken.username requestToken = userToken.token # Big client meme here. If someone logs out and logs in right after, # the old logout packet will still be in the queue and will be sent to # the server, so we accept logout packets sent at least 5 seconds after login # if the user logs out before 5 seconds, he will be disconnected later with timeout check if int(time.time() - userToken.loginTime) >= 5 or userToken.irc: # Stop spectating userToken.stopSpectating() # Part matches userToken.leaveMatch() # Part all joined channels for i in userToken.joinedChannels: chat.partChannel(token=userToken, channel=i) # Leave all joined streams userToken.leaveAllStreams() # Enqueue our disconnection to everyone else glob.streams.broadcast("main", serverPackets.userLogout(userID)) # Disconnect from IRC if needed if userToken.irc and glob.irc: glob.ircServer.forceDisconnection(userToken.username) # Delete token if deleteToken: glob.tokens.deleteToken(requestToken) else: userToken.kicked = True # Change username if needed newUsername = glob.redis.get( "ripple:change_username_pending:{}".format(userID)) if newUsername is not None: log.debug( "Sending username change request for user {}".format(userID)) glob.redis.publish( "peppy:change_username", json.dumps({ "userID": userID, "newUsername": newUsername.decode("utf-8") })) # Console output url = glob.conf.extra["webhook"] embed = Webhook(url, color=123123) embed.set_author(name=username, icon='https://a.themansions.nl/u/{}', url='http://osu.themansions.nl/u/{}'.format( userID, userID)) embed.set_title(title='{} has logged out!'.format(username)) embed.post() log.info("{} has been disconnected. (logout)".format(username))
def __init__(self, beatmap_, score_=None, acc=None, mods_=None, tillerino=False, gameMode=gameModes.STD, tillerinoOnlyPP=False): """ Set peace params. beatmap_ -- beatmap object score_ -- score object acc -- manual acc. Used in tillerino-like bot. You don't need this if you pass __score object mods_ -- manual mods. Used in tillerino-like bot. You don't need this if you pass __score object tillerino -- If True, self.pp will be a list with pp values for 100%, 99%, 98% and 95% acc. Optional. """ # Default values self.pp = None self.score = None self.acc = 0 self.mods = mods.NOMOD self.combo = -1 # FC self.misses = 0 self.stars = 0 self.tillerino = tillerino self.tillerinoOnlyPP = tillerinoOnlyPP # Beatmap object self.beatmap = beatmap_ self.map = "{}.osu".format(self.beatmap.beatmapID) # If passed, set everything from score object if score_ is not None: self.score = score_ self.acc = self.score.accuracy * 100 self.mods = self.score.mods self.combo = self.score.maxCombo self.misses = self.score.cMiss self.gameMode = self.score.gameMode else: # Otherwise, set acc and mods from params (tillerino) self.acc = acc self.mods = mods_ if gameMode is not None: self.gameMode = gameMode elif self.beatmap.starsStd > 0: self.gameMode = gameModes.STD elif self.beatmap.starsTaiko > 0: self.gameMode = gameModes.TAIKO elif self.beatmap.starsCtb > 0: self.gameMode = gameModes.CTB elif self.beatmap.starsMania > 0: self.gameMode = gameModes.MANIA else: self.gameMode = None # Calculate pp log.debug("ezPeace ~> Initialized rust stuff diffcalc") self.calculatePP()
def cacheMap(mapFile, _beatmap): # Check if we have to download the .osu file download = shouldDownloadMap(mapFile, _beatmap) # Download .osu file if needed if download: log.debug("maps ~> Downloading {} osu file".format(_beatmap.beatmapId)) # Get .osu file from osu servers fileContent = osuapiHelper.getOsuFileFromID(_beatmap.beatmapId) # Make sure osu servers returned something if fileContent is None or not isBeatmap(content=fileContent): raise exceptions.osuApiFailException("maps") # Delete old .osu file if it exists if os.path.isfile(mapFile): os.remove(mapFile) # Save .osu file with open(mapFile, "wb+") as f: f.write(fileContent) else: # Map file is already in folder log.debug("maps ~> Beatmap found in cache!")
def asyncPost(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["sid", "refresh"]): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get beatmap set data from osu api beatmapSetID = self.get_argument("sid") refresh = int(self.get_argument("refresh")) if refresh == 1: log.debug("Forced refresh") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "s={}".format(beatmapSetID), False) if len(apiResponse) == 0: raise exceptions.invalidBeatmapException # Loop through all beatmaps in this set and save them in db data["maps"] = [] for i in apiResponse: log.debug("Saving beatmap {} in db".format(i["file_md5"])) bmap = beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=refresh) pp = glob.db.fetch( "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1", [bmap.beatmapID]) if pp is None: pp = 0 else: pp = pp["pp_100"] data["maps"].append({ "id": bmap.beatmapID, "name": bmap.songName, "status": bmap.rankedStatus, "frozen": bmap.rankedStatusFrozen, "pp": pp, }) # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found from osu!api." finally: # Add status code to data data["status"] = statusCode # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") #self.add_header("Access-Control-Allow-Origin", "*") self.set_status(statusCode)
def reload(self): """ Reloads the AQL thresholds configuration from DB :return: """ log.debug("Reloading AQL thresholds") self._thresholds = {} for x in glob.db.fetchAll( "SELECT `name`, value_string FROM system_settings WHERE `name` LIKE 'aql\_threshold\_%%'" ): parts = x["name"].split("aql_threshold_") if len(parts) < 1: continue m = gameModes.getGameModeFromDB(parts[1]) if m is None: continue try: self._thresholds[m] = float(x["value_string"]) except ValueError: continue log.debug([(gameModes.getGameModeForDB(x), self[x]) for x in self]) if not all(x in self._thresholds for x in range(gameModes.STD, gameModes.MANIA)): raise RuntimeError( "Invalid AQL thresholds. Please check your system_settings table." )
def handle(userToken, _=None, deleteToken=True): # get usertoken data userID = userToken.userID username = userToken.username requestToken = userToken.token # Big client meme here. If someone logs out and logs in right after, # the old logout packet will still be in the queue and will be sent to # the server, so we accept logout packets sent at least 5 seconds after login # if the user logs out before 5 seconds, he will be disconnected later with timeout check if int(time.time() - userToken.loginTime) >= 5 or userToken.irc: # Stop spectating userToken.stopSpectating() # Part matches userToken.leaveMatch() # Check if a users login/logouts are being tracked. If so, log to discord tracked = userUtils.getUserTracked(userID) if tracked: log.cmyui( 'Tracked user {} ({}) has logged out.'.format( username, userID), 'cm') # Part all joined channels for i in userToken.joinedChannels: chat.partChannel(token=userToken, channel=i) # Leave all joined streams userToken.leaveAllStreams() # Enqueue our disconnection to everyone else glob.streams.broadcast("main", serverPackets.userLogout(userID)) # Disconnect from IRC if needed if userToken.irc and glob.irc: glob.ircServer.forceDisconnection(userToken.username) # Delete token if deleteToken: glob.tokens.deleteToken(requestToken) else: userToken.kicked = True # Change username if needed newUsername = glob.redis.get( "ripple:change_username_pending:{}".format(userID)) if newUsername is not None: log.debug( "Sending username change request for user {}".format(userID)) glob.redis.publish( "peppy:change_username", json.dumps({ "userID": userID, "newUsername": newUsername.decode("utf-8") })) # Console output log.info("{} has been disconnected. (logout)".format(username))
def asyncPost(self): statusCode = 200 data = {"response": "empty", "status": 200} # Debug output log.debug(str(data)) # Send response #self.clear() self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def getReadReplayBucketName(scoreID): r = objects.glob.db.fetch( "SELECT `name`, max_score_id FROM s3_replay_buckets WHERE max_score_id IS NOT NULL " "ORDER BY abs(max_score_id - %s) LIMIT 1", (scoreID, )) if r is not None and scoreID <= r["max_score_id"]: log.debug("s3 replay buckets resolve: {} -> {}".format( scoreID, r["name"])) return r["name"] log.debug("s3 replay buckets resolve: {} -> WRITE BUCKET".format(scoreID)) return getWriteReplayBucketName()
def handle(userToken, packetData): # get token data userID = userToken.userID # Send spectator frames to every spectator streamName = "spect/{}".format(userID) glob.streams.broadcast(streamName, serverPackets.spectatorFrames(packetData[7:])) log.debug("Broadcasting {}'s frames to {} clients.".format( userID, len(glob.streams.streams[streamName].clients)))
def printArguments(t): """ Print passed arguments, for debug purposes :param t: tornado object (self) """ msg = "ARGS::" for i in t.request.arguments: msg += "{}={}\r\n".format(i, t.get_argument(i)) log.debug(msg)
def update(userID, newScore, gameMode): """ Update gamemode's leaderboard the leaderboard userID -- newScore -- new score or pp gameMode -- gameMode number """ log.debug("Updating leaderboard...") mode = scoreUtils.readableGameMode(gameMode) newPlayer = False us = glob.db.fetch("SELECT * FROM leaderboard_{} WHERE user=%s LIMIT 1".format(mode), [userID]) if us is None: newPlayer = True # Find player who is right below our score target = glob.db.fetch("SELECT * FROM leaderboard_{} WHERE v <= %s ORDER BY position ASC LIMIT 1".format(mode), [newScore]) plus = 0 if target is None: # Wow, this user completely sucks at this game. target = glob.db.fetch("SELECT * FROM leaderboard_{} ORDER BY position DESC LIMIT 1".format(mode)) plus = 1 # Set newT if target is None: # Okay, nevermind. It's not this user to suck. It's just that no-one has ever entered the leaderboard thus far. # So, the player is now #1. Yay! newT = 1 else: # Otherwise, just give them the position of the target. newT = target["position"] + plus # Make some place for the new "place holder". if newPlayer: glob.db.execute("UPDATE leaderboard_{} SET position = position + 1 WHERE position >= %s ORDER BY position DESC".format(mode), [newT]) else: glob.db.execute("DELETE FROM leaderboard_{} WHERE user = %s".format(mode), [userID]) glob.db.execute("UPDATE leaderboard_{} SET position = position + 1 WHERE position < %s AND position >= %s ORDER BY position DESC".format(mode), [us["position"], newT]) #if newT <= 1: # log.info("{} is now #{} ({})".format(userID, newT, mode), "bunker") # Finally, insert the user back. glob.db.execute("INSERT INTO leaderboard_{} (position, user, v) VALUES (%s, %s, %s);".format(mode), [newT, userID, newScore]) if gameMode == 0: newPlayer = False us = glob.db.fetch("SELECT * FROM users_peak_rank WHERE userid = %s LIMIT 1", [userID]) if us is None: newPlayer = True if newPlayer: glob.db.execute("INSERT INTO users_peak_rank (userid, peak_rank) VALUES (%s, %s);", [userID, newT]) else: if us["peak_rank"] > newT: glob.db.execute("UPDATE users_peak_rank SET peak_rank = %s WHERE userid = %s", [newT,userID])
def _execute(self, query, params=None, cb=None): if params is None: params = () attempts = 0 result = None lastExc = None while attempts < self.maxAttempts: # cur is needed in except (linter complains) cur = None # Calling objects.glob.threadScope.db may create a new connection # and we need to except OperationalErorrs raised by it as well try: conn = objects.glob.threadScope.db cur = conn.cursor(pymysql.cursors.DictCursor) log.debug("{} ({})".format(query, params)) cur.execute(query, params) if callable(cb): result = cb(cur) # Clear any exception we may have due to previously # failed attempts to execute the query lastExc = None break except (pymysql.err.OperationalError, pymysql.err.InternalError) as e: lastExc = e log.error( "MySQL operational/internal error on Thread {} ({}). Trying to recover" .format(threading.get_ident(), e)) # Close cursor now try: cur.close() except: pass # Sleep if necessary if attempts > 0: time.sleep(1) # Reset connection (this closes the connection as well) objects.glob.threadScope.dbClose() attempts += 1 finally: # Try to close the cursor (will except if there was a failure) try: cur.close() except: pass if lastExc is not None: raise lastExc return result
def writeSocket(self): """ Write buffer to socket :return: """ try: sent = self.socket.send(self.__writebuffer.encode()) log.debug("[IRC] [{}:{}] <- {}".format(self.ip, self.port, self.__writebuffer[:sent])) self.__writebuffer = self.__writebuffer[sent:] except socket.error as x: self.disconnect(str(x))
def __init__(self, __beatmap, __score=None, acc=0, mods=0, tillerino=False, combo: int = -1): """ Set oppai params. __beatmap -- beatmap object __score -- score object acc -- manual acc. Used in tillerino-like bot. You don't need this if you pass __score object mods -- manual mods. Used in tillerino-like bot. You don't need this if you pass __score object tillerino -- If True, self.pp will be a list with pp values for 100%, 99%, 98% and 95% acc. Optional. combo -- The maximum combo achieved by the user, used for PP api. Optional. Set to -1 for FC. """ # Default values self.pp = None self.score = None self.acc = 0 self.mods = 0 self.combo = combo #FC self.misses = 0 self.stars = 0 self.tillerino = tillerino # Beatmap object self.beatmap = __beatmap # If passed, set everything from score object if __score is not None: self.score = __score self.acc = self.score.accuracy * 100 self.mods = self.score.mods self.combo = self.score.maxCombo self.misses = self.score.cMiss self.gameMode = self.score.gameMode else: # Otherwise, set acc and mods from params (tillerino) self.acc = acc self.mods = mods if self.beatmap.starsStd > 0: self.gameMode = gameModes.STD elif self.beatmap.starsTaiko > 0: self.gameMode = gameModes.TAIKO else: self.gameMode = None # Calculate pp log.debug("oppai-relax ~> Initialized oppai diffcalc") self.calculatePP()
def handle(userToken, packetData): # Read userIDs list packetData = clientPackets.userPanelRequest(packetData) # Process lists with length <= 32 if len(packetData) > 256: log.warning("Received userPanelRequest with length > 256.") return for i in packetData["users"]: # Enqueue userpanel packets relative to this user log.debug("Sending panel for user {}.".format(i)) userToken.enqueue(serverPackets.userPanel(i))
def update(userID, newScore, gameMode): """ Update gamemode's leaderboard. Doesn't do anything if userID is banned/restricted. :param userID: user :param newScore: new score or pp :param gameMode: gameMode number """ if userUtils.isAllowed(userID): log.debug("Updating leaderboard...") glob.redis.zadd("ripple:leaderboard_auto:{}".format(scoreUtils.readableGameMode(gameMode)), str(userID), str(newScore)) else: log.debug("Leaderboard update for user {} skipped (not allowed)".format(userID))