Esempio n. 1
0
	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!")
Esempio n. 2
0
	def asyncPost(self):
		try:
			return self.write("Register via ussr.pl")
			"""
			if not requestsManager.checkArguments(self.request.arguments, ["user[username]", "user[user_email]", "user[password]", "check"]):
				return self.write("what are you doing here?")
			username = self.get_argument("user[username]")
			email = self.get_argument("user[user_email]")
			password = self.get_argument("user[password]") # Raw password
			accountCreated = self.get_argument("check")
			if accountCreated == "1":
				return self.write('{"form_error":{"user":{"check":["Account already created."]}}}') 
			emailCheck = glob.db.fetch("SELECT 1 FROM users WHERE email = %s", [email])
			usernameCheck = glob.db.fetch("SELECT 1 FROM users WHERE username = %s", [username])	
			if emailCheck != None:
				return self.write('{"form_error":{"user":{"user_email":["Email address already used."]}}}')
			if usernameCheck != None or username.lower() in ["peppy","rrtyui","cookiezi","azer","loctav","banchobot","happystick","doomsday","sharingan33","andrea","cptnxn","reimu-desu","hvick225","_index","my aim sucks","kynan","rafis","sayonara-bye","thelewa","wubwoofwolf","millhioref","tom94","tillerino","clsw","spectator","exgon","axarious","angelsim","recia","nara","emperorpenguin83","bikko","xilver","vettel","kuu01","_yu68","tasuke912","dusk","ttobas","velperk","jakads","jhlee0133","abcdullah","yuko-","entozer","hdhr","ekoro","snowwhite","osuplayer111","musty","nero","elysion","ztrot","koreapenguin","fort","asphyxia","niko","shigetora"]:
				return self.write('{"form_error":{"user":{"username":["Username already used or it is forbidden."]}}}')	
			if len(password) < 8 or len(password) > 32:
				return self.write('{"form_error":{"user":{"password":["Password too short or long! (Password length must be more than 8 and less than 32)"]}}}') 
			if "_" in username and " " in username:
				self.write('{"form_error":{"user":{"username":["An username can not contain both underscores and spaces."]}}}')
			userID = int(glob.db.execute("INSERT INTO users(username, username_safe, password_md5, salt, email, register_datetime, privileges, password_version) VALUES (%s,       %s,            %s,            '',  %s,     %s,                 1048576,          2)", [username, userUtils.safeUsername(username), passwordUtils.genBcrypt(hashlib.md5(password.encode('utf-8')).hexdigest()), email, int(time.time())]))
			glob.db.execute("INSERT INTO users_stats(id, username, user_color, user_style, ranked_score_std, playcount_std, total_score_std, ranked_score_taiko, playcount_taiko, total_score_taiko, ranked_score_ctb, playcount_ctb, total_score_ctb, ranked_score_mania, playcount_mania, total_score_mania) VALUES (%s, %s, 'black', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)", [userID, username])
			glob.db.execute("INSERT INTO rx_stats(id, username, user_color, user_style, ranked_score_std, playcount_std, total_score_std, ranked_score_taiko, playcount_taiko, total_score_taiko, ranked_score_ctb, playcount_ctb, total_score_ctb, ranked_score_mania, playcount_mania, total_score_mania) VALUES (%s, %s, 'black', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)", [userID, username])
			log.info("{} created their account using ingame registration.".format(username))
			"""
		except Exception as e:
			log.error(e)
Esempio n. 3
0
    def asyncGet(self):
        try:
            args = {}
            try:
                # Check user auth because of sneaky people
                if not requestsManager.checkArguments(self.request.arguments,
                                                      ["u", "h"]):
                    raise exceptions.invalidArgumentsException(MODULE_NAME)
                username = self.get_argument("u")
                password = self.get_argument("h")
                ip = self.getRequestIP()
                userID = userUtils.getID(username)
                if not userUtils.checkLogin(userID, password):
                    raise exceptions.loginFailedException(
                        MODULE_NAME, username)
                if userUtils.check2FA(userID, ip):
                    raise exceptions.need2FAException(MODULE_NAME, username,
                                                      ip)
            except ValueError:
                raise exceptions.invalidArgumentsException(MODULE_NAME)

            # Pass all arguments otherwise it doesn't work
            for key, _ in self.request.arguments.items():
                args[key] = self.get_argument(key)

            response = requests.get("{}/web/osu-search-set.php?{}".format(
                glob.conf.config["beatmapserver"]["domain"], urlencode(args)))
            self.write(response.text)
        except Exception as e:
            log.error("search failed: {}".format(e))
            self.write("")
Esempio n. 4
0
def handle(userToken, packetData):
    try:
        # get usertoken data
        userID = userToken.userID

        # Read packet data
        packetData = clientPackets.createMatch(packetData)

        # Create a match object
        # TODO: Player number check
        matchID = glob.matches.createMatch(packetData["matchName"],
                                           packetData["matchPassword"],
                                           packetData["beatmapID"],
                                           packetData["beatmapName"],
                                           packetData["beatmapMD5"],
                                           packetData["gameMode"], userID)

        # Make sure the match has been created
        if matchID not in glob.matches.matches:
            raise exceptions.matchCreateError()

        with glob.matches.matches[matchID] as match:
            # Join that match
            userToken.joinMatch(matchID)

            # Give host to match creator
            match.setHost(userID)
            match.sendUpdates()
            match.changePassword(packetData["matchPassword"])
    except exceptions.matchCreateError:
        log.error("Error while creating match!")
Esempio n. 5
0
    def asyncGet(self):
        try:
            args = {}
            #if "stream" in self.request.arguments:
            #	args["stream"] = self.get_argument("stream")
            #if "action" in self.request.arguments:
            #	args["action"] = self.get_argument("action")
            #if "time" in self.request.arguments:
            #	args["time"] = self.get_argument("time")

            # Pass all arguments otherwise it doesn't work
            for key, _ in self.request.arguments.items():
                args[key] = self.get_argument(key)

            if args["action"].lower() == "put":
                self.write("nope")
                return

            response = requests.get(
                "https://osu.ppy.sh/web/check-updates.php?{}".format(
                    urlencode(args)),
                timeout=5)
            self.write(response.text)
        except Exception as e:
            log.error("check-updates failed: {}".format(e))
            self.write("")
