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 loadSettings(self): """ (re)load bancho_settings from DB and set values in config array """ self.config["banchoMaintenance"] = generalUtils.stringToBool( glob.db.fetch( "SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'" )["value_int"]) self.config["freeDirect"] = generalUtils.stringToBool( glob.db.fetch( "SELECT value_int FROM bancho_settings WHERE name = 'free_direct'" )["value_int"]) mainMenuIcon = glob.db.fetch( "SELECT file_id, url, lokasi_file FROM main_menu_icons WHERE is_current = 1 LIMIT 1" ) if mainMenuIcon is None: self.config["menuIcon"] = "" else: imageURL = "{}/{}.png".format(mainMenuIcon["lokasi_file"], mainMenuIcon["file_id"]) self.config["menuIcon"] = "{}|{}".format(imageURL, mainMenuIcon["url"]) self.config["loginNotification"] = glob.db.fetch( "SELECT value_string FROM bancho_settings WHERE name = 'login_notification'" )["value_string"]
def loadSettings(self): """ (re)load bancho_settings from DB and set values in config array """ self.config["banchoMaintenance"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"]) self.config["freeDirect"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'free_direct'")["value_int"]) mainMenuIcon = glob.db.fetch("SELECT file_id, url FROM main_menu_icons WHERE is_current = 1 LIMIT 1") if mainMenuIcon is None: self.config["menuIcon"] = "" else: imageURL = "https://i.ppy.sh/{}.png".format(mainMenuIcon["file_id"]) self.config["menuIcon"] = "{}|{}".format(imageURL, mainMenuIcon["url"]) #self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"] self.config["Quotes"] = [ "Don't forget to visit c.ussr.pl!", "WE SERVE THE SOVIET UNION!", "Ran by best VEVO channel!", "Stay home! Click circles!", "Spelchecked!", "Screw you segmentation!", "I forgot how to decrypt bcrypt", "Introducing : Time", "Python to the moon!", "We have the best devsSyntaxError: good_devs is not defined.", "Powered by electricity!", "i still can't decrypt bcrypt", "how to run py on 7m??", "It may not work, but it is fast!" ]
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 loadSettings(self): """ (re)load bancho_settings from DB and set values in config array """ self.config["banchoMaintenance"] = generalUtils.stringToBool( glob.db.fetch( "SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'" )["value_int"]) self.config["freeDirect"] = generalUtils.stringToBool( glob.db.fetch( "SELECT value_int FROM bancho_settings WHERE name = 'free_direct'" )["value_int"]) self.config["menuIcon"] = glob.db.fetch( "SELECT value_string FROM bancho_settings WHERE name = 'menu_icon'" )["value_string"] self.config["loginNotification"] = glob.db.fetch( "SELECT value_string FROM bancho_settings WHERE name = 'login_notification'" )["value_string"]
def delta(fro, chan, message): if chan.startswith("#"): return if not glob.conf.config["server"]["deltaurl"].strip(): return "Delta is disabled." userToken = glob.tokens.getTokenFromUserID(userUtils.getID(fro), ignoreIRC=True, _all=False) if userToken is None: return "You must be connected from a game client to switch to delta" if not generalUtils.stringToBool(glob.conf.config["server"]["publicdelta"]) and not userToken.admin: return "You can't use delta yet. Try again later." userToken.enqueue(serverPackets.switchServer(glob.conf.config["server"]["deltaurl"])) return "Connecting to delta..."
def get(self, userID: int, fileMd5: str, country: bool = False, friends: bool = False, mods: int = -1) -> int: """ 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(f"lets:personal_best_cache:{userID}") if data is None: raise cacheMiss() # Unpack cached data data = data.decode().split("|") # Check if everything matches if (fileMd5 != data[1] or country != generalUtils.stringToBool(data[2]) or friends != generalUtils.stringToBool(data[3]) or mods != int(data[4])): raise cacheMiss() # Cache hit log.debug("personalBestCache hit") return int(data[0]) except cacheMiss: log.debug("personalBestCache miss") return 0
def loadSettings(self): """ (re)load bancho_settings from DB and set values in config array """ self.config["banchoMaintenance"] = generalUtils.stringToBool( glob.db.fetch( "SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'" )["value_int"]) self.config["freeDirect"] = generalUtils.stringToBool( glob.db.fetch( "SELECT value_int FROM bancho_settings WHERE name = 'free_direct'" )["value_int"]) mainMenuIcon = glob.db.fetch( "SELECT file_id, url FROM main_menu_icons WHERE is_current = 1 LIMIT 1" ) if mainMenuIcon is None: self.config["menuIcon"] = "" else: imageURL = "".format(mainMenuIcon["file_id"]) self.config["menuIcon"] = "".format(imageURL, mainMenuIcon["url"]) #self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"] self.config["Quotes"] = [ "Welcome to osuHOW!", "sakuru is a loli", "t r i a n g l e s", "gamer'd", "peppy did a bad job on my taxes", "osu!2013 GAMING", "c.eggradio.tk", "peppycode", "ripplecode", "bad python code", "HOWosu", "bad server", "peppy GET OUT", "If you don't play osu!, well, I don't even know how you got here!", "osu bad game", "disco prince", "habbas is funny", "buy the c o i n s", "no cbcc allowed, seriously, i unranked it!", "realistikdash why did you cheat on my old server", "neko cucucumber", "help my pc is on fire", "bad game", "i installed osu on my toaster", "peppy open up", "Activate Windows", "hha funi nubmer guysz psl liek an subscrbe", "peppy (osu osx)", "Go to Settings to activate Windows.", "i hate u ripple", "Second biggest exporter of triangles!", '"yuuor amd" -hubz' ]
def getOsuFileFromID(beatmapID): """ Send a request to osu! servers to download a .osu file from beatmap ID Used to get .osu files for oppai beatmapID -- ID of beatmap (not beatmapset) to download return -- .osu file content if success, None if failed """ # Make sure osuapi is enabled if not generalUtils.stringToBool(glob.conf.config["osuapi"]["enable"]): log.warning("osuapi is disabled") return None response = None try: URL = "{}/osu/{}".format(glob.conf.config["osuapi"]["apiurl"], beatmapID) response = requests.get(URL, timeout=20).content finally: glob.dog.increment(glob.DATADOG_PREFIX+".osu_api.osu_file_requests") return response
def getOsuFileFromName(fileName): """ Send a request to osu! servers to download a .osu file from file name Used to update beatmaps fileName -- .osu file name to download return -- .osu file content if success, None if failed """ # Make sure osuapi is enabled if not generalUtils.stringToBool(glob.conf.config["osuapi"]["enable"]): log.warning("osuapi is disabled") return None response = None try: URL = "{}/web/maps/{}".format(glob.conf.config["osuapi"]["apiurl"], quote(fileName)) req = requests.get(URL, timeout=20) req.encoding = "utf-8" response = req.content finally: glob.dog.increment(glob.DATADOG_PREFIX+".osu_api.osu_file_requests") return response
glob.tokens.usersTimeoutCheckLoop() consoleHelper.printDone() # Initialize spam protection reset loop consoleHelper.printNoNl( "> Initializing spam protection reset loop... ") glob.tokens.spamProtectionResetLoop() consoleHelper.printDone() # Initialize multiplayer cleanup loop consoleHelper.printNoNl("> Initializing multiplayer cleanup loop... ") glob.matches.cleanupLoop() consoleHelper.printDone() # Localize warning glob.localize = generalUtils.stringToBool( glob.conf.config["localize"]["enable"]) if not glob.localize: consoleHelper.printColored( "[!] Warning! Users localization is disabled!", bcolors.YELLOW) # Discord if generalUtils.stringToBool(glob.conf.config["discord"]["enable"]): glob.schiavo = schiavo.schiavo( glob.conf.config["discord"]["boturl"], "**pep.py**") else: consoleHelper.printColored( "[!] Warning! Discord logging is disabled!", bcolors.YELLOW) # Gzip glob.gzip = generalUtils.stringToBool( glob.conf.config["server"]["gzip"])
# Save lets version in redis glob.redis.set("lets:version", glob.VERSION) # Create threads pool try: consoleHelper.printNoNl("> Creating threads pool... ") glob.pool = ThreadPool(int(glob.conf.config["server"]["threads"])) consoleHelper.printDone() except: consoleHelper.printError() consoleHelper.printColored( "[!] Error while creating threads pool. Please check your config.ini and run the server again", bcolors.RED) # Check osuapi if not generalUtils.stringToBool(glob.conf.config["osuapi"]["enable"]): consoleHelper.printColored( "[!] osu!api features are disabled. If you don't have a valid beatmaps table, all beatmaps will show as unranked", bcolors.YELLOW) if int(glob.conf.config["server"]["beatmapcacheexpire"]) > 0: consoleHelper.printColored( "[!] IMPORTANT! Your beatmapcacheexpire in config.ini is > 0 and osu!api features are disabled.\nWe do not reccoment this, because too old beatmaps will be shown as unranked.\nSet beatmapcacheexpire to 0 to disable beatmap latest update check and fix that issue.", bcolors.YELLOW) # Load achievements consoleHelper.printNoNl("Loading achievements... ") try: secret.achievements.utils.load_achievements() except Exception as e: consoleHelper.printError() consoleHelper.printColored(
help="force number of workers", required=False) parser.add_argument('-v', '--verbose', help="run ripp in verbose/debug mode", required=False, action='store_true') args = parser.parse_args() # Platform print("Running under {}".format("UNIX" if UNIX == True else "WIN32")) # Load config consoleHelper.printNoNl("> Reading config file... ") glob.conf = config.config("config.ini") glob.debug = generalUtils.stringToBool(glob.conf.config["server"]["debug"]) consoleHelper.printDone() # Get workers from arguments if set workers = 0 if args.workers is not None: workers = int(args.workers) # Connect to MySQL try: consoleHelper.printNoNl("> Connecting to MySQL db") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], max(workers, MAX_WORKERS))
def main() -> int: # AGPL license agreement try: agpl.check_license("ripple", "LETS") except agpl.LicenseError as e: print(str(e)) return 1 try: consoleHelper.printServerStartHeader(True) # Read config consoleHelper.printNoNl("> Reading config file... ") glob.conf = config.config("config.ini") if glob.conf.default: # We have generated a default config.ini, quit server consoleHelper.printWarning() consoleHelper.printColored( "[!] config.ini not found. A default one has been generated.", bcolors.YELLOW) consoleHelper.printColored( "[!] Please edit your config.ini and run the server again.", bcolors.YELLOW) return 1 # If we haven't generated a default config.ini, check if it's valid if not glob.conf.checkConfig(): consoleHelper.printError() consoleHelper.printColored( "[!] Invalid config.ini. Please configure it properly", bcolors.RED) consoleHelper.printColored( "[!] Delete your config.ini to generate a default one", bcolors.RED) return 1 else: consoleHelper.printDone() # Read additional config file consoleHelper.printNoNl("> Loading additional config file... ") try: if not os.path.isfile(glob.conf.config["custom"]["config"]): consoleHelper.printWarning() consoleHelper.printColored( "[!] Missing config file at {}; A default one has been generated at this location." .format(glob.conf.config["custom"]["config"]), bcolors.YELLOW) shutil.copy("common/default_config.json", glob.conf.config["custom"]["config"]) with open(glob.conf.config["custom"]["config"], "r") as f: glob.conf.extra = json.load(f) consoleHelper.printDone() except: consoleHelper.printWarning() consoleHelper.printColored( "[!] Unable to load custom config at {}".format( glob.conf.config["custom"]["config"]), bcolors.RED) return 1 # Create data/oppai maps folder if needed consoleHelper.printNoNl("> Checking folders... ") paths = [ ".data", ".data/oppai", ".data/catch_the_pp", glob.conf.config["server"]["replayspath"], "{}_relax".format(glob.conf.config["server"]["replayspath"]), glob.conf.config["server"]["beatmapspath"], glob.conf.config["server"]["screenshotspath"] ] for i in paths: if not os.path.exists(i): os.makedirs(i, 0o770) consoleHelper.printDone() # Connect to db try: consoleHelper.printNoNl("> Connecting to MySQL database... ") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"])) consoleHelper.printNoNl(" ") consoleHelper.printDone() except: # Exception while connecting to db consoleHelper.printError() consoleHelper.printColored( "[!] Error while connection to database. Please check your config.ini and run the server again", bcolors.RED) raise # Connect to redis try: consoleHelper.printNoNl("> Connecting to redis... ") glob.redis = redis.Redis(glob.conf.config["redis"]["host"], glob.conf.config["redis"]["port"], glob.conf.config["redis"]["database"], glob.conf.config["redis"]["password"]) glob.redis.ping() consoleHelper.printNoNl(" ") consoleHelper.printDone() except: # Exception while connecting to db consoleHelper.printError() consoleHelper.printColored( "[!] Error while connection to redis. Please check your config.ini and run the server again", bcolors.RED) raise # Empty redis cache #TODO: do we need this? try: glob.redis.eval( "return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "lets:*") except redis.exceptions.ResponseError: # Script returns error if there are no keys starting with peppy:* pass # Save lets version in redis glob.redis.set("lets:version", glob.VERSION) # Create threads pool try: consoleHelper.printNoNl("> Creating threads pool... ") glob.pool = ThreadPool(int(glob.conf.config["server"]["threads"])) consoleHelper.printDone() except: consoleHelper.printError() consoleHelper.printColored( "[!] Error while creating threads pool. Please check your config.ini and run the server again", bcolors.RED) # Load achievements consoleHelper.printNoNl("> Loading achievements... ") try: achievements = glob.db.fetchAll("SELECT * FROM achievements") for achievement in achievements: condition = eval( f"lambda score, mode_vn, stats: {achievement.pop('cond')}") glob.achievements.append( Achievement(_id=achievement['id'], file=achievement['icon'], name=achievement['name'], desc=achievement['description'], cond=condition)) except Exception as e: consoleHelper.printError() consoleHelper.printColored( "[!] Error while loading achievements! ({})".format( traceback.format_exc()), bcolors.RED, ) return 1 consoleHelper.printDone() # Set achievements version glob.redis.set("lets:achievements_version", glob.ACHIEVEMENTS_VERSION) consoleHelper.printColored( "Achievements version is {}".format(glob.ACHIEVEMENTS_VERSION), bcolors.YELLOW) # Print disallowed mods into console (Used to also assign it into variable but has been moved elsewhere) unranked_mods = [ key for key, value in glob.conf.extra["common"] ["rankable-mods"].items() if not value ] consoleHelper.printColored( "Unranked mods: {}".format(", ".join(unranked_mods)), bcolors.YELLOW) # Print allowed beatmap rank statuses allowed_beatmap_rank = [ key for key, value in glob.conf.extra["lets"] ["allowed-beatmap-rankstatus"].items() if value ] consoleHelper.printColored( "Allowed beatmap rank statuses: {}".format( ", ".join(allowed_beatmap_rank)), bcolors.YELLOW) # Make array of bools to respective rank id's glob.conf.extra["_allowed_beatmap_rank"] = [ getattr(rankedStatuses, key) for key in allowed_beatmap_rank ] # Store the allowed beatmap rank id's into glob # Discord if generalUtils.stringToBool(glob.conf.config["discord"]["enable"]): glob.schiavo = schiavo.schiavo( glob.conf.config["discord"]["boturl"], "**lets**") else: consoleHelper.printColored( "[!] Warning! Discord logging is disabled!", bcolors.YELLOW) # Check debug mods glob.debug = generalUtils.stringToBool( glob.conf.config["server"]["debug"]) if glob.debug: consoleHelper.printColored( "[!] Warning! Server running in debug mode!", bcolors.YELLOW) # Server port try: serverPort = int(glob.conf.config["server"]["port"]) except: consoleHelper.printColored( "[!] Invalid server port! Please check your config.ini and run the server again", bcolors.RED) # Make app glob.application = make_app() # Set up sentry try: glob.sentry = generalUtils.stringToBool( glob.conf.config["sentry"]["enable"]) if glob.sentry: glob.application.sentry_client = AsyncSentryClient( glob.conf.config["sentry"]["dsn"], release=glob.VERSION) else: consoleHelper.printColored( "[!] Warning! Sentry logging is disabled!", bcolors.YELLOW) except: consoleHelper.printColored( "[!] Error while starting Sentry client! Please check your config.ini and run the server again", bcolors.RED) # Set up Datadog try: if generalUtils.stringToBool( glob.conf.config["datadog"]["enable"]): glob.dog = datadogClient.datadogClient( glob.conf.config["datadog"]["apikey"], glob.conf.config["datadog"]["appkey"]) else: consoleHelper.printColored( "[!] Warning! Datadog stats tracking is disabled!", bcolors.YELLOW) except: consoleHelper.printColored( "[!] Error while starting Datadog client! Please check your config.ini and run the server again", bcolors.RED) # Connect to pubsub channels pubSub.listener(glob.redis, { "lets:beatmap_updates": beatmapUpdateHandler.handler(), }).start() # Prometheus port statsPort = None try: if glob.conf.config["prometheus"]["port"]: statsPort = int(glob.conf.config["prometheus"]["port"]) except: consoleHelper.printColored( "Invalid stats port! Please check your config.ini and run the server again", bcolors.YELLOW) raise if statsPort: consoleHelper.printColored( "Stats exporter listening on localhost:{}".format(statsPort), bcolors.GREEN) prometheus_client.start_http_server(statsPort, addr="127.0.0.1") # Server start message and console output consoleHelper.printColored( "> L.E.T.S. is listening for clients on {}:{}...".format( glob.conf.config["server"]["host"], serverPort), bcolors.GREEN) # Start Tornado glob.application.listen(serverPort, address=glob.conf.config["server"]["host"]) tornado.ioloop.IOLoop.instance().start() finally: # Perform some clean up print("> Disposing server... ") glob.fileBuffers.flushAll() consoleHelper.printColored("Goodbye!", bcolors.GREEN) return 0