Esempio n. 6
0
def recalcFirstPlaces(userID):

    c = glob.db.fetch("select * from user_first_places WHERE userid = %s",
                      [userID])

    fs = glob.db.fetchAll(
        "SELECT o.pp pp, o.mods mods,	beatmap_id, difficulty_std stars, difficulty_hr starsHR, difficulty_dt starsDT, hit_length length FROM (SELECT scores.* from scores JOIN users ON scores.userid = users.id WHERE privileges > 2 AND completed = 3 AND play_mode = 0) o LEFT JOIN (SELECT scores.* from scores JOIN users ON scores.userid = users.id WHERE privileges > 2 AND completed = 3 AND play_mode = 0) b on o.beatmap_md5 = b.beatmap_md5 AND o.pp < b.pp JOIN beatmaps ON o.beatmap_md5 = beatmaps.beatmap_md5 WHERE b.pp is NULL AND o.userid = %s AND o.play_mode = 0 AND o.completed = 3 AND o.pp > 40",
        [userID])

    if fs is None:
        return

    fvalue = 0
    for f in fs:
        mod = scoreUtils.readableMods(int(f["mods"]) & 80)
        stars = f["stars"]
        if (mod == "HR" or mod == "DT"):
            if (f["stars{}".format(mod)] <= 0):
                log.error("stars <= 0 beatmap: {}".format(f["beatmap_id"]))
            else:
                stars = f["stars{}".format(mod)]

        cv = addModsBonus(stars, f["mods"], f["pp"])**4.45 / 800
        fvalue += cv * lengthBonusMultiplier(f["length"])

    fvalue = round(fvalue)

    if c is None:
        glob.db.execute(
            "INSERT INTO user_first_places(userid,value) VALUES(%s,%s)",
            [userID, fvalue])
    else:
        glob.db.execute(
            "UPDATE user_first_places SET value = %s WHERE userID = %s",
            [fvalue, userID])
Esempio n. 7
0
 def wrapper(*args, **kwargs):
     try:
         return func(*args, **kwargs)
     except:
         log.error("Unhandled exception!\n```\n{}\n{}```".format(
             sys.exc_info(), traceback.format_exc()))
         if glob.sentry:
             glob.application.sentry_client.captureException()
Esempio n. 8
0
 def wrapper(self, *args, **kwargs):
     try:
         return func(self, *args, **kwargs)
     except:
         log.error("Unhandled exception!\n```\n{}\n{}```".format(
             sys.exc_info(), traceback.format_exc()))
         if glob.sentry:
             yield tornado.gen.Task(self.captureException, exc_info=True)
Esempio n. 9
0
 def asyncGet(self):
     try:
         response = requests.get(
             "https://osu.ppy.sh/web/osu-getseasonal.php")
         self.write(response.text)
     except Exception as e:
         log.error("check-seasonal failed: {}".format(e))
         self.write("")
Esempio n. 10
0
 def call_safed(self, funcd, *args, **kwargs):
     try:
         result = funcd(*args, **kwargs)
         return result
     except Exception as e:
         log.error("FokaBot error. Log:")
         traceback.print_exc()
         return False
Esempio n. 11
0
 def _decorator(request, *args, **kwargs):
     try:
         response = func(request, *args, **kwargs)
         return response
     except:
         log.error(
             "Unknown error{}!\n```\n{}\n{}```".format(
                 " in " + moduleName if moduleName != "" else "",
                 sys.exc_info(), traceback.format_exc()), True)
Esempio n. 12
0
    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
Esempio n. 13
0
def getTotalSize(o):
    """
	Get approximate object size using dill

	:param o: object
	:return: approximate bytes size
	"""
    try:
        return len(dill.dumps(o, recurse=True))
    except:
        log.error("Error while getting total object size!")
        return 0
Esempio n. 14
0
def getCountry(ip):
	"""
	Get country from IP address using geoip api

	:param ip: IP address
	:return: country code. XX if invalid.
	"""
	try:
		# Try to get country from Pikolo Aul's Go-Sanic ip API
		result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["country"]
		return result.upper()
	except:
		log.error("Error in get country")
		return "XX"
Esempio n. 15
0
	def sendUpdates(self):
		"""
		Send match updates packet to everyone in lobby and room streams

		:return:
		"""
		self.matchDataCache = serverPackets.updateMatch(self.matchID)
		censoredDataCache = serverPackets.updateMatch(self.matchID, censored=True)
		if self.matchDataCache is not None:
			glob.streams.broadcast(self.streamName, self.matchDataCache)
		if censoredDataCache is not None:
			glob.streams.broadcast("lobby", censoredDataCache)
		else:
			log.error("MPROOM{}: Can't send match update packet, match data is None!!!".format(self.matchID))
Esempio n. 16
0
def getLocation(ip):
	"""
	Get latitude and longitude from IP address using geoip api

	:param ip: IP address
	:return: (latitude, longitude)
	"""
	try:
		# Try to get position from Pikolo Aul's Go-Sanic ip API
		result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["loc"].split(",")
		return float(result[0]), float(result[1])
	except:
		log.error("Error in get position")
		return 0, 0
Esempio n. 17
0
	def asyncGet(self):
		try:
			args = {}
			try:
				# Check user auth because of sneaky people
				if not requestsManager.checkArguments(self.request.arguments, ["u", "h"]):
					raise exceptions.invalidArgumentsException(MODULE_NAME)
				username = self.get_argument("u")
				password = self.get_argument("h")
				ip = self.getRequestIP()
				userID = userUtils.getID(username)
				if not userUtils.checkLogin(userID, password):
					raise exceptions.loginFailedException(MODULE_NAME, username)
				if userUtils.check2FA(userID, ip):
					raise exceptions.need2FAException(MODULE_NAME, username, ip)
					
				# Get arguments
				gameMode = self.get_argument("m", None)
				if gameMode is not None:
					gameMode = int(gameMode)
				try:
					if gameMode < 0 or gameMode > 3:
						gameMode = None
				except:
					gameMode = None

				rankedStatus = self.get_argument("r", None)
				if rankedStatus is not None:
					rankedStatus = int(rankedStatus)

				query = self.get_argument("q", "")
				page = int(self.get_argument("p", "0"))
				if query.lower() in ["newest", "top rated", "most played"]:
					query = ""
			except ValueError:
				raise exceptions.invalidArgumentsException(MODULE_NAME)

			# Pass all arguments otherwise it doesn't work
			for key, _ in self.request.arguments.items():
				args[key] = self.get_argument(key)

			# Get data from cheesegull API
			log.info("{} has requested osu!direct search: {}".format(username, query if query != "" else "index"))

			response = requests.get("http://ripple.moe/web/osu-search.php?{}".format(urlencode(args)))
			self.write(response.text)
		except Exception as e:
			log.error("search failed: {}".format(e))
			self.write("")
Esempio n. 18
0
	def asyncGet(self, replayID):
		try:
			replayID = int(replayID)
			fullReplay = replayHelper.buildFullReplay(scoreID=replayID)
			self.write(fullReplay)
			self.add_header("Content-type", "application/octet-stream")
			self.set_header("Content-length", len(fullReplay))
			self.set_header("Content-Description", "File Transfer")
			self.set_header("Content-Disposition", "attachment; filename=\"{}.osr\"".format(replayID))
		except (exceptions.fileNotFoundException, exceptions.scoreNotFoundError, ValueError):
			self.set_status(404)
			self.write("Replay not found")
		except timeout_decorator.TimeoutError:
			log.error("S3 timed out")
			sentry.captureMessage("S3 timeout while fetching replay.")
			self.set_status(500)
			self.write("S3 Error")
Esempio n. 19
0
def getLocation(ip):
    """
	Get latitude and longitude from IP address using geoip api

	:param ip: IP address
	:return: (latitude, longitude)
	"""
    try:
        # Try to get position from Pikolo Aul's Go-Sanic ip API
        resp = requests.get("{}/{}".format(
            glob.conf.config['localize']['ipapiurl'], ip),
                            timeout=3)
        result = json.loads(resp.text)["loc"].split(",")
        return float(result[0]), float(result[1])
    except:
        log.error("Error in get position")
        return 0, 0
Esempio n. 20
0
def getCountry(ip):
    """
	Get country from IP address using geoip api

	:param ip: IP address
	:return: country code. XX if invalid.
	"""
    try:
        # Re-coded that shit, because ripple used python 2 api) And this is not readable ;d
        resp = requests.get("{}/{}".format(
            glob.conf.config['localize']['ipapiurl'], ip),
                            timeout=3)
        result = json.loads(resp.text)["country"]
        return result.upper()
    except:
        log.error("Error in get country")
        return "XX"
Esempio n. 21
0
def handle(userToken, packetData):
    try:
        # get usertoken data
        userID = userToken.userID

        # Read packet data
        packetData = clientPackets.createMatch(packetData)

        # Make sure the name is valid
        matchName = packetData["matchName"].strip()
        if not matchName:
            raise exceptions.matchCreateError()

        # Create a match object
        # TODO: Player number check
        matchID = glob.matches.createMatch(matchName,
                                           packetData["matchPassword"].strip(),
                                           packetData["beatmapID"],
                                           packetData["beatmapName"],
                                           packetData["beatmapMD5"],
                                           packetData["gameMode"],
                                           userID,
                                           creatorUserID=userID)

        # Make sure the match has been created
        if matchID not in glob.matches.matches:
            raise exceptions.matchCreateError()

        with glob.matches.matches[matchID] as match:
            # Join that match
            userToken.joinMatch(matchID)

            # Multiplayer Room Patch
            for i in range(0, 16):
                if match.slots[i].status is not 4:
                    match.slots[i].status = packetData["slot{}Status".format(
                        i)]

            # Give host to match creator
            match.setHost(userID)
            match.sendUpdates()
            match.changePassword(packetData["matchPassword"])
    except exceptions.matchCreateError:
        log.error("Error while creating match!")
        userToken.enqueue(serverPackets.matchJoinFail())
Esempio n. 22
0
    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:
		"""
        try:
            log.debug("Checking timed out clients")
            exceptions = []
            timedOutTokens = []  # timed out users
            timeoutLimit = int(time.time()) - 100
            for key, value in self.tokens.items():
                # Check timeout (fokabot is ignored)
                #print("UserID {} Always Online: {}".format(value.userID, aobaHelper.getAlwaysOnline(value.userID)))
                if aobaHelper.getAlwaysOnline(value.userID) == False:
                    if value.pingTime < timeoutLimit and not value.irc and not value.tournament:
                        # 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."))
                try:
                    logoutEvent.handle(self.tokens[i], None)
                except Exception as e:
                    exceptions.append(e)
                    log.error(
                        "Something wrong happened while disconnecting a timed out client. Reporting to Sentry "
                        "when the loop ends.")
            del timedOutTokens

            # Re-raise exceptions if needed
            if exceptions:
                raise periodicLoopException(exceptions)
        finally:
            # Schedule a new check (endless loop)
            threading.Timer(100, self.usersTimeoutCheckLoop).start()
Esempio n. 23
0
	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:
		"""
		try:
			log.debug("Checking empty matches")
			t = int(time.time())
			emptyMatches = []
			exceptions = []

			# 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 m.inProgress:
					continue
				if t - m.lastActionTime >= 1800:
					log.debug("Match #{} marked for cleanup".format(m.matchID))
					emptyMatches.append(m.matchID)

			# Dispose all idle matches
			for matchID in emptyMatches:
				try:
					self.disposeMatch(matchID)
				except Exception as e:
					exceptions.append(e)
					log.error(
						"Something wrong happened while disposing a timed out match. Reporting to Sentry when "
						"the loop ends."
					)

			# Re-raise exception if needed
			if exceptions:
				raise periodicLoopException(exceptions)
		finally:
			# Schedule a new check (endless loop)
			threading.Timer(30, self.cleanupLoop).start()
Esempio n. 24
0
 def asyncGet(self):
     try:
         userID = None
         remote_ip = self.request.headers.get('X-Real-IP') or \
          self.request.headers.get('X-Forwarded-For') or \
          self.request.remote_ip
         # here's the catch
         # if you are breaking this with v6, idgaf but you wont enjoy the seasonal feature.
         # thanks.
         # it's your risk to use v6, we never recommend on doing so in first place
         userLoginsResp = requests.get(
             "https://c.troke.id/api/v1/ipUsers?k={}&mode=1&umode=bancho&ip={}"
             .format(glob.conf.config["server"]["apikey"], remote_ip))
         userLoginsData = json.loads(userLoginsResp.text)
         ua = self.request.headers.get('User-Agent')
         if 'prua' in glob.conf.config['server'] and ua == glob.conf.config[
                 'server']['prua'] and 'logins' in userLoginsData:
             for loginData in userLoginsData['logins']:
                 if loginData[2] != 'bancho':
                     continue
                 if loginData[0] != remote_ip:
                     continue
                 userID = loginData[1]
                 break
         if darkness:
             # we are not disclosing this.
             k = common.darktenshi.decipher.seasonalKey(userID)
         else:
             k = 'default'
         response = None
         if k != 'default':
             # response = requests.get("https://old.troke.id/seasonal.php?k={}".format(k))
             images = common.darktenshi.decipher.seasonalImages(k)
             if images:
                 self.write(json.dumps(images))
                 return
         response = requests.get("https://old.troke.id/seasonal.php")
         self.write(response.text)
     except Exception as e:
         log.error("check-seasonal failed: {}".format(e))
         self.write("")
Esempio n. 25
0
    def asyncPost(self) -> None:

        if not requestsManager.checkArguments(self.request.arguments, ['u', 'h', 'action']):
            raise exceptions.invalidArgumentsException(MODULE_NAME)

        if self.get_argument('action') != 'submit':
            self.write('Not yet')
            return

        content = self.get_argument("content")

        try:
            glob.db.execute('INSERT INTO osu_session (id, user, ip, content, time) VALUES (NULL, %s, %s, %s, %s);', [
                userUtils.getID(self.get_argument('u')),
                self.getRequestIP(),
                content,
                time()
            ])
        except: log.error(f'osu session failed to save!\n\n**Content**\n{content}')

        self.write("Not yet")
        return
Esempio n. 26
0
    def calculate_pp(self):
        try:
            # Cache beatmap
            mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID)
            mapsHelper.cacheMap(mapFile, self.beatmap)

            # TODO: Sanizite mods

            # Gamemode check
            if self.score and self.score.gameMode != gameModes.CTB:
                raise exceptions.unsupportedGameModeException()

            # Accuracy check
            if self.accuracy > 1:
                raise ValueError("Accuracy must be between 0 and 1")

            # Calculate difficulty
            calcBeatmap = CalcBeatmap(mapFile)
            difficulty = Difficulty(beatmap=calcBeatmap, mods=self.mods)

            # Calculate pp
            if self.tillerino:
                results = []
                for acc in [1, 0.99, 0.98, 0.95]:
                    results.append(ppCalc.calculate_pp(
                        diff=difficulty,
						accuracy=acc,
						combo=self.combo if self.combo >= 0 else calcBeatmap.max_combo,
						miss=self.misses
                    ))
                self.pp = results
            else:
                self.pp = ppCalc.calculate_pp(
                    diff=difficulty,
					accuracy=self.accuracy,
					combo=self.combo if self.combo >= 0 else calcBeatmap.max_combo,
					miss=self.misses
                )
        except exceptions.osuApiFailException:
            log.error("cicciobello ~> osu!api error!")
            self.pp = 0
        except exceptions.unsupportedGameModeException:
            log.error("cicciobello ~> Unsupported gamemode")
            self.pp = 0
        except Exception as e:
            log.error("cicciobello ~> Unhandled exception: {}".format(str(e)))
            self.pp = 0
            raise
        finally:
            log.debug("cicciobello ~> Shutting down, pp = {}".format(self.pp))
Esempio n. 27
0
def handle(tornadoRequest):
    # Data to return
    responseToken = None
    responseTokenString = "ayy"
    responseData = bytes()

    # Get IP from tornado request
    requestIP = tornadoRequest.getRequestIP()

    # Avoid exceptions
    clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"]
    osuVersion = "unknown"

    # Split POST body so we can get username/password/hardware data
    # 2:-3 thing is because requestData has some escape stuff that we don't need
    loginData = str(tornadoRequest.request.body)[2:-3].split("\\n")
    try:
        # Make sure loginData is valid
        if len(loginData) < 3:
            raise exceptions.invalidArgumentsException()

        # Get HWID, MAC address and more
        # 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
        splitData = loginData[2].split("|")
        osuVersion = splitData[0]
        timeOffset = int(splitData[1])
        clientData = splitData[3].split(":")[:5]
        if len(clientData) < 4:
            raise exceptions.forceUpdateException()

        # Try to get the ID from username
        username = str(loginData[0])
        userID = userUtils.getID(username)

        if not userID:
            # Invalid username
            raise exceptions.loginFailedException()
        if not userUtils.checkLogin(userID, loginData[1]):
            # Invalid password
            raise exceptions.loginFailedException()

        # Make sure we are not banned or locked
        priv = userUtils.getPrivileges(userID)
        if userUtils.isBanned(
                userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
            raise exceptions.loginBannedException()
        if userUtils.isLocked(
                userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
            raise exceptions.loginLockedException()

        # 2FA check
        if userUtils.check2FA(userID, requestIP):
            log.warning("Need 2FA check for user {}".format(loginData[0]))
            raise exceptions.need2FAException()

        # No login errors!

        # Verify this user (if pending activation)
        firstLogin = False
        if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(
                userID):
            if userUtils.verifyUser(userID, clientData):
                # Valid account
                log.info("Account {} verified successfully!".format(userID))
                glob.verifiedCache[str(userID)] = 1
                firstLogin = True
            else:
                # Multiaccount detected
                log.info("Account {} NOT verified!".format(userID))
                glob.verifiedCache[str(userID)] = 0
                raise exceptions.loginBannedException()

        # Save HWID in db for multiaccount detection
        hwAllowed = userUtils.logHardware(userID, clientData, firstLogin)

        # This is false only if HWID is empty
        # if HWID is banned, we get restricted so there's no
        # need to deny bancho access
        if not hwAllowed:
            raise exceptions.haxException()

        # Log user IP
        userUtils.logIP(userID, requestIP)

        # Delete old tokens for that user and generate a new one
        isTournament = "tourney" in osuVersion
        if not isTournament:
            glob.tokens.deleteOldTokens(userID)
        responseToken = glob.tokens.addToken(userID,
                                             requestIP,
                                             timeOffset=timeOffset,
                                             tournament=isTournament)
        responseTokenString = responseToken.token

        # Check restricted mode (and eventually send message)
        responseToken.checkRestricted()

        # Send message if donor expires soon
        if responseToken.privileges & privileges.USER_DONOR > 0:
            expireDate = userUtils.getDonorExpire(responseToken.userID)
            if expireDate - int(time.time()) <= 86400 * 3:
                expireDays = round((expireDate - int(time.time())) / 86400)
                expireIn = "{} days".format(
                    expireDays) if expireDays > 1 else "less than 24 hours"
                responseToken.enqueue(
                    serverPackets.notification(
                        "Your donor tag expires in {}! When your donor tag expires, you won't have any of the donor privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Ripple and you don't want to lose your donor privileges, you can donate again by clicking on 'Support us' on Ripple's website."
                        .format(expireIn)))

        # Deprecate telegram 2fa and send alert
        if userUtils.deprecateTelegram2Fa(userID):
            responseToken.enqueue(
                serverPackets.notification(
                    "As stated on our blog, Telegram 2FA has been deprecated on 29th June 2018. Telegram 2FA has just been disabled from your account. If you want to keep your account secure with 2FA, please enable TOTP-based 2FA from our website https://ripple.moe. Thank you for your patience."
                ))

        # Set silence end UNIX time in token
        responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)

        # Get only silence remaining seconds
        silenceSeconds = responseToken.getSilenceSecondsLeft()

        # Get supporter/GMT
        userGMT = False
        userSupporter = True
        userTournament = False
        if responseToken.admin:
            userGMT = True
        if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0:
            userTournament = True

        # Server restarting check
        if glob.restarting:
            raise exceptions.banchoRestartingException()

        # Send login notification before maintenance message
        if glob.banchoConf.config["loginNotification"] != "":
            responseToken.enqueue(
                serverPackets.notification(
                    glob.banchoConf.config["loginNotification"]))

        # Maintenance check
        if glob.banchoConf.config["banchoMaintenance"]:
            if not userGMT:
                # We are not mod/admin, delete token, send notification and logout
                glob.tokens.deleteToken(responseTokenString)
                raise exceptions.banchoMaintenanceException()
            else:
                # We are mod/admin, send warning notification and continue
                responseToken.enqueue(
                    serverPackets.notification(
                        "Bancho is in maintenance mode. Only mods/admins have full access to the server.\nType !system maintenance off in chat to turn off maintenance mode."
                    ))

        # Send all needed login packets
        responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds))
        responseToken.enqueue(serverPackets.userID(userID))
        responseToken.enqueue(serverPackets.protocolVersion())
        responseToken.enqueue(
            serverPackets.userSupporterGMT(userSupporter, userGMT,
                                           userTournament))
        responseToken.enqueue(serverPackets.userPanel(userID, True))
        responseToken.enqueue(serverPackets.userStats(userID, True))

        # Channel info end (before starting!?! wtf bancho?)
        responseToken.enqueue(serverPackets.channelInfoEnd())
        # Default opened channels
        # TODO: Configurable default channels
        chat.joinChannel(token=responseToken, channel="#osu")
        chat.joinChannel(token=responseToken, channel="#announce")

        # Join admin channel if we are an admin
        if responseToken.admin:
            chat.joinChannel(token=responseToken, channel="#admin")

        # Output channels info
        for key, value in glob.channels.channels.items():
            if value.publicRead and not value.hidden:
                responseToken.enqueue(serverPackets.channelInfo(key))

        # Send friends list
        responseToken.enqueue(serverPackets.friendList(userID))

        # Send main menu icon
        if glob.banchoConf.config["menuIcon"] != "":
            responseToken.enqueue(
                serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))

        # Send online users' panels
        with glob.tokens:
            for _, token in glob.tokens.tokens.items():
                if not token.restricted:
                    responseToken.enqueue(serverPackets.userPanel(
                        token.userID))

        # Get location and country from ip.zxq.co or database
        if glob.localize:
            # Get location and country from IP
            latitude, longitude = locationHelper.getLocation(requestIP)
            countryLetters = locationHelper.getCountry(requestIP)
            country = countryHelper.getCountryID(countryLetters)
        else:
            # Set location to 0,0 and get country from db
            log.warning("Location skipped")
            latitude = 0
            longitude = 0
            countryLetters = "XX"
            country = countryHelper.getCountryID(userUtils.getCountry(userID))

        # Set location and country
        responseToken.setLocation(latitude, longitude)
        responseToken.country = country

        # Set country in db if user has no country (first bancho login)
        if userUtils.getCountry(userID) == "XX":
            userUtils.setCountry(userID, countryLetters)

        # Send to everyone our userpanel if we are not restricted or tournament
        if not responseToken.restricted:
            glob.streams.broadcast("main", serverPackets.userPanel(userID))

        # Set reponse data to right value and reset our queue
        responseData = responseToken.queue
        responseToken.resetQueue()
    except exceptions.loginFailedException:
        # Login failed error packet
        # (we don't use enqueue because we don't have a token since login has failed)
        responseData += serverPackets.loginFailed()
    except exceptions.invalidArgumentsException:
        # Invalid POST data
        # (we don't use enqueue because we don't have a token since login has failed)
        responseData += serverPackets.loginFailed()
        responseData += serverPackets.notification(
            "I see what you're doing...")
    except exceptions.loginBannedException:
        # Login banned error packet
        responseData += serverPackets.loginBanned()
    except exceptions.loginLockedException:
        # Login banned error packet
        responseData += serverPackets.loginLocked()
    except exceptions.banchoMaintenanceException:
        # Bancho is in maintenance mode
        responseData = bytes()
        if responseToken is not None:
            responseData = responseToken.queue
        responseData += serverPackets.notification(
            "Our bancho server is in maintenance mode. Please try to login again later."
        )
        responseData += serverPackets.loginFailed()
    except exceptions.banchoRestartingException:
        # Bancho is restarting
        responseData += serverPackets.notification(
            "Bancho is restarting. Try again in a few minutes.")
        responseData += serverPackets.loginFailed()
    except exceptions.need2FAException:
        # User tried to log in from unknown IP
        responseData += serverPackets.needVerification()
    except exceptions.haxException:
        # Using oldoldold client, we don't have client data. Force update.
        # (we don't use enqueue because we don't have a token since login has failed)
        responseData += serverPackets.forceUpdate()
        responseData += serverPackets.notification(
            "Hory shitto, your client is TOO old! Nice prehistory! Please turn update it from the settings!"
        )
    except:
        log.error("Unknown error!\n```\n{}\n{}```".format(
            sys.exc_info(), traceback.format_exc()))
    finally:
        # Console and discord log
        if len(loginData) < 3:
            log.info(
                "Invalid bancho login request from **{}** (insufficient POST data)"
                .format(requestIP), "bunker")

        # Return token string and data
        return responseTokenString, responseData
Esempio n. 28
0
	def calculatePP(self):
		"""
		Calculate total pp value with oppai and return it

		return -- total pp
		"""
		# Set variables
		self.pp = None
		try:
			# Build .osu map file path
			mapFile = mapsHelper.cachedMapPath(self.beatmap.beatmapID)
			log.debug("oppai ~> Map file: {}".format(mapFile))
			mapsHelper.cacheMap(mapFile, self.beatmap)

			# Use only mods supported by oppai
			modsFixed = self.mods & 5983

			# Check gamemode
			if self.gameMode != gameModes.STD and self.gameMode != gameModes.TAIKO:
				raise exceptions.unsupportedGameModeException()

			command = "./pp/oppai-ng/oppai {}".format(mapFile)
			if not self.tillerino:
				# force acc only for non-tillerino calculation
				# acc is set for each subprocess if calculating tillerino-like pp sets
				if self.acc > 0:
					command += " {acc:.2f}%".format(acc=self.acc)
			if self.mods > 0:
				command += " +{mods}".format(mods=scoreUtils.readableMods(modsFixed))
			if self.combo >= 0:
				command += " {combo}x".format(combo=self.combo)
			if self.misses > 0:
				command += " {misses}xm".format(misses=self.misses)
			if self.gameMode == gameModes.TAIKO:
				command += " -taiko"
			command += " -ojson"

			# Calculate pp
			if not self.tillerino:
				# self.pp, self.stars = self._runOppaiProcess(command)
				temp_pp, self.stars = self._runOppaiProcess(command)
				if (self.gameMode == gameModes.TAIKO and self.beatmap.starsStd > 0 and temp_pp > 800) or \
					self.stars > 50:
					# Invalidate pp for bugged taiko converteds and bugged inf pp std maps
					self.pp = 0
				else:
					self.pp = temp_pp
			else:
				pp_list = []
				for acc in [100, 99, 98, 95]:
					temp_command = command
					temp_command += " {acc:.2f}%".format(acc=acc)
					pp, self.stars = self._runOppaiProcess(temp_command)

					# If this is a broken converted, set all pp to 0 and break the loop
					if self.gameMode == gameModes.TAIKO and self.beatmap.starsStd > 0 and pp > 800:
						pp_list = [0, 0, 0, 0]
						break

					pp_list.append(pp)
				self.pp = pp_list

			log.debug("oppai ~> Calculated PP: {}, stars: {}".format(self.pp, self.stars))
		except OppaiError:
			log.error("oppai ~> oppai-ng error!")
			self.pp = 0
		except exceptions.osuApiFailException:
			log.error("oppai ~> osu!api error!")
			self.pp = 0
		except exceptions.unsupportedGameModeException:
			log.error("oppai ~> Unsupported gamemode")
			self.pp = 0
		except Exception as e:
			log.error("oppai ~> Unhandled exception: {}".format(str(e)))
			self.pp = 0
			raise
		finally:
			log.debug("oppai ~> Shutting down, pp = {}".format(self.pp))
Esempio n. 29
0
    def start(self):
        """
		Start IRC server main loop

		:return:
		"""
        # Sentry
        sentryClient = None
        if glob.sentry:
            sentryClient = raven.Client(glob.conf.config["sentry"]["ircdns"])

        serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            serversocket.bind(("0.0.0.0", self.port))
        except socket.error as e:
            log.error("[IRC] Could not bind port {}:{}".format(self.port, e))
            sys.exit(1)
        serversocket.listen(5)
        lastAliveCheck = time.time()

        # Main server loop
        while True:
            try:
                (iwtd, owtd, ewtd) = select.select(
                    [serversocket] + [x.socket for x in self.clients.values()],
                    [
                        x.socket for x in self.clients.values()
                        if x.writeBufferSize() > 0
                    ], [], 1)

                # Handle incoming connections
                for x in iwtd:
                    if x in self.clients:
                        self.clients[x].readSocket()
                    else:
                        (conn, addr) = x.accept()
                        try:
                            self.clients[conn] = Client(self, conn)
                            log.info(
                                "[IRC] Accepted connection from {}:{}".format(
                                    addr[0], addr[1]))
                        except socket.error:
                            try:
                                conn.close()
                            except:
                                pass

                # Handle outgoing connections
                for x in owtd:
                    if x in self.clients:  # client may have been disconnected
                        self.clients[x].writeSocket()

                # Make sure all IRC clients are still connected
                now = time.time()
                if lastAliveCheck + 10 < now:
                    for client in list(self.clients.values()):
                        client.checkAlive()
                    lastAliveCheck = now
            except:
                log.error("[IRC] Unknown error!\n```\n{}\n{}```".format(
                    sys.exc_info(), traceback.format_exc()))
                if glob.sentry and sentryClient is not None:
                    sentryClient.captureException()
Esempio n. 30
0
def handle(tornadoRequest):
    # Data to return
    responseToken = None
    responseTokenString = "ayy"
    responseData = bytes()

    # Get IP from tornado request
    requestIP = tornadoRequest.getRequestIP()

    # Avoid exceptions
    clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"]
    osuVersion = "unknown"

    # Split POST body so we can get username/password/hardware data
    # 2:-3 thing is because requestData has some escape stuff that we don't need
    loginData = str(tornadoRequest.request.body)[2:-3].split("\\n")
    try:
        # Make sure loginData is valid
        #if len(loginData) < 3:
        #raise exceptions.invalidArgumentsException()

        # Get HWID, MAC address and more
        # 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
        splitData = loginData[2].split("|")
        osuVersion = splitData[0]
        timeOffset = int(splitData[1])
        clientData = splitData[3].split(":")[:5]
        if len(clientData) < 4:
            raise exceptions.forceUpdateException()

        # Try to get the ID from username
        username = str(loginData[0])
        userID = userUtils.getID(username)

        if not userID:
            # Invalid username
            raise exceptions.loginFailedException()
        if not userUtils.checkLogin(userID, loginData[1]):
            # Invalid password
            raise exceptions.loginFailedException()

        # Make sure we are not banned or locked
        priv = userUtils.getPrivileges(userID)
        if userUtils.isBanned(
                userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
            raise exceptions.loginBannedException()
        if userUtils.isLocked(
                userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
            raise exceptions.loginLockedException()

        # 2FA check
        if userUtils.check2FA(userID, requestIP):
            if userID == 1000:  #make sakuru do a different check because he plays over LAN
                if glob.db.fetch(
                        f"SELECT 2fabypassip FROM users WHERE id = 1000"
                ) == requestIP:
                    pass
            log.warning("Need 2FA check for user {}".format(loginData[0]))
            raise exceptions.need2FAException()

        # No login errors!

        # Verify this user (if pending activation)
        firstLogin = False
        if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(
                userID):
            # Log user IP
            userUtils.logIP(userID, requestIP)
            if glob.db.fetch(
                    f"SELECT ip FROM ip_blacklist WHERE ip = '{requestIP}'"
            ):  #requestIP comes from tornado, so this isn't an sql injection vulnerability afaik
                glob.tokens.deleteToken(userID)
                userUtils.restrict(userID)
                return
            if userUtils.verifyUser(userID, clientData):
                # Valid account
                log.info("Account {} verified successfully!".format(userID))
                glob.verifiedCache[str(userID)] = 1
                firstLogin = True
            else:
                # Multiaccount detected
                log.info("Account {} NOT verified!".format(userID))
                glob.verifiedCache[str(userID)] = 0
                raise exceptions.loginBannedException()

        # Save HWID in db for multiaccount detection
        hwAllowed = userUtils.logHardware(userID, clientData, firstLogin)

        # This is false only if HWID is empty
        # if HWID is banned, we get restricted so there's no
        # need to deny bancho access
        if not hwAllowed:
            raise exceptions.haxException()

        # Log user osuver
        kotrikhelper.setUserLastOsuVer(userID, osuVersion)

        # Delete old tokens for that user and generate a new one
        isTournament = "tourney" in osuVersion
        if not isTournament:
            glob.tokens.deleteOldTokens(userID)
        responseToken = glob.tokens.addToken(userID,
                                             requestIP,
                                             timeOffset=timeOffset,
                                             tournament=isTournament)
        responseTokenString = responseToken.token

        # Check restricted mode (and eventually send message)
        responseToken.checkRestricted()

        # Check if frozen
        IsFrozen = glob.db.fetch(
            f"SELECT frozen, firstloginafterfrozen, freezedate FROM users WHERE id = {userID} LIMIT 1"
        )  #ok kids, dont ever use formats in sql queries. here i can do it as the userID comes from a trusted source (this being pep.py itself) so it wont leave me susceptable to sql injection
        frozen = bool(IsFrozen["frozen"])

        present = datetime.now()
        readabledate = datetime.utcfromtimestamp(
            IsFrozen["freezedate"]).strftime('%d-%m-%Y %H:%M:%S')
        date2 = datetime.utcfromtimestamp(
            IsFrozen["freezedate"]).strftime('%d/%m/%Y')
        date3 = present.strftime('%d/%m/%Y')
        passed = date2 < date3
        if frozen and passed == False:
            responseToken.enqueue(
                serverPackets.notification(
                    f"The osuHOW staff team has found you suspicious and would like to request a liveplay. You have until {readabledate} (UTC) to provide a liveplay to the staff team. This can be done via the osuHOW Discord server. Failure to provide a valid liveplay will result in your account being automatically restricted."
                ))
        elif frozen and passed == True:
            responseToken.enqueue(
                serverPackets.notification(
                    "Your window for liveplay sumbission has expired! Your account has been restricted as per our cheating policy. Please contact staff for more information on what can be done. This can be done via the osuHOW Discord server."
                ))
            userUtils.restrict(responseToken.userID)

        #we thank unfrozen people
        first = IsFrozen["firstloginafterfrozen"]

        if not frozen and first:
            responseToken.enqueue(
                serverPackets.notification(
                    "Thank you for providing a liveplay! You have proven your legitemacy and have subsequently been unfrozen."
                ))
            glob.db.execute(
                f"UPDATE users SET firstloginafterfrozen = 0 WHERE id = {userID}"
            )

        # Deprecate telegram 2fa and send alert
        #if userUtils.deprecateTelegram2Fa(userID):
        #	responseToken.enqueue(serverPackets.notification("As stated on our blog, Telegram 2FA has been deprecated on 29th June 2018. Telegram 2FA has just been disabled from your account. If you want to keep your account secure with 2FA, please enable TOTP-based 2FA from our website https://ripple.moe. Thank you for your patience."))

        # Set silence end UNIX time in token
        responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)

        # Get only silence remaining seconds
        silenceSeconds = responseToken.getSilenceSecondsLeft()

        # Get supporter/GMT
        userGMT = False
        if not userUtils.isRestricted(userID):
            userSupporter = True
        else:
            userSupporter = False
        userTournament = False
        if responseToken.admin:
            userGMT = True
        if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0:
            userTournament = True

        # Server restarting check
        if glob.restarting:
            raise exceptions.banchoRestartingException()

        # Send login notification before maintenance message
        #if glob.banchoConf.config["loginNotification"] != "":

        #creating notification
        OnlineUsers = int(
            glob.redis.get("ripple:online_users").decode("utf-8"))
        Notif = f"""- Online Users: {OnlineUsers}
		- {random.choice(glob.banchoConf.config['Quotes'])}"""
        responseToken.enqueue(serverPackets.notification(Notif))

        # Maintenance check
        if glob.banchoConf.config["banchoMaintenance"]:
            if not userGMT:
                # We are not mod/admin, delete token, send notification and logout
                glob.tokens.deleteToken(responseTokenString)
                raise exceptions.banchoMaintenanceException()
            else:
                # We are mod/admin, send warning notification and continue
                responseToken.enqueue(
                    serverPackets.notification(
                        "Bancho is in maintenance mode. Only mods/admins have full access to the server.\nType !system maintenance off in chat to turn off maintenance mode."
                    ))

        # BAN CUSTOM CHEAT CLIENTS
        # 0Ainu = First Ainu build
        # b20190326.2 = Ainu build 2 (MPGH PAGE 10)
        # b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Ainu Aoba's Birthday Build
        # b20191223.3 = Unknown Ainu build? (Taken from most users osuver in cookiezi.pw)
        # b20190226.2 = hqOsu (hq-af)
        if glob.conf.extra["mode"]["anticheat"]:
            # Ainu Client 2020 update
            if tornadoRequest.request.headers.get("ainu") == "happy":
                log.info(f"Account {userID} tried to use Ainu Client 2020!")
                if userUtils.isRestricted(userID):
                    responseToken.enqueue(
                        serverPackets.notification(
                            "Ainu client... Really? Welp enjoy your ban!"))
                else:
                    glob.tokens.deleteToken(userID)
                    userUtils.restrict(userID)
                    raise exceptions.loginCheatClientsException()
            # Ainu Client 2019,
            elif aobaHelper.getOsuVer(userID) in [
                    "0Ainu", "b20190401.22f56c084ba339eefd9c7ca4335e246f80"
            ]:
                log.info(f"Account {userID} tried to use 0Ainu Client!")
                if userUtils.isRestricted(userID):
                    responseToken.enqueue(
                        serverPackets.notification(
                            "Ainu client... Really? Welp enjoy your ban!"))
                else:
                    glob.tokens.deleteToken(userID)
                    userUtils.restrict(userID)
                    raise exceptions.loginCheatClientsException()
            elif aobaHelper.getOsuVer(userID) in [
                    "b20190326.2", "b20191223.3"
            ]:
                log.info(f"Account {userID} tried to use 1Ainu Client!")
            # hqOsu
            elif aobaHelper.getOsuVer(userID) == "b20190226.2":
                log.info(f"Account {userID} is maybe using hqosu")

            #hqosu legacy
            elif aobaHelper.getOsuVer(userID) == "b20190716.5":
                log.info(f"Account {userID} is maybe using hqosu legacy")

            elif tornadoRequest.request.headers.get(
                    "a") == "@_@_@_@_@_@_@_@___@_@_@_@___@_@___@":
                log.info("Account ID {} tried to use secret!".format(userID))
                if userUtils.isRestricted(userID):
                    responseToken.enqueue(
                        serverPackets.notification(
                            "You're banned because you're currently using some darkness secret that no one has..."
                        ))
                    return
                else:
                    glob.tokens.deleteToken(userID)
                    userUtils.restrict(userID)
                    #if glob.conf.config["discord"]["enable"] == True:
                    webhook = aobaHelper.Webhook(
                        glob.conf.config["discord"]["anticheat"],
                        color=0xadd8e6,
                        footer="@_@_@_@_@_@_@_@___@_@_@_@___@_@___@")
                    webhook.set_title(
                        title="Catched some cheater Account ID {}".format(
                            userID))
                    webhook.set_desc(
                        "{} tried to @_@_@_@_@_@_@_@___@_@_@_@___@_@___@ and got restricted!"
                        .format(username))
                    log.info("Sent to webhook {} DONE!!".format(
                        glob.conf.config["discord"]["enable"]))
                    webhook.post()
                    raise exceptions.loginCheatClientsException()

            elif osuVersion.startswith("skoot"):
                log.info(f"Account {userID} tried to skooooot!!!!")
                if userUtils.isRestricted(userID):
                    responseToken.enqueue(
                        serverPackets.notification(
                            "oyoyoyoyoyoyoy no skooooooooting allowed here bud"
                        ))
                else:
                    glob.tokens.deleteToken(userID)
                    userUtils.restrict(userID)
                    raise exceptions.loginCheatClientsException()
            elif osuVersion[0] != "b":
                glob.tokens.deleteToken(userID)
                raise exceptions.haxException()

        # Send all needed login packets
        responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds))
        responseToken.enqueue(serverPackets.userID(userID))
        responseToken.enqueue(serverPackets.protocolVersion())
        responseToken.enqueue(
            serverPackets.userSupporterGMT(userSupporter, userGMT,
                                           userTournament))
        responseToken.enqueue(serverPackets.userPanel(userID, True))
        responseToken.enqueue(serverPackets.userStats(userID, True))

        # Channel info end (before starting!?! wtf bancho?)
        responseToken.enqueue(serverPackets.channelInfoEnd())
        # Default opened channels
        # TODO: Configurable default channels
        chat.joinChannel(token=responseToken, channel="#osu")
        chat.joinChannel(token=responseToken, channel="#announce")

        # Join admin channel if we are an admin
        if responseToken.admin:
            chat.joinChannel(token=responseToken, channel="#admin")

        # Output channels info
        for key, value in glob.channels.channels.items():
            if value.publicRead and not value.hidden:
                responseToken.enqueue(serverPackets.channelInfo(key))

        # Send friends list
        responseToken.enqueue(serverPackets.friendList(userID))

        # Send main menu icon
        if glob.banchoConf.config["menuIcon"] != "":
            responseToken.enqueue(
                serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))

        # Send online users' panels
        with glob.tokens:
            for _, token in glob.tokens.tokens.items():
                if not token.restricted:
                    responseToken.enqueue(serverPackets.userPanel(
                        token.userID))

        # Get location and country from ip.zxq.co or database
        if glob.localize:
            # Get location and country from IP
            latitude, longitude = locationHelper.getLocation(requestIP)
            countryLetters = locationHelper.getCountry(requestIP)
            country = countryHelper.getCountryID(countryLetters)
        else:
            # Set location to 0,0 and get country from db
            log.warning("Location skipped")
            latitude = 0
            longitude = 0
            countryLetters = "XX"
            country = countryHelper.getCountryID(userUtils.getCountry(userID))

        # Set location and country
        responseToken.setLocation(latitude, longitude)
        responseToken.country = country

        # Set country in db if user has no country (first bancho login)
        if userUtils.getCountry(userID) == "XX":
            userUtils.setCountry(userID, countryLetters)

        # Send to everyone our userpanel if we are not restricted or tournament
        if not responseToken.restricted:
            glob.streams.broadcast("main", serverPackets.userPanel(userID))

        # Set reponse data to right value and reset our queue
        responseData = responseToken.queue
        responseToken.resetQueue()
    except exceptions.loginFailedException:
        # Login failed error packet
        # (we don't use enqueue because we don't have a token since login has failed)
        responseData += serverPackets.loginFailed()
    except exceptions.invalidArgumentsException:
        # Invalid POST data
        # (we don't use enqueue because we don't have a token since login has failed)
        responseData += serverPackets.loginFailed()
        responseData += serverPackets.notification(
            "I see what you're doing...")
    except exceptions.loginBannedException:
        # Login banned error packet
        responseData += serverPackets.loginBanned()
    except exceptions.loginLockedException:
        # Login banned error packet
        responseData += serverPackets.loginLocked()
    except exceptions.loginCheatClientsException:
        # Banned for logging in with cheats
        responseData += serverPackets.loginBanned()
    except exceptions.banchoMaintenanceException:
        # Bancho is in maintenance mode
        responseData = bytes()
        if responseToken is not None:
            responseData = responseToken.queue
        responseData += serverPackets.notification(
            "Our bancho server is in maintenance mode. Please try to login again later."
        )
        responseData += serverPackets.loginFailed()
    except exceptions.banchoRestartingException:
        # Bancho is restarting
        responseData += serverPackets.notification(
            "Bancho is restarting. Try again in a few minutes.")
        responseData += serverPackets.loginFailed()
    except exceptions.need2FAException:
        # User tried to log in from unknown IP
        responseData += serverPackets.needVerification()
    except exceptions.haxException:
        # Using oldoldold client, we don't have client data. Force update.
        # (we don't use enqueue because we don't have a token since login has failed)
        responseData += serverPackets.forceUpdate()
    except:
        log.error("Unknown error!\n```\n{}\n{}```".format(
            sys.exc_info(), traceback.format_exc()))
    finally:
        # Console and discord log
        if len(loginData) < 3:
            log.info(
                "Invalid bancho login request from **{}** (insufficient POST data)"
                .format(requestIP), "bunker")

        # Return token string and data
        return responseTokenString, responseData