diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..25f0f9ed --- /dev/null +++ b/.env.example @@ -0,0 +1,39 @@ +# HTTP Configuration +HTTP_PORT=2001 +HTTP_ADDRESS=0.0.0.0 +HTTP_THREAD_COUNT=4 +HTTP_USING_CLOUDFLARE=true + +# MySQL Database Configuration +MYSQL_HOST=localhost +MYSQL_PORT=2002 +MYSQL_USER= +MYSQL_PASSWORD= +MYSQL_DATABASE= +MYSQL_POOL_SIZE=10 + +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= +REDIS_DB=0 + +# Discord Webhook Configuration +DISCORD_RANKED_WEBHOOK_URL= + +# osu! Private Server Configuration (PS) +PS_NAME=RealistikOsu +PS_DOMAIN=ussr.pl +PS_BOT_USERNAME=RealistikBot +PS_BOT_USER_ID=999 +PS_MINIMUM_CLIENT_YEAR=2023 +PS_ENABLE_PY_COMMAND=true +PS_PY_COMMAND_WHITELIST=1000,1180 + +# Data Directory Configuration +DATA_BEATMAP_DIRECTORY=/path/to/your/beatmap/directory +DATA_GEOLOCATION_PATH=/path/to/geoloc/db +DATA_BIBLE_PATH=/path/to/bible + +# Misc Configuration. +SERVICE_READINESS_TIMEOUT=30 diff --git a/.gitignore b/.gitignore index b9b2c82a..1d5dd66e 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ **/__pycache__ **/build -config.ini filters.txt .data .idea @@ -8,4 +7,5 @@ redistest.py *.c *.so .pyenv -config.json +.vscode/ +.env diff --git a/.gitmodules b/.gitmodules deleted file mode 100755 index e69de29b..00000000 diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index baf324f5..00000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "/usr/bin/gcc", - "cStandard": "gnu11", - "cppStandard": "gnu++14", - "intelliSenseMode": "linux-gcc-x64" - } - ], - "version": 4 -} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c92e7326 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3.12 + +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +# NOTE: These are handled as image resources, and therefore moved into the image. +COPY ./resources/geolocation_database.mmdb /app/resources/geolocation_database.mmdb +COPY ./resources/bible.txt /app/resources/bible.txt + +ENV DATA_GEOLOCATION_PATH=/app/resources/geolocation_database.mmdb +ENV DATA_BIBLE_PATH=/app/resources/bible.txt + +# Requirements +COPY ./requirements/main.txt /app/requirements.txt +RUN python3.12 -m pip install -r /app/requirements.txt + +# Scripts +COPY ./scripts /app/scripts + +# Application. +COPY ./peppy /app/peppy + +ENTRYPOINT [ "/app/scripts/bootstrap.sh" ] diff --git a/Makefile b/Makefile index cd5080c7..0bb4dd45 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,3 @@ -all: build run - +#!/usr/bin/make build: - python3.9 setup.py build_ext --inplace - -run: - python3.9 pep.py + docker build -t peppy:latest . diff --git a/README.md b/README.md index c1443261..9ef32c8f 100755 --- a/README.md +++ b/README.md @@ -14,42 +14,17 @@ This portion of the RealistikOsu manages all of the real-time, packet related po ## Why is our fork better? This fork of pep.py has been developed specifically to suit the need of RealistikOsu. With the rapid growth of the server, more and more demand has been placed on us in regards of features alongside performance. The original repo features a large quantity of fatal flaws alongside performance hogs, and through our usage of the software, we have solved a majority of those issues. +- Full Dockerisation - Fixed multiplayer -- MASSIVE OPTIMISATIONS (your database will thank you) +- Major performance optimisations. - Relax and Autopilot support - Extended Redis API - Extended 3rd party API support - Customised HWID system - Extended in-game bot commands -- Python 3.9 support! +- Python 3.9 support ## Requirements To run pep.py, there is an list of requirements to ensure the server runs at all. -- Python >=3.6 +- Python >=3.9 - RealistikOsu MySQL Database -- Cython + GCC -- Linux (preferably Ubuntu 18.04) - -## Notes for potential users -If you are planning on using our fork of pep.py, there is a multitude of things to consider that are unique to our variant -- Low reliance on `userutils` for performance reasons. - -The entire `userutils` module promotes inefficient use of the database. This is especially bad on established servers with large -databases, where the cost of each query becomes more and more expensive with every user. This appends unnecessary stress on the -database and as a consequence, the server as a whole. -- Tendency to hardcode things. - -I, RealistikDash, have a bad habit of hardcoding things. While this is usually fine for the intended application of this, being used -on RealistikOsu, it may be a pain to scan through the code if you are attempting to run this on your server. In this scenario, I would -advise searching through `constants/rosuprivs.py` and `constants/serverPackets.py` for any references you would like to change. -- Private database - -As expected, our variant uses our own database schema. A copy of our database schema (designed for use with [USSR](https://github.com/RealistikOsu/USSR)) can be found [here!](https://github.com/RealistikOsu/USSR/blob/master/extras/db.sql) - -Due to the old nature of the origin code, the age of the modules is **quite large**. This means that we do not benefit from any improvements, -bugfixes or any other quality of life improvements of any new module updates. This is an issue with the whole Python based Ripple stack, and is -not an exclusive to RealistikOsu pep.py. **This issue is however planned on being addressed soon.** -- No IRC - -Due to the lack of usage from the RealistikOsu community, the entire IRC server has essentially been nuked. This is because while not being used, it -still took up a thread and served as dead code in the repo. Not much else to say other than that it was pretty much never used. diff --git a/build.sh b/build.sh deleted file mode 100755 index 62279396..00000000 --- a/build.sh +++ /dev/null @@ -1 +0,0 @@ -python3.9 setup.py build_ext --inplace diff --git a/common/.gitignore b/common/.gitignore deleted file mode 100755 index 88ce1ac2..00000000 --- a/common/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -**/build -**/__pycache__ -.idea -*.c -*.so -config.json diff --git a/config.py b/config.py deleted file mode 100644 index 971dc5ba..00000000 --- a/config.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -import os -from dataclasses import dataclass -from json import dump -from json import load -from typing import Any - -from logger import log as logger # TODO: tf. - - -@dataclass -class Config: - PORT: int = 5001 - DB_HOST: str = "localhost" - DB_USERNAME: str = "root" - DB_PASSWORD: str = "lole" - DB_DATABASE: str = "rosu" - DB_WORKERS: int = 4 - REDIS_HOST: str = "localhost" - REDIS_PORT: int = 6379 - REDIS_DB: int = 0 - REDIS_PASSWORD: str = "" - THREADS_COUNT: int = 2 - NEW_RANKED_WEBHOOK: str = "" - USING_CF: bool = True - SRV_NAME: str = "RealistikOsu" - SRV_DOMAIN: str = "ussr.pl" - SRV_BOT_NAME: str = "RealistikBot" - SRV_BOT_ID: int = 999 - SRV_MIN_CLIENT_YEAR: int = 2022 - MAPS_DIRECTORY: str = "/home/realistikosu/ussr/.data/maps/" - - -def read_config_json() -> dict[str, Any]: - with open("config.json") as f: - return load(f) - - -def write_config(config: Config): - with open("config.json", "w") as f: - dump(config.__dict__, f, indent=4) - - -def load_config() -> Config: - """Loads the config from the file, handling config updates. - Note: - Raises `SystemExit` on config update. - """ - - config_dict = {} - - if os.path.exists("config.json"): - config_dict = read_config_json() - - # Compare config json attributes with config class attributes - missing_keys = [key for key in Config.__annotations__ if key not in config_dict] - - # Remove extra fields - for key in tuple( - config_dict, - ): - if key not in Config.__annotations__: - del config_dict[key] - - # Create config regardless, populating it with missing keys and removing - # unnecessary keys. - config = Config(**config_dict) - - if missing_keys: - logger.info(f"Your config has been updated with {len(missing_keys)} new keys.") - logger.debug("Missing keys: " + ", ".join(missing_keys)) - write_config(config) - raise SystemExit(0) - - return config - - -config = load_config() diff --git a/collection/channels.py b/peppy/collection/channels.py old mode 100755 new mode 100644 similarity index 100% rename from collection/channels.py rename to peppy/collection/channels.py diff --git a/collection/matches.py b/peppy/collection/matches.py old mode 100755 new mode 100644 similarity index 100% rename from collection/matches.py rename to peppy/collection/matches.py diff --git a/collection/streams.py b/peppy/collection/streams.py old mode 100755 new mode 100644 similarity index 100% rename from collection/streams.py rename to peppy/collection/streams.py diff --git a/collection/tokens.py b/peppy/collection/tokens.py old mode 100755 new mode 100644 similarity index 98% rename from collection/tokens.py rename to peppy/collection/tokens.py index af0c36e0..8a5e4b5b --- a/collection/tokens.py +++ b/peppy/collection/tokens.py @@ -5,9 +5,8 @@ from typing import Optional import redis - +import settings from common.ripple import userUtils -from config import config from constants import serverPackets from constants.exceptions import periodicLoopException from events import logoutEvent @@ -169,7 +168,7 @@ def usersTimeoutCheckLoop(self) -> None: # Check timeout (fokabot is ignored) if ( value.pingTime < timeoutLimit - and value.userID != config.SRV_BOT_ID + and value.userID != settings.PS_BOT_USER_ID and not value.irc and not value.tournament ): diff --git a/common/__init__.py b/peppy/common/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from common/__init__.py rename to peppy/common/__init__.py diff --git a/common/constants/__init__.py b/peppy/common/constants/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from common/constants/__init__.py rename to peppy/common/constants/__init__.py diff --git a/common/constants/actions.py b/peppy/common/constants/actions.py old mode 100755 new mode 100644 similarity index 100% rename from common/constants/actions.py rename to peppy/common/constants/actions.py diff --git a/common/constants/bcolors.py b/peppy/common/constants/bcolors.py old mode 100755 new mode 100644 similarity index 100% rename from common/constants/bcolors.py rename to peppy/common/constants/bcolors.py diff --git a/common/constants/gameModes.py b/peppy/common/constants/gameModes.py old mode 100755 new mode 100644 similarity index 100% rename from common/constants/gameModes.py rename to peppy/common/constants/gameModes.py diff --git a/common/constants/mods.py b/peppy/common/constants/mods.py old mode 100755 new mode 100644 similarity index 100% rename from common/constants/mods.py rename to peppy/common/constants/mods.py diff --git a/common/constants/privileges.py b/peppy/common/constants/privileges.py old mode 100755 new mode 100644 similarity index 100% rename from common/constants/privileges.py rename to peppy/common/constants/privileges.py diff --git a/common/db/__init__.py b/peppy/common/db/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from common/db/__init__.py rename to peppy/common/db/__init__.py diff --git a/common/db/dbConnector.py b/peppy/common/db/dbConnector.py old mode 100755 new mode 100644 similarity index 87% rename from common/db/dbConnector.py rename to peppy/common/db/dbConnector.py index 36ac0d2f..82e24fad --- a/common/db/dbConnector.py +++ b/peppy/common/db/dbConnector.py @@ -6,9 +6,8 @@ from typing import Optional import MySQLdb -from MySQLdb.connections import Connection - from logger import log +from MySQLdb.connections import Connection class Worker: @@ -42,35 +41,21 @@ def __del__(self) -> None: class ConnectionPool: - """ - A MySQL workers pool - """ - - __slots__ = ( - "config", - "maxSize", - "pool", - "consecutiveEmptyPool", - ) - def __init__( self, host: str, + port: int, username: str, password: str, database: str, size: int = 128, ) -> None: - """ - Initialize a MySQL connections pool + self.host = host + self.port = port + self.username = username + self.password = password + self.database = database - :param host: MySQL host - :param username: MySQL username - :param password: MySQL password - :param database: MySQL database name - :param size: pool max size - """ - self.config = (host, username, password, database) self.maxSize = size self.pool = queue.Queue(self.maxSize) self.consecutiveEmptyPool = 0 @@ -84,7 +69,14 @@ def newWorker(self, temporary: bool = False) -> Worker: :return: instance of worker class """ db = MySQLdb.connect( - *self.config, autocommit=True, charset="utf8", use_unicode=True + host=self.host, + port=self.port, + user=self.username, + password=self.password, + database=self.database, + autocommit=True, + charset="utf8", + use_unicode=True, ) conn = Worker(db, temporary) return conn @@ -118,7 +110,7 @@ def getWorker(self) -> Worker: if self.pool.empty(): # The pool is empty. Spawn a new temporary worker log.warning("MySQL connections pool is empty. Using temporary worker.") - worker = self.newWorker(True) + worker = self.newWorker(temporary=True) # Increment saturation self.consecutiveEmptyPool += 1 @@ -172,22 +164,15 @@ class DatabasePool: def __init__( self, host: str, + port: int, username: str, password: str, database: str, initialSize: int, ) -> None: - """ - Initialize a new MySQL database helper with multiple workers. - This class is thread safe. - - :param host: MySQL host - :param username: MySQL username - :param password: MySQL password - :param database: MySQL database name - :param initialSize: initial pool size - """ - self.pool = ConnectionPool(host, username, password, database, initialSize) + self.pool = ConnectionPool( + host, port, username, password, database, initialSize, + ) def execute(self, query: str, params: object = ()) -> int: """ diff --git a/common/generalUtils.py b/peppy/common/generalUtils.py old mode 100755 new mode 100644 similarity index 100% rename from common/generalUtils.py rename to peppy/common/generalUtils.py diff --git a/common/redis/__init__.py b/peppy/common/redis/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from common/redis/__init__.py rename to peppy/common/redis/__init__.py diff --git a/common/redis/generalPubSubHandler.py b/peppy/common/redis/generalPubSubHandler.py old mode 100755 new mode 100644 similarity index 100% rename from common/redis/generalPubSubHandler.py rename to peppy/common/redis/generalPubSubHandler.py diff --git a/common/redis/pubSub.py b/peppy/common/redis/pubSub.py old mode 100755 new mode 100644 similarity index 100% rename from common/redis/pubSub.py rename to peppy/common/redis/pubSub.py diff --git a/common/ripple/__init__.py b/peppy/common/ripple/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from common/ripple/__init__.py rename to peppy/common/ripple/__init__.py diff --git a/common/ripple/scoreUtils.py b/peppy/common/ripple/scoreUtils.py old mode 100755 new mode 100644 similarity index 100% rename from common/ripple/scoreUtils.py rename to peppy/common/ripple/scoreUtils.py diff --git a/common/ripple/userUtils.py b/peppy/common/ripple/userUtils.py old mode 100755 new mode 100644 similarity index 99% rename from common/ripple/userUtils.py rename to peppy/common/ripple/userUtils.py index 7337fe93..309d6162 --- a/common/ripple/userUtils.py +++ b/peppy/common/ripple/userUtils.py @@ -12,13 +12,13 @@ from typing import Optional +import settings from common import generalUtils from common.constants import gameModes, mods from common.constants import privileges from logger import log from common.ripple import scoreUtils from objects import glob -from config import config def getBeatmapTime(beatmapID): @@ -1431,7 +1431,7 @@ def silence(userID, seconds, silenceReason, author=None): """ if author is None: - author = config.SRV_BOT_ID + author = settings.PS_BOT_USER_ID # db qurey silenceEndTime = int(time.time()) + seconds glob.db.execute( @@ -2239,7 +2239,7 @@ def insert_ban_log( """ if from_id is None: - from_id = config.SRV_BOT_ID + from_id = settings.PS_BOT_USER_ID if prefix: detail = "pep.py Autoban: " + detail @@ -2275,7 +2275,7 @@ def restrict_with_log( """ if from_id is None: - from_id = config.SRV_BOT_ID + from_id = settings.PS_BOT_USER_ID glob.db.execute( f"UPDATE users SET privileges = privileges & ~{privileges.USER_PUBLIC}, " @@ -2308,7 +2308,7 @@ def ban_with_log( """ if from_id is None: - from_id = config.SRV_BOT_ID + from_id = settings.PS_BOT_USER_ID glob.db.execute( f"UPDATE users SET privileges = 0, " diff --git a/common/web/__init__.py b/peppy/common/web/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from common/web/__init__.py rename to peppy/common/web/__init__.py diff --git a/common/web/requestsManager.py b/peppy/common/web/requestsManager.py old mode 100755 new mode 100644 similarity index 94% rename from common/web/requestsManager.py rename to peppy/common/web/requestsManager.py index 71b45249..40311f3a --- a/common/web/requestsManager.py +++ b/peppy/common/web/requestsManager.py @@ -2,13 +2,12 @@ from typing import Optional +import settings import tornado.gen import tornado.web -from tornado.ioloop import IOLoop - -from config import config from logger import log from objects import glob +from tornado.ioloop import IOLoop class asyncRequestHandler(tornado.web.RequestHandler): @@ -58,7 +57,10 @@ def getRequestIP(self) -> Optional[str]: """ # Check if they are connecting through a switcher - if "ppy.sh" in self.request.headers.get("Host", "") or not config.USING_CF: + if ( + "ppy.sh" in self.request.headers.get("Host", "") + or not settings.HTTP_USING_CLOUDFLARE + ): return self.request.headers.get("X-Real-IP") return self.request.headers.get("CF-Connecting-IP") diff --git a/constants/__init__.py b/peppy/constants/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from constants/__init__.py rename to peppy/constants/__init__.py diff --git a/constants/clientPackets.py b/peppy/constants/clientPackets.py old mode 100755 new mode 100644 similarity index 100% rename from constants/clientPackets.py rename to peppy/constants/clientPackets.py diff --git a/constants/dataTypes.py b/peppy/constants/dataTypes.py old mode 100755 new mode 100644 similarity index 100% rename from constants/dataTypes.py rename to peppy/constants/dataTypes.py diff --git a/constants/exceptions.py b/peppy/constants/exceptions.py old mode 100755 new mode 100644 similarity index 100% rename from constants/exceptions.py rename to peppy/constants/exceptions.py diff --git a/constants/fokabotCommands.py b/peppy/constants/fokabotCommands.py similarity index 97% rename from constants/fokabotCommands.py rename to peppy/constants/fokabotCommands.py index f30e997e..27e3ed46 100644 --- a/constants/fokabotCommands.py +++ b/peppy/constants/fokabotCommands.py @@ -15,16 +15,13 @@ import osupyparser import requests -from discord_webhook import DiscordEmbed -from discord_webhook import DiscordWebhook - +import settings from common import generalUtils from common.constants import gameModes from common.constants import mods from common.constants import privileges from common.ripple import userUtils from common.ripple.userUtils import restrict_with_log -from config import config from constants import exceptions from constants import matchModModes from constants import matchScoringTypes @@ -32,6 +29,8 @@ from constants import matchTeamTypes from constants import serverPackets from constants import slotStatuses +from discord_webhook import DiscordEmbed +from discord_webhook import DiscordWebhook from helpers import chatHelper as chat from helpers import systemHelper from helpers import user_helper @@ -77,7 +76,7 @@ def refresh_bmap(md5: str) -> None: def calc_completion(bmapid, n300, n100, n50, miss): bmap = osupyparser.OsuFile( - f"{config.MAPS_DIRECTORY}{bmapid}.osu", + f"{settings.DATA_BEATMAP_DIRECTORY}/{bmapid}.osu", ).parse_file() total_hits = int(n300 + n100 + n50 + miss) @@ -298,21 +297,21 @@ def editMap(fro: str, chan: str, message: list[str]) -> str: if set_check: # In theory it should work, practically i have no fucking clue. map_name = res["song_name"].split("[")[0].strip() - beatmap_url = f"the beatmap set [https://{config.SRV_DOMAIN}/beatmaps/{token.tillerino[0]} {map_name}]" + beatmap_url = f"the beatmap set [https://{settings.PS_DOMAIN}/beatmaps/{token.tillerino[0]} {map_name}]" else: map_name = res["song_name"] - beatmap_url = f"the beatmap [https://{config.SRV_DOMAIN}/beatmaps/{token.tillerino[0]} {map_name}]" + beatmap_url = f"the beatmap [https://{settings.PS_DOMAIN}/beatmaps/{token.tillerino[0]} {map_name}]" - if config.NEW_RANKED_WEBHOOK: - webhook = DiscordWebhook(url=config.NEW_RANKED_WEBHOOK) + if settings.DISCORD_RANKED_WEBHOOK_URL: + webhook = DiscordWebhook(url=settings.DISCORD_RANKED_WEBHOOK_URL) embed = DiscordEmbed( description=f"{status_readable.title()} by {fro}", color=242424, ) embed.set_author( name=f"{map_name} was just {status_readable}", - url=f"https://{config.SRV_DOMAIN}/beatmaps/{token.tillerino[0]}", - icon_url=f"https://a.{config.SRV_DOMAIN}/{token.userID}", + url=f"https://{settings.PS_DOMAIN}/beatmaps/{token.tillerino[0]}", + icon_url=f"https://a.{settings.PS_DOMAIN}/{token.userID}", ) embed.set_footer(text="via pep.py!") embed.set_image( @@ -324,7 +323,7 @@ def editMap(fro: str, chan: str, message: list[str]) -> str: chat.sendMessage( glob.BOT_NAME, "#announce", - f"[https://{config.SRV_DOMAIN}/u/{token.userID} {fro}] has {status_readable} {beatmap_url}", + f"[https://{settings.PS_DOMAIN}/u/{token.userID} {fro}] has {status_readable} {beatmap_url}", ) return f"Successfully {status_readable} a map." @@ -455,8 +454,8 @@ def kick(fro, chan, message): def fokabotReconnect(fro, chan, message): """Forces the bot to reconnect.""" # Check if the bot is already connected - if glob.tokens.getTokenFromUserID(config.SRV_BOT_ID) is not None: - return f"{glob.BOT_NAME} is already connected to {config.SRV_NAME}!" + if glob.tokens.getTokenFromUserID(settings.PS_BOT_USER_ID) is not None: + return f"{glob.BOT_NAME} is already connected to {settings.PS_NAME}!" # Bot is not connected, connect it fokabot.connect() @@ -676,8 +675,8 @@ def freeze(fro, chan, message): if targetToken is not None: targetToken.enqueue( serverPackets.notification( - f"You have been frozen! The {config.SRV_NAME} staff team has found you " - f"suspicious and would like to request a liveplay. Visit {config.SRV_DOMAIN} for more info.", + f"You have been frozen! The {settings.PS_NAME} staff team has found you " + f"suspicious and would like to request a liveplay. Visit {settings.PS_DOMAIN} for more info.", ), ) @@ -718,7 +717,7 @@ def unfreeze(fro, chan, message): targetToken.enqueue( serverPackets.notification( "Your account has been unfrozen! You have proven your legitemacy. " - f"Thank you and have fun playing on {config.SRV_NAME}!", + f"Thank you and have fun playing on {settings.PS_NAME}!", ), ) @@ -813,7 +812,7 @@ def systemStatus(fro, chan, message): return "\n".join( ( - f"---> {config.SRV_NAME} <---", + f"---> {settings.PS_NAME} <---", " - Realtime Server -", "> Running the RealistikOsu pep.py fork.", f"> Online Users: {data['connectedUsers']}", @@ -1034,8 +1033,8 @@ def tillerinoLast(fro, chan, message): token.tillerino[2] = fc_acc oppaiData = getPPMessage(token.userID, just_data=True) - user_embed = f"[https://{config.SRV_DOMAIN}/u/{token.userID} {fro}]" - map_embed = f"[http://{config.SRV_DOMAIN}/beatmaps/{data['bid']} {data['sn']}]" + user_embed = f"[https://{settings.PS_DOMAIN}/u/{token.userID} {fro}]" + map_embed = f"[http://{settings.PS_DOMAIN}/beatmaps/{data['bid']} {data['sn']}]" response = [ f"{user_embed} | {map_embed} +{generalUtils.readableMods(data['mods'])}", @@ -1356,7 +1355,7 @@ def mpInvite(): "That user is not connected to bancho right now.", ) _match = glob.matches.matches[getMatchIDFromChannel(chan)] - _match.invite(config.SRV_BOT_ID, userID) + _match.invite(settings.PS_BOT_USER_ID, userID) token.enqueue( serverPackets.notification( f"Please accept the invite you've just received from {glob.BOT_NAME} to " @@ -1792,7 +1791,7 @@ def bless(fro: str, chan: str, message: str) -> str: return "This user is not online, and may not be blessed." # Acquire bible from file. - with open("bible.txt") as stream: + with open(settings.DATA_BIBLE_PATH) as stream: holy_bible = stream.read() # Split the bible into 2000 char chunks (str writer and reader limit) @@ -1854,8 +1853,11 @@ def troll(fro: str, chan: str, message: str) -> str: def py(fro: str, chan: str, message: str) -> str: """Allows for code execution inside server (DANGEROUS COMMAND)""" + if not settings.PS_ENABLE_PY_COMMAND: + return "This command has been disabled on this server." + user = glob.tokens.getTokenFromUsername(username_safe(fro), safe=True) - if not user.userID in (1000, 1180): + if not user.userID in settings.PS_PY_COMMAND_WHITELIST: return "This command is reserved for head developers only!" if not message[0]: diff --git a/constants/matchModModes.py b/peppy/constants/matchModModes.py old mode 100755 new mode 100644 similarity index 100% rename from constants/matchModModes.py rename to peppy/constants/matchModModes.py diff --git a/constants/matchScoringTypes.py b/peppy/constants/matchScoringTypes.py old mode 100755 new mode 100644 similarity index 100% rename from constants/matchScoringTypes.py rename to peppy/constants/matchScoringTypes.py diff --git a/constants/matchTeamTypes.py b/peppy/constants/matchTeamTypes.py old mode 100755 new mode 100644 similarity index 100% rename from constants/matchTeamTypes.py rename to peppy/constants/matchTeamTypes.py diff --git a/constants/matchTeams.py b/peppy/constants/matchTeams.py old mode 100755 new mode 100644 similarity index 100% rename from constants/matchTeams.py rename to peppy/constants/matchTeams.py diff --git a/constants/packetIDs.py b/peppy/constants/packetIDs.py old mode 100755 new mode 100644 similarity index 100% rename from constants/packetIDs.py rename to peppy/constants/packetIDs.py diff --git a/constants/rosuprivs.py b/peppy/constants/rosuprivs.py similarity index 100% rename from constants/rosuprivs.py rename to peppy/constants/rosuprivs.py diff --git a/constants/serverPackets.py b/peppy/constants/serverPackets.py old mode 100755 new mode 100644 similarity index 98% rename from constants/serverPackets.py rename to peppy/constants/serverPackets.py index fc417d3c..8cd05d64 --- a/constants/serverPackets.py +++ b/peppy/constants/serverPackets.py @@ -1,9 +1,9 @@ """ Contains functions used to write specific server packets to byte streams """ from __future__ import annotations +import settings from common.constants import privileges from common.ripple import userUtils -from config import config from constants import dataTypes from constants import packetIDs from constants import userRanks @@ -30,7 +30,7 @@ def force_update(): def login_banned() -> bytes: return login_reply(-1) + notification( - f"Your account has been banned from {config.SRV_NAME}! " + f"Your account has been banned from {settings.PS_NAME}! " "Please contact a member of staff for more information.", ) @@ -41,7 +41,7 @@ def login_error(): def login_cheats() -> bytes: return login_reply(-1) + notification( - f"Your account has been restricted from {config.SRV_NAME}! " + f"Your account has been restricted from {settings.PS_NAME}! " "Please contact a member of staff for more information.", ) @@ -174,7 +174,6 @@ def user_stats(userID): if performancePoints >= 32767: rankedScore = performancePoints performancePoints = 0 - return packetHelper.buildPacket( packetIDs.server_userStats, diff --git a/constants/slotStatuses.py b/peppy/constants/slotStatuses.py old mode 100755 new mode 100644 similarity index 100% rename from constants/slotStatuses.py rename to peppy/constants/slotStatuses.py diff --git a/constants/userRanks.py b/peppy/constants/userRanks.py old mode 100755 new mode 100644 similarity index 100% rename from constants/userRanks.py rename to peppy/constants/userRanks.py diff --git a/events/__init__.py b/peppy/events/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from events/__init__.py rename to peppy/events/__init__.py diff --git a/events/beatmapInfoRequest.py b/peppy/events/beatmapInfoRequest.py similarity index 100% rename from events/beatmapInfoRequest.py rename to peppy/events/beatmapInfoRequest.py diff --git a/events/cantSpectateEvent.py b/peppy/events/cantSpectateEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/cantSpectateEvent.py rename to peppy/events/cantSpectateEvent.py diff --git a/events/changeActionEvent.py b/peppy/events/changeActionEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/changeActionEvent.py rename to peppy/events/changeActionEvent.py diff --git a/events/changeMatchModsEvent.py b/peppy/events/changeMatchModsEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/changeMatchModsEvent.py rename to peppy/events/changeMatchModsEvent.py diff --git a/events/changeMatchPasswordEvent.py b/peppy/events/changeMatchPasswordEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/changeMatchPasswordEvent.py rename to peppy/events/changeMatchPasswordEvent.py diff --git a/events/changeMatchSettingsEvent.py b/peppy/events/changeMatchSettingsEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/changeMatchSettingsEvent.py rename to peppy/events/changeMatchSettingsEvent.py diff --git a/events/changeSlotEvent.py b/peppy/events/changeSlotEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/changeSlotEvent.py rename to peppy/events/changeSlotEvent.py diff --git a/events/channelJoinEvent.py b/peppy/events/channelJoinEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/channelJoinEvent.py rename to peppy/events/channelJoinEvent.py diff --git a/events/channelPartEvent.py b/peppy/events/channelPartEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/channelPartEvent.py rename to peppy/events/channelPartEvent.py diff --git a/events/createMatchEvent.py b/peppy/events/createMatchEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/createMatchEvent.py rename to peppy/events/createMatchEvent.py diff --git a/events/friendAddEvent.py b/peppy/events/friendAddEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/friendAddEvent.py rename to peppy/events/friendAddEvent.py diff --git a/events/friendRemoveEvent.py b/peppy/events/friendRemoveEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/friendRemoveEvent.py rename to peppy/events/friendRemoveEvent.py diff --git a/events/joinLobbyEvent.py b/peppy/events/joinLobbyEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/joinLobbyEvent.py rename to peppy/events/joinLobbyEvent.py diff --git a/events/joinMatchEvent.py b/peppy/events/joinMatchEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/joinMatchEvent.py rename to peppy/events/joinMatchEvent.py diff --git a/events/loginEvent.py b/peppy/events/loginEvent.py old mode 100755 new mode 100644 similarity index 94% rename from events/loginEvent.py rename to peppy/events/loginEvent.py index aa795f27..7b73603e --- a/events/loginEvent.py +++ b/peppy/events/loginEvent.py @@ -6,10 +6,10 @@ import traceback from datetime import datetime +import settings from common.constants import privileges from common.ripple import userUtils from common.ripple.userUtils import restrict_with_log -from config import config from constants import exceptions from constants import serverPackets from helpers import chatHelper as chat @@ -24,23 +24,23 @@ UNFREEZE_NOTIF = serverPackets.notification( "Thank you for providing a liveplay! You have proven your legitemacy and " - f"have subsequently been unfrozen. Have fun playing {config.SRV_NAME}!", + f"have subsequently been unfrozen. Have fun playing {settings.PS_NAME}!", ) FREEZE_RES_NOTIF = serverPackets.notification( "Your window for liveplay sumbission has expired! Your account has been " "restricted as per our cheating policy. Please contact staff for more " - f"information on what can be done. This can be done via the {config.SRV_NAME} Discord server.", + f"information on what can be done. This can be done via the {settings.PS_NAME} Discord server.", ) FALLBACK_NOTIF = serverPackets.notification( - f"Fallback clients are not supported by {config.SRV_NAME}. This is due to a combination of missing features " - f"and server security. Please use a modern build of osu! to play {config.SRV_NAME}.", + f"Fallback clients are not supported by {settings.PS_NAME}. This is due to a combination of missing features " + f"and server security. Please use a modern build of osu! to play {settings.PS_NAME}.", ) OLD_CLIENT_NOTIF = serverPackets.notification( - f"You are using an outdated client (minimum release year {config.SRV_MIN_CLIENT_YEAR}). " - f"Please update your client to the latest version to play {config.SRV_NAME}.", + f"You are using an outdated client (minimum release year {settings.PS_MINIMUM_CLIENT_YEAR}). " + f"Please update your client to the latest version to play {settings.PS_NAME}.", ) BOT_ACCOUNT_RESPONSE = serverPackets.notification( - f"You may not log into a bot account using a real client. Please use a bot client to play {config.SRV_NAME}.", + f"You may not log into a bot account using a real client. Please use a bot client to play {settings.PS_NAME}.", ) @@ -95,7 +95,7 @@ def handle(tornadoRequest): # Invalid username log.error(f"Login failed for user {username} (user not found)!") responseData += serverPackets.notification( - f"{config.SRV_NAME}: This user does not exist!", + f"{settings.PS_NAME}: This user does not exist!", ) raise exceptions.loginFailedException() @@ -112,7 +112,7 @@ def handle(tornadoRequest): # Invalid password log.error(f"Login failed for user {username} (invalid password)!") responseData += serverPackets.notification( - f"{config.SRV_NAME}: Invalid password!", + f"{settings.PS_NAME}: Invalid password!", ) raise exceptions.loginFailedException() @@ -194,9 +194,9 @@ def handle(tornadoRequest): if frozen and not passed: responseToken.enqueue( serverPackets.notification( - f"The {config.SRV_NAME} staff team has found you suspicious and would like to request a liveplay. " + f"The {settings.PS_NAME} staff team has found you suspicious and would like to request a liveplay. " f"You have until {readabledate} (UTC) to provide a liveplay to the staff team. This can be done via " - f"the {config.SRV_NAME} Discord server. Failure to provide a valid liveplay will result in your account " + f"the {settings.PS_NAME} Discord server. Failure to provide a valid liveplay will result in your account " "being automatically restricted.", ), ) @@ -226,7 +226,7 @@ def handle(tornadoRequest): serverPackets.notification( f"Your supporter status expires in {expireIn}! Following this, you will lose your supporter privileges " "(such as the further profile customisation options, name changes or profile wipes) and will not " - f"be able to access supporter features. If you wish to keep supporting {config.SRV_NAME} and you " + f"be able to access supporter features. If you wish to keep supporting {settings.PS_NAME} and you " "don't want to lose your donor privileges, you can donate again by clicking on 'Donate' on our website.", ), ) @@ -362,7 +362,7 @@ def handle(tornadoRequest): raise exceptions.loginFailedException # Misc outdated client check - elif int(osuVersion[1:5]) < config.SRV_MIN_CLIENT_YEAR: + elif int(osuVersion[1:5]) < settings.PS_MINIMUM_CLIENT_YEAR: glob.tokens.deleteToken(userID) responseData += OLD_CLIENT_NOTIF raise exceptions.loginFailedException @@ -436,7 +436,7 @@ def handle(tornadoRequest): # Me and relesto are getting one as well lmao. UPDATE: Sky and Aochi gets it too lmao. elif userID in (1000, 1180, 3452, 4812): quote = ( - f"Hello I'm {config.SRV_BOT_NAME}! The server's official bot to assist you, " + f"Hello I'm {settings.PS_BOT_USERNAME}! The server's official bot to assist you, " "if you want to know what I can do just type !help" ) else: @@ -497,7 +497,7 @@ def handle(tornadoRequest): ) responseData += serverPackets.login_reply(-5) # Bancho error responseData += serverPackets.notification( - f"{config.SRV_NAME}: The server has experienced an error while logging you " + f"{settings.PS_NAME}: The server has experienced an error while logging you " "in! Please notify the developers for help.", ) finally: diff --git a/events/logoutEvent.py b/peppy/events/logoutEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/logoutEvent.py rename to peppy/events/logoutEvent.py diff --git a/events/matchBeatmapEvent.py b/peppy/events/matchBeatmapEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchBeatmapEvent.py rename to peppy/events/matchBeatmapEvent.py diff --git a/events/matchChangeTeamEvent.py b/peppy/events/matchChangeTeamEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchChangeTeamEvent.py rename to peppy/events/matchChangeTeamEvent.py diff --git a/events/matchCompleteEvent.py b/peppy/events/matchCompleteEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchCompleteEvent.py rename to peppy/events/matchCompleteEvent.py diff --git a/events/matchFailedEvent.py b/peppy/events/matchFailedEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchFailedEvent.py rename to peppy/events/matchFailedEvent.py diff --git a/events/matchFramesEvent.py b/peppy/events/matchFramesEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchFramesEvent.py rename to peppy/events/matchFramesEvent.py diff --git a/events/matchHasBeatmapEvent.py b/peppy/events/matchHasBeatmapEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchHasBeatmapEvent.py rename to peppy/events/matchHasBeatmapEvent.py diff --git a/events/matchInviteEvent.py b/peppy/events/matchInviteEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchInviteEvent.py rename to peppy/events/matchInviteEvent.py diff --git a/events/matchLockEvent.py b/peppy/events/matchLockEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchLockEvent.py rename to peppy/events/matchLockEvent.py diff --git a/events/matchNoBeatmapEvent.py b/peppy/events/matchNoBeatmapEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchNoBeatmapEvent.py rename to peppy/events/matchNoBeatmapEvent.py diff --git a/events/matchPlayerLoadEvent.py b/peppy/events/matchPlayerLoadEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchPlayerLoadEvent.py rename to peppy/events/matchPlayerLoadEvent.py diff --git a/events/matchReadyEvent.py b/peppy/events/matchReadyEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchReadyEvent.py rename to peppy/events/matchReadyEvent.py diff --git a/events/matchSkipEvent.py b/peppy/events/matchSkipEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchSkipEvent.py rename to peppy/events/matchSkipEvent.py diff --git a/events/matchStartEvent.py b/peppy/events/matchStartEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchStartEvent.py rename to peppy/events/matchStartEvent.py diff --git a/events/matchTransferHostEvent.py b/peppy/events/matchTransferHostEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/matchTransferHostEvent.py rename to peppy/events/matchTransferHostEvent.py diff --git a/events/partLobbyEvent.py b/peppy/events/partLobbyEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/partLobbyEvent.py rename to peppy/events/partLobbyEvent.py diff --git a/events/partMatchEvent.py b/peppy/events/partMatchEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/partMatchEvent.py rename to peppy/events/partMatchEvent.py diff --git a/events/requestStatusUpdateEvent.py b/peppy/events/requestStatusUpdateEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/requestStatusUpdateEvent.py rename to peppy/events/requestStatusUpdateEvent.py diff --git a/events/sendPrivateMessageEvent.py b/peppy/events/sendPrivateMessageEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/sendPrivateMessageEvent.py rename to peppy/events/sendPrivateMessageEvent.py diff --git a/events/sendPublicMessageEvent.py b/peppy/events/sendPublicMessageEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/sendPublicMessageEvent.py rename to peppy/events/sendPublicMessageEvent.py diff --git a/events/setAwayMessageEvent.py b/peppy/events/setAwayMessageEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/setAwayMessageEvent.py rename to peppy/events/setAwayMessageEvent.py diff --git a/events/spectateFramesEvent.py b/peppy/events/spectateFramesEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/spectateFramesEvent.py rename to peppy/events/spectateFramesEvent.py diff --git a/events/startSpectatingEvent.py b/peppy/events/startSpectatingEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/startSpectatingEvent.py rename to peppy/events/startSpectatingEvent.py diff --git a/events/stopSpectatingEvent.py b/peppy/events/stopSpectatingEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/stopSpectatingEvent.py rename to peppy/events/stopSpectatingEvent.py diff --git a/events/tournamentJoinMatchChannelEvent.py b/peppy/events/tournamentJoinMatchChannelEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/tournamentJoinMatchChannelEvent.py rename to peppy/events/tournamentJoinMatchChannelEvent.py diff --git a/events/tournamentLeaveMatchChannelEvent.py b/peppy/events/tournamentLeaveMatchChannelEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/tournamentLeaveMatchChannelEvent.py rename to peppy/events/tournamentLeaveMatchChannelEvent.py diff --git a/events/tournamentMatchInfoRequestEvent.py b/peppy/events/tournamentMatchInfoRequestEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/tournamentMatchInfoRequestEvent.py rename to peppy/events/tournamentMatchInfoRequestEvent.py diff --git a/events/userPanelRequestEvent.py b/peppy/events/userPanelRequestEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/userPanelRequestEvent.py rename to peppy/events/userPanelRequestEvent.py diff --git a/events/userStatsRequestEvent.py b/peppy/events/userStatsRequestEvent.py old mode 100755 new mode 100644 similarity index 100% rename from events/userStatsRequestEvent.py rename to peppy/events/userStatsRequestEvent.py diff --git a/handlers/__init__.py b/peppy/handlers/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from handlers/__init__.py rename to peppy/handlers/__init__.py diff --git a/handlers/apiAerisThing.py b/peppy/handlers/apiAerisThing.py similarity index 85% rename from handlers/apiAerisThing.py rename to peppy/handlers/apiAerisThing.py index 04d1125f..d7a60f20 100644 --- a/handlers/apiAerisThing.py +++ b/peppy/handlers/apiAerisThing.py @@ -3,11 +3,10 @@ import json import random +import settings import tornado.gen import tornado.web - from common.web import requestsManager -from config import config from objects import glob @@ -18,10 +17,10 @@ def asyncGet(self): """Handles the server info endpoint for the Aeris client.""" resp_dict = { "version": 0, - "motd": f"{config.SRV_NAME}\n" + "motd": f"{settings.PS_NAME}\n" + random.choice(glob.banchoConf.config["Quotes"]), "onlineUsers": len(glob.tokens.tokens), "icon": "https://ussr.pl/static/image/newlogo2.png", - "botID": config.SRV_BOT_ID, + "botID": settings.PS_BOT_USER_ID, } self.write(json.dumps(resp_dict)) diff --git a/handlers/apiOnlineUsers.py b/peppy/handlers/apiOnlineUsers.py similarity index 96% rename from handlers/apiOnlineUsers.py rename to peppy/handlers/apiOnlineUsers.py index e27abb3f..951bf825 100644 --- a/handlers/apiOnlineUsers.py +++ b/peppy/handlers/apiOnlineUsers.py @@ -1,28 +1,27 @@ -from __future__ import annotations - -import json - -import tornado.gen -import tornado.web - -from common.web import requestsManager -from objects import glob - - -class handler(requestsManager.asyncRequestHandler): - @tornado.web.asynchronous - @tornado.gen.engine - def asyncGet(self): - statusCode = 400 - data = {"message": "unknown error"} - try: - statusCode = 200 - data["message"] = "ok" - data["ids"] = [i.userID for i in glob.tokens.tokens.values()] - finally: - # Add status code to data - data["status"] = statusCode - - # Send response - self.write(json.dumps(data)) - self.set_status(statusCode) +from __future__ import annotations + +import json + +import tornado.gen +import tornado.web +from common.web import requestsManager +from objects import glob + + +class handler(requestsManager.asyncRequestHandler): + @tornado.web.asynchronous + @tornado.gen.engine + def asyncGet(self): + statusCode = 400 + data = {"message": "unknown error"} + try: + statusCode = 200 + data["message"] = "ok" + data["ids"] = [i.userID for i in glob.tokens.tokens.values()] + finally: + # Add status code to data + data["status"] = statusCode + + # Send response + self.write(json.dumps(data)) + self.set_status(statusCode) diff --git a/handlers/apiOnlineUsersHandler.py b/peppy/handlers/apiOnlineUsersHandler.py old mode 100755 new mode 100644 similarity index 99% rename from handlers/apiOnlineUsersHandler.py rename to peppy/handlers/apiOnlineUsersHandler.py index b7a18278..eda5aeab --- a/handlers/apiOnlineUsersHandler.py +++ b/peppy/handlers/apiOnlineUsersHandler.py @@ -4,7 +4,6 @@ import tornado.gen import tornado.web - from common.web import requestsManager from objects import glob diff --git a/handlers/apiServerStatusHandler.py b/peppy/handlers/apiServerStatusHandler.py old mode 100755 new mode 100644 similarity index 99% rename from handlers/apiServerStatusHandler.py rename to peppy/handlers/apiServerStatusHandler.py index 1c523336..8325bb91 --- a/handlers/apiServerStatusHandler.py +++ b/peppy/handlers/apiServerStatusHandler.py @@ -4,7 +4,6 @@ import tornado.gen import tornado.web - from common.web import requestsManager from objects import glob diff --git a/handlers/api_status.py b/peppy/handlers/api_status.py similarity index 99% rename from handlers/api_status.py rename to peppy/handlers/api_status.py index 0e268b56..6e2025f7 100644 --- a/handlers/api_status.py +++ b/peppy/handlers/api_status.py @@ -4,7 +4,6 @@ import tornado.gen import tornado.web - from common.web import requestsManager from logger import log from objects import glob diff --git a/handlers/mainHandler.pyx b/peppy/handlers/mainHandler.py old mode 100755 new mode 100644 similarity index 87% rename from handlers/mainHandler.pyx rename to peppy/handlers/mainHandler.py index 71892afd..97b8521c --- a/handlers/mainHandler.pyx +++ b/peppy/handlers/mainHandler.py @@ -1,15 +1,17 @@ +from __future__ import annotations + import datetime import sys import traceback +import settings import tornado.gen import tornado.web - -from logger import log from common.web import requestsManager from constants import exceptions from constants import packetIDs from constants import serverPackets +from events import beatmapInfoRequest from events import cantSpectateEvent from events import changeActionEvent from events import changeMatchModsEvent @@ -47,15 +49,14 @@ from events import spectateFramesEvent from events import startSpectatingEvent from events import stopSpectatingEvent -from events import userPanelRequestEvent -from events import userStatsRequestEvent -from events import tournamentMatchInfoRequestEvent from events import tournamentJoinMatchChannelEvent from events import tournamentLeaveMatchChannelEvent -from events import beatmapInfoRequest +from events import tournamentMatchInfoRequestEvent +from events import userPanelRequestEvent +from events import userStatsRequestEvent from helpers import packetHelper +from logger import log from objects import glob -from config import config # Placing this here so we do not have to register this every conn. @@ -129,7 +130,7 @@ def asyncPost(self): # Server's token string and request data responseTokenString = "" - responseData = bytes() + responseData = b'' if requestTokenString is None: # No token, first request. Handle login. @@ -157,21 +158,31 @@ def asyncPost(self): # Get packet ID, data length and data packetID = packetHelper.readPacketID(leftData) dataLength = packetHelper.readPacketLength(leftData) - packetData = requestData[pos:(pos+dataLength+7)] + packetData = requestData[pos : (pos + dataLength + 7)] # Process/ignore packet if packetID != 4: if packetID in eventHandler: - if not userToken.restricted or (userToken.restricted and packetID in packetsRestricted): + if not userToken.restricted or ( + userToken.restricted and packetID in packetsRestricted + ): eventHandler[packetID].handle(userToken, packetData) else: - log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID)) + log.warning( + "Ignored packet id from {} ({}) (user is restricted)".format( + requestTokenString, packetID, + ), + ) else: - log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID)) + log.warning( + "Unknown packet id from {} ({})".format( + requestTokenString, packetID, + ), + ) # Update pos so we can read the next stacked packet # +7 because we add packet ID bytes, unused byte and data length bytes - pos += dataLength+7 + pos += dataLength + 7 # Token queue built, send it responseTokenString = userToken.token @@ -180,10 +191,12 @@ def asyncPost(self): # Token not found. Get the user to be reconnected. responseData = serverPackets.server_restart(1) responseData += serverPackets.notification( - f"You don't seem to be logged into {config.SRV_NAME} anymore... " - "This is common during server restarts, trying to log you back in." + f"You don't seem to be logged into {settings.PS_NAME} anymore... " + "This is common during server restarts, trying to log you back in.", + ) + log.warning( + "Received unknown token! This is normal during server restarts. Reconnecting them.", ) - log.warning("Received unknown token! This is normal during server restarts. Reconnecting them.") finally: # Unlock token if userToken is not None: @@ -198,7 +211,6 @@ def asyncPost(self): # Send server's response to client # We don't use token object because we might not have a token (failed login) - self.write(responseData) # Add all the headers AFTER the response has been written @@ -214,5 +226,5 @@ def asyncPost(self): def asyncGet(self): # We are updating this to be full stealth self.write( - """Loading site... """ + """Loading site... """, ) diff --git a/helpers/__init__.py b/peppy/helpers/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from helpers/__init__.py rename to peppy/helpers/__init__.py diff --git a/helpers/chatHelper.py b/peppy/helpers/chatHelper.py old mode 100755 new mode 100644 similarity index 99% rename from helpers/chatHelper.py rename to peppy/helpers/chatHelper.py index d939a16e..91d3312b --- a/helpers/chatHelper.py +++ b/peppy/helpers/chatHelper.py @@ -5,8 +5,8 @@ from typing import TYPE_CHECKING from typing import Union +import settings from common.ripple import userUtils -from config import config from constants import exceptions from constants import serverPackets from events import logoutEvent @@ -348,7 +348,7 @@ def sendMessage(fro="", to="", message="", token=None, toIRC=True): log_message_db(token, recipientToken.userID, message) # Spam protection (ignore the bot) - if token.userID > config.SRV_BOT_ID or not token.admin: + if token.userID > settings.PS_BOT_USER_ID or not token.admin: token.spamProtection() # Some bot message diff --git a/helpers/consoleHelper.py b/peppy/helpers/consoleHelper.py old mode 100755 new mode 100644 similarity index 100% rename from helpers/consoleHelper.py rename to peppy/helpers/consoleHelper.py diff --git a/helpers/geo_helper.py b/peppy/helpers/geo_helper.py similarity index 98% rename from helpers/geo_helper.py rename to peppy/helpers/geo_helper.py index e9f48ccb..c82e7fcb 100644 --- a/helpers/geo_helper.py +++ b/peppy/helpers/geo_helper.py @@ -1,8 +1,9 @@ from __future__ import annotations +import settings from geoip2 import database -db_reader = database.Reader("ip_db_2.mmdb") +db_reader = database.Reader(settings.DATA_GEOLOCATION_PATH) countryCodes = { "IO": 104, diff --git a/helpers/packetHelper.pyx b/peppy/helpers/packetHelper.py old mode 100755 new mode 100644 similarity index 75% rename from helpers/packetHelper.pyx rename to peppy/helpers/packetHelper.py index ae827286..729f059b --- a/helpers/packetHelper.pyx +++ b/peppy/helpers/packetHelper.py @@ -1,15 +1,19 @@ +from __future__ import annotations + import struct + from constants import dataTypes -cpdef bytearray uleb128Encode(int num): + +def uleb128Encode(num: int) -> bytearray: """ Encode an int to uleb128 :param num: int to encode :return: bytearray with encoded number """ - cdef bytearray arr = bytearray() - cdef int length = 0 + arr = bytearray() + length = 0 if num == 0: return bytearray(b"\x00") @@ -19,32 +23,35 @@ num >>= 7 if num != 0: arr[length] |= 128 - length+=1 + length += 1 return arr -cpdef list uleb128Decode(bytes num): + +def uleb128Decode(num: bytes) -> tuple[int, int]: """ Decode a uleb128 to int :param num: encoded uleb128 int :return: (total, length) """ - cdef int shift = 0 - cdef list arr = [0,0] #total, length - cdef int b + + shift = 0 + value = 0 + length = 0 while True: - b = num[arr[1]] - arr[1]+=1 - arr[0] |= int(b & 127) << shift + b = num[length] + length += 1 + value |= int(b & 127) << shift if b & 128 == 0: break shift += 7 - return arr + return value, length -cpdef unpackData(bytes data, int dataType): + +def unpackData(data: bytes, dataType: int): """ Unpacks a single section of a packet. @@ -75,7 +82,8 @@ # Unpack return struct.unpack(unpackType, bytes(data))[0] -cpdef bytes packData(__data, int dataType): + +def packData(__data, dataType: int) -> bytes: """ Packs a single section of a packet. @@ -83,9 +91,9 @@ :param dataType: data type :return: packed bytes """ - cdef bytes data = bytes() # data to return - cdef bint pack = True # if True, use pack. False only with strings - cdef str packType + data = b'' # data to return + pack = True # if True, use pack. False only with strings + packType = "" # Get right pack Type if dataType == dataTypes.BBYTES: @@ -137,7 +145,8 @@ return data -cpdef bytes buildPacket(int __packet, __packetData = None): + +def buildPacket(__packet: int, __packetData=None) -> bytes: """ Builds a packet @@ -149,9 +158,9 @@ if __packetData is None: __packetData = [] # Set some variables - cdef bytes packetData = bytes() - cdef int packetLength = 0 - cdef bytes packetBytes = bytes() + packetData = b'' + packetLength = 0 + packetBytes = b'' # Pack packet data for i in __packetData: @@ -161,13 +170,14 @@ packetLength = len(packetData) # Return packet as bytes - packetBytes += struct.pack(" int: """ Read packetID (first two bytes) from a packet @@ -176,7 +186,8 @@ """ return unpackData(stream[0:2], dataTypes.UINT16) -cpdef int readPacketLength(bytes stream): + +def readPacketLength(stream: bytes) -> int: """ Read packet data length (3:7 bytes) from a packet @@ -186,7 +197,7 @@ return unpackData(stream[3:7], dataTypes.UINT32) -cpdef readPacketData(bytes stream, list structure=None, bint hasFirstBytes = True): +def readPacketData(stream: bytes, structure=None, hasFirstBytes=True): """ Read packet data from `stream` according to `structure` :param stream: packet bytes @@ -200,10 +211,9 @@ structure = [] # Read packet ID (first 2 bytes) - cdef dict data = {} + data = {} # Skip packet ID and packet length if needed - cdef start, end if hasFirstBytes: end = 7 start = 7 @@ -212,8 +222,6 @@ start = 0 # Read packet - cdef list i - cdef bint unpack for i in structure: start = end unpack = True @@ -223,15 +231,20 @@ unpack = False # Read length (uInt16) - length = unpackData(stream[start:start+2], dataTypes.UINT16) + length = unpackData(stream[start : start + 2], dataTypes.UINT16) # Read all int inside list data[i[0]] = [] - for j in range(0,length): - data[i[0]].append(unpackData(stream[start+2+(4*j):start+2+(4*(j+1))], dataTypes.SINT32)) + for j in range(0, length): + data[i[0]].append( + unpackData( + stream[start + 2 + (4 * j) : start + 2 + (4 * (j + 1))], + dataTypes.SINT32, + ), + ) # Update end - end = start+2+(4*length) + end = start + 2 + (4 * length) elif i[1] == dataTypes.STRING: # String, don't unpack unpack = False @@ -240,23 +253,23 @@ if stream[start] == 0: # Empty string data[i[0]] = "" - end = start+1 + end = start + 1 else: # Non empty string # Read length and calculate end - length = uleb128Decode(stream[start+1:]) - end = start+length[0]+length[1]+1 + length = uleb128Decode(stream[start + 1 :]) + end = start + length[0] + length[1] + 1 # Read bytes - data[i[0]] = stream[start+1+length[1]:end].decode() + data[i[0]] = stream[start + 1 + length[1] : end].decode() elif i[1] == dataTypes.BYTE: - end = start+1 + end = start + 1 elif i[1] in (dataTypes.UINT16, dataTypes.SINT16): - end = start+2 + end = start + 2 elif i[1] in (dataTypes.UINT32, dataTypes.SINT32, dataTypes.FFLOAT): - end = start+4 + end = start + 4 elif i[1] in (dataTypes.UINT64, dataTypes.SINT64): - end = start+8 + end = start + 8 # Unpack if needed if unpack: diff --git a/helpers/realistik_stuff.py b/peppy/helpers/realistik_stuff.py similarity index 100% rename from helpers/realistik_stuff.py rename to peppy/helpers/realistik_stuff.py diff --git a/helpers/status_helper.py b/peppy/helpers/status_helper.py similarity index 100% rename from helpers/status_helper.py rename to peppy/helpers/status_helper.py diff --git a/helpers/systemHelper.py b/peppy/helpers/systemHelper.py old mode 100755 new mode 100644 similarity index 99% rename from helpers/systemHelper.py rename to peppy/helpers/systemHelper.py index 9dd43525..cd018cc1 --- a/helpers/systemHelper.py +++ b/peppy/helpers/systemHelper.py @@ -8,7 +8,6 @@ import time import psutil - from constants import serverPackets from helpers import consoleHelper from logger import log diff --git a/helpers/user_helper.py b/peppy/helpers/user_helper.py similarity index 92% rename from helpers/user_helper.py rename to peppy/helpers/user_helper.py index d2ed29c4..bb812d96 100644 --- a/helpers/user_helper.py +++ b/peppy/helpers/user_helper.py @@ -1,12 +1,6 @@ from __future__ import annotations -from typing import Optional - import bcrypt - -from common.constants import privileges -from common.ripple.userUtils import removeFromLeaderboard -from config import config from objects import glob diff --git a/logger.py b/peppy/logger.py similarity index 100% rename from logger.py rename to peppy/logger.py diff --git a/pep.py b/peppy/main.py similarity index 81% rename from pep.py rename to peppy/main.py index 86918e58..a1e488fb 100644 --- a/pep.py +++ b/peppy/main.py @@ -3,14 +3,14 @@ import os import sys import traceback -from multiprocessing.pool import ThreadPool +from concurrent.futures import ThreadPoolExecutor -import redis +import redis.exceptions +import settings import tornado.gen import tornado.httpserver import tornado.ioloop import tornado.web - from common.db import dbConnector from common.redis import pubSub from handlers import api_status @@ -26,13 +26,13 @@ from objects import banchoConfig from objects import fokabot from objects import glob -from pubSubHandlers import banHandler -from pubSubHandlers import bot_msg_handler -from pubSubHandlers import disconnectHandler -from pubSubHandlers import notificationHandler -from pubSubHandlers import refreshPrivsHandler -from pubSubHandlers import updateSilenceHandler -from pubSubHandlers import updateStatsHandler +from redis_handlers import banHandler +from redis_handlers import bot_msg_handler +from redis_handlers import disconnectHandler +from redis_handlers import notificationHandler +from redis_handlers import refreshPrivsHandler +from redis_handlers import updateSilenceHandler +from redis_handlers import updateStatsHandler def make_app(): @@ -49,7 +49,6 @@ def make_app(): def main(): - """A main function to execute code.""" try: # Server start consoleHelper.printServerStartHeader(True) @@ -66,19 +65,20 @@ def main(): try: log.info("Connecting to MySQL database... ") glob.db = dbConnector.DatabasePool( - glob.config.DB_HOST, - glob.config.DB_USERNAME, - glob.config.DB_PASSWORD, - glob.config.DB_DATABASE, - glob.config.DB_WORKERS, + host=settings.MYSQL_HOST, + port=settings.MYSQL_PORT, + username=settings.MYSQL_USER, + password=settings.MYSQL_PASSWORD, + database=settings.MYSQL_DATABASE, + initialSize=settings.MYSQL_POOL_SIZE, ) log.info("Connecting to redis... ") glob.redis = redis.Redis( - glob.config.REDIS_HOST, - glob.config.REDIS_PORT, - glob.config.REDIS_DB, - glob.config.REDIS_PASSWORD, + host=settings.REDIS_HOST, + port=settings.REDIS_PORT, + password=settings.REDIS_PASSWORD, + db=settings.REDIS_DB, ) glob.redis.ping() except Exception: @@ -119,15 +119,12 @@ def main(): glob.tokens.deleteBanchoSessions() log.info("Complete!") - # Create threads pool - try: - log.info("Creating threads pool... ") - glob.pool = ThreadPool(glob.config.THREADS_COUNT) - log.info("Complete!") - except ValueError: - log.error( - "Error while creating threads pool. Please check your config.ini and run the server again", - ) + # Create thread pool + log.info("Creating thread pool...") + glob.pool = ThreadPoolExecutor( + max_workers=settings.HTTP_THREAD_COUNT, + ) + log.info("Complete!") # Start fokabot log.info("Connecting RealistikBot...") @@ -182,7 +179,7 @@ def main(): # Server start message and console output log.info( - f"pep.py listening for HTTP(s) clients on 127.0.0.1:{glob.config.PORT}...", + f"pep.py listening for HTTP(s) clients on {settings.HTTP_ADDRESS}:{settings.HTTP_PORT}...", ) # Connect to pubsub channels @@ -207,7 +204,7 @@ def main(): } # Start tornado - glob.application.listen(port=glob.config.PORT, address="127.0.0.1") + glob.application.listen(port=settings.HTTP_PORT, address=settings.HTTP_ADDRESS) tornado.ioloop.IOLoop.instance().start() finally: system.dispose() diff --git a/objects/__init__.py b/peppy/objects/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from objects/__init__.py rename to peppy/objects/__init__.py diff --git a/objects/banchoConfig.py b/peppy/objects/banchoConfig.py old mode 100755 new mode 100644 similarity index 100% rename from objects/banchoConfig.py rename to peppy/objects/banchoConfig.py diff --git a/objects/channel.py b/peppy/objects/channel.py old mode 100755 new mode 100644 similarity index 94% rename from objects/channel.py rename to peppy/objects/channel.py index dc504b4f..67861c51 --- a/objects/channel.py +++ b/peppy/objects/channel.py @@ -2,7 +2,7 @@ import logging -from config import config +import settings from constants import exceptions from objects import glob @@ -28,7 +28,7 @@ def __init__(self, name, description, publicRead, publicWrite, temp, hidden): self.hidden = hidden # Make Foka join the channel - fokaToken = glob.tokens.getTokenFromUserID(config.SRV_BOT_ID) + fokaToken = glob.tokens.getTokenFromUserID(settings.PS_BOT_USER_ID) if fokaToken is not None: try: fokaToken.joinChannel(self) diff --git a/objects/fokabot.py b/peppy/objects/fokabot.py similarity index 85% rename from objects/fokabot.py rename to peppy/objects/fokabot.py index 495c8795..30c14ea0 100644 --- a/objects/fokabot.py +++ b/peppy/objects/fokabot.py @@ -5,9 +5,9 @@ import traceback from importlib import reload +import settings from common.constants import actions from common.ripple import userUtils -from config import config from constants import fokabotCommands from constants import serverPackets from logger import log @@ -20,10 +20,10 @@ def connect(): :return: """ - glob.BOT_NAME = userUtils.getUsername(config.SRV_BOT_ID) - token = glob.tokens.addToken(config.SRV_BOT_ID) + glob.BOT_NAME = userUtils.getUsername(settings.PS_BOT_USER_ID) + token = glob.tokens.addToken(settings.PS_BOT_USER_ID) token.actionID = actions.WATCHING - token.actionText = f"over {config.SRV_NAME}!" + token.actionText = f"over {settings.PS_NAME}!" token.pp = 69 token.accuracy = 0.69 token.playcount = 69 @@ -31,8 +31,8 @@ def connect(): token.timeOffset = 0 token.country = 2 # this is retared, fuck it im keeping it as europe, couldnt find the uk as its ordered stupidly token.location = (39.01955903386848, 125.75276158057767) # Pyongyang red square - glob.streams.broadcast("main", serverPackets.user_presence(config.SRV_BOT_ID)) - glob.streams.broadcast("main", serverPackets.user_stats(config.SRV_BOT_ID)) + glob.streams.broadcast("main", serverPackets.user_presence(settings.PS_BOT_USER_ID)) + glob.streams.broadcast("main", serverPackets.user_stats(settings.PS_BOT_USER_ID)) def reload_commands(): @@ -46,7 +46,7 @@ def disconnect(): :return: """ - glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(config.SRV_BOT_ID)) + glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(settings.PS_BOT_USER_ID)) def fokabotResponse(fro, chan, message): @@ -102,7 +102,7 @@ def fokabotResponse(fro, chan, message): f"There was an issue while running '{cmd.trigger}' command. \nTraceback: {tb}", ) resp = [ - f"There was issue while processing your command, please report this to {config.SRV_NAME} developer!", + f"There was issue while processing your command, please report this to {settings.PS_NAME} developer!", ] # Debugging for staff if user.admin: diff --git a/objects/glob.py b/peppy/objects/glob.py similarity index 87% rename from objects/glob.py rename to peppy/objects/glob.py index 4f9226c8..54222622 100644 --- a/objects/glob.py +++ b/peppy/objects/glob.py @@ -2,24 +2,23 @@ from __future__ import annotations import time -from multiprocessing.pool import ThreadPool +from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING -from redis import Redis - +import settings from collection.channels import ChannelList from collection.matches import MatchList from collection.streams import StreamList from collection.tokens import TokenList from common.db.dbConnector import DatabasePool -from config import config from objects.banchoConfig import banchoConfig +from redis import Redis if TYPE_CHECKING: from helpers.status_helper import StatusManager # Consts. -BOT_NAME = config.SRV_BOT_NAME +BOT_NAME = settings.PS_BOT_USERNAME __version__ = "3.1.0" @@ -34,7 +33,7 @@ matches = MatchList() cached_passwords: dict[str, str] = {} chatFilters = None -pool: ThreadPool +pool: ThreadPoolExecutor busyThreads = 0 debug = False diff --git a/objects/match.py b/peppy/objects/match.py old mode 100755 new mode 100644 similarity index 99% rename from objects/match.py rename to peppy/objects/match.py index 891c2c79..327fa602 --- a/objects/match.py +++ b/peppy/objects/match.py @@ -7,7 +7,7 @@ from typing import Optional from typing import TYPE_CHECKING -from config import config +import settings from constants import dataTypes from constants import matchModModes from constants import matchScoringTypes @@ -521,7 +521,7 @@ def allPlayersCompleted(self) -> None: chat.sendMessage( glob.BOT_NAME, chanName, - f"Hey! Welcome to the {config.SRV_NAME} multiplayer! If you ever encounter " + f"Hey! Welcome to the {settings.PS_NAME} multiplayer! If you ever encounter " "a map you are unable to download through our direct, you can use the " "!mirror command to get an external download link!", ) @@ -802,7 +802,7 @@ def invite(self, fro: int, to: int): return # BOT IS BUSY!!! - if to == config.SRV_BOT_ID: + if to == settings.PS_BOT_USER_ID: chat.sendMessage( glob.BOT_NAME, froToken.username, diff --git a/objects/osuToken.py b/peppy/objects/osuToken.py similarity index 99% rename from objects/osuToken.py rename to peppy/objects/osuToken.py index 62b9ef77..d386c776 100644 --- a/objects/osuToken.py +++ b/peppy/objects/osuToken.py @@ -6,11 +6,11 @@ from typing import Optional from typing import TYPE_CHECKING +import settings from common.constants import actions from common.constants import gameModes from common.constants import privileges from common.ripple import userUtils -from config import config from constants import exceptions from constants import serverPackets from constants.rosuprivs import ADMIN_PRIVS @@ -165,7 +165,7 @@ def enqueue(self, bytes_: bytes) -> None: """ # Stop queuing stuff to the bot so we dont run out of mem - if self.userID == config.SRV_BOT_ID: + if self.userID == settings.PS_BOT_USER_ID: return with self._bufferLock: @@ -485,7 +485,7 @@ def silence(self, seconds=None, reason="", author: Optional[int] = None): """ if author is None: - author = config.SRV_BOT_ID + author = settings.PS_BOT_USER_ID if seconds is None: # Get silence expire from db if needed @@ -611,7 +611,7 @@ def notify_restricted(self) -> None: chat.sendMessage( glob.BOT_NAME, self.username, - f"Your account has been restricted! Please contact the {config.SRV_NAME} staff through our Discord server for more info!", + f"Your account has been restricted! Please contact the {settings.PS_NAME} staff through our Discord server for more info!", ) def notify_unrestricted(self) -> None: diff --git a/objects/stream.py b/peppy/objects/stream.py old mode 100755 new mode 100644 similarity index 100% rename from objects/stream.py rename to peppy/objects/stream.py diff --git a/pubSubHandlers/__init__.py b/peppy/redis_handlers/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from pubSubHandlers/__init__.py rename to peppy/redis_handlers/__init__.py diff --git a/pubSubHandlers/banHandler.py b/peppy/redis_handlers/banHandler.py old mode 100755 new mode 100644 similarity index 100% rename from pubSubHandlers/banHandler.py rename to peppy/redis_handlers/banHandler.py diff --git a/pubSubHandlers/bot_msg_handler.py b/peppy/redis_handlers/bot_msg_handler.py similarity index 100% rename from pubSubHandlers/bot_msg_handler.py rename to peppy/redis_handlers/bot_msg_handler.py diff --git a/pubSubHandlers/disconnectHandler.py b/peppy/redis_handlers/disconnectHandler.py old mode 100755 new mode 100644 similarity index 100% rename from pubSubHandlers/disconnectHandler.py rename to peppy/redis_handlers/disconnectHandler.py diff --git a/pubSubHandlers/notificationHandler.py b/peppy/redis_handlers/notificationHandler.py old mode 100755 new mode 100644 similarity index 100% rename from pubSubHandlers/notificationHandler.py rename to peppy/redis_handlers/notificationHandler.py diff --git a/pubSubHandlers/refreshPrivsHandler.py b/peppy/redis_handlers/refreshPrivsHandler.py similarity index 100% rename from pubSubHandlers/refreshPrivsHandler.py rename to peppy/redis_handlers/refreshPrivsHandler.py diff --git a/pubSubHandlers/updateSilenceHandler.py b/peppy/redis_handlers/updateSilenceHandler.py old mode 100755 new mode 100644 similarity index 100% rename from pubSubHandlers/updateSilenceHandler.py rename to peppy/redis_handlers/updateSilenceHandler.py diff --git a/pubSubHandlers/updateStatsHandler.py b/peppy/redis_handlers/updateStatsHandler.py old mode 100755 new mode 100644 similarity index 100% rename from pubSubHandlers/updateStatsHandler.py rename to peppy/redis_handlers/updateStatsHandler.py diff --git a/peppy/settings.py b/peppy/settings.py new file mode 100644 index 00000000..d21769f1 --- /dev/null +++ b/peppy/settings.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +import os + +from dotenv import load_dotenv + +load_dotenv() + +_BOOLEAN_STRINGS = ("true", "1", "yes") + + +def _parse_bool(value: str) -> bool: + return value.strip().lower() in _BOOLEAN_STRINGS + + +def _parse_int_list(value: str) -> list[int]: + if not value: + return [] + + return [int(i) for i in value.strip().replace(", ", ",").split(",")] + + +HTTP_PORT = int(os.environ["HTTP_PORT"]) +HTTP_ADDRESS = os.environ["HTTP_ADDRESS"] +HTTP_THREAD_COUNT = int(os.environ["HTTP_THREAD_COUNT"]) +HTTP_USING_CLOUDFLARE = _parse_bool(os.environ["HTTP_USING_CLOUDFLARE"]) + +MYSQL_HOST = os.environ["MYSQL_HOST"] +MYSQL_PORT = int(os.environ["MYSQL_PORT"]) +MYSQL_USER = os.environ["MYSQL_USER"] +MYSQL_PASSWORD = os.environ["MYSQL_PASSWORD"] +MYSQL_DATABASE = os.environ["MYSQL_DATABASE"] +MYSQL_POOL_SIZE = int(os.environ["MYSQL_POOL_SIZE"]) + +REDIS_HOST = os.environ["REDIS_HOST"] +REDIS_PORT = int(os.environ["REDIS_PORT"]) +REDIS_PASSWORD = os.environ["REDIS_PASSWORD"] +REDIS_DB = int(os.environ["REDIS_DB"]) + +DISCORD_RANKED_WEBHOOK_URL = os.environ["DISCORD_RANKED_WEBHOOK_URL"] + +# TODO: Find a better acronym to call an "osu! private server" than "PS" +PS_NAME = os.environ["PS_NAME"] +PS_DOMAIN = os.environ["PS_DOMAIN"] +PS_BOT_USERNAME = os.environ["PS_BOT_USERNAME"] +PS_BOT_USER_ID = int(os.environ["PS_BOT_USER_ID"]) +PS_MINIMUM_CLIENT_YEAR = int(os.environ["PS_MINIMUM_CLIENT_YEAR"]) +PS_ENABLE_PY_COMMAND = _parse_bool(os.environ["PS_ENABLE_PY_COMMAND"]) +PS_PY_COMMAND_WHITELIST = _parse_int_list(os.environ["PS_PY_COMMAND_WHITELIST"]) + +DATA_BEATMAP_DIRECTORY = os.environ["DATA_BEATMAP_DIRECTORY"] +DATA_GEOLOCATION_PATH = os.environ["DATA_GEOLOCATION_PATH"] +DATA_BIBLE_PATH = os.environ["DATA_BIBLE_PATH"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100755 index 288e6ed6..00000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -bcrypt==4.0.0 -colorama==0.4.5 -cython==0.29.32 -discord-webhook==0.17.0 -mysqlclient==2.0.3 -osupyparser -psutil==5.8.0 -redis==2.10.5 -requests==2.28.1 -tornado==4.4.2 diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 00000000..bae22f5a --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,2 @@ +-r main.txt +pre-commit diff --git a/requirements/main.txt b/requirements/main.txt new file mode 100644 index 00000000..d1985874 --- /dev/null +++ b/requirements/main.txt @@ -0,0 +1,10 @@ +bcrypt==4.1.3 +colorama==0.4.6 +discord-webhook==1.3.1 +mysqlclient==2.2.4 +osupyparser +psutil==5.9.8 +python-dotenv==1.0.1 +redis==5.0.4 +requests==2.32.3 +tornado==6.4 diff --git a/bible.txt b/resources/bible.txt similarity index 100% rename from bible.txt rename to resources/bible.txt diff --git a/ip_db_2.mmdb b/resources/geolocation_database.mmdb similarity index 100% rename from ip_db_2.mmdb rename to resources/geolocation_database.mmdb diff --git a/scripts/await_service.sh b/scripts/await_service.sh new file mode 100644 index 00000000..c73a3a3d --- /dev/null +++ b/scripts/await_service.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -uo pipefail + +await_service() +{ + local start_ts=$(date +%s) + while [ $(date +%s) -lt $((start_ts + $3)) ]; + do + (echo -n > /dev/tcp/$1/$2) > /dev/null + if [[ $? -eq 0 ]]; then + break + fi + sleep 1 + done + local end_ts=$(date +%s) + + if [ $(date +%s) -ge $((start_ts + $3)) ]; then + echo "Timeout occurred while waiting for $1:$2 to become available" + exit 1 + fi + + echo "$1:$2 is available after $((end_ts - start_ts)) seconds" +} + +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " + exit 1 +fi + +await_service $1 $2 $3 diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100644 index 00000000..b09a5a10 --- /dev/null +++ b/scripts/bootstrap.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -euo pipefail + +echo "Waiting for services to become available..." + +./scripts/await_service.sh $MYSQL_HOST $MYSQL_PORT $SERVICE_READINESS_TIMEOUT +./scripts/await_service.sh $REDIS_HOST $REDIS_PORT $SERVICE_READINESS_TIMEOUT + +exec /app/scripts/run_app.sh diff --git a/scripts/run_app.sh b/scripts/run_app.sh new file mode 100644 index 00000000..e81be3aa --- /dev/null +++ b/scripts/run_app.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -euo pipefail + +echo "Starting server..." + +cd /app/peppy/ +python3.12 main.py diff --git a/setup.py b/setup.py deleted file mode 100644 index ce1c0ec1..00000000 --- a/setup.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from Cython.Build import cythonize -from setuptools import Extension -from setuptools import setup - -# List of Cython modules to build -cython_modules = [ - Extension("handlers.mainHandler", ["handlers/mainHandler.pyx"]), - Extension("helpers.packetHelper", ["helpers/packetHelper.pyx"]), -] - -# Build the Cython modules -setup( - name="pep.py Cython modules", - ext_modules=cythonize(cython_modules, nthreads=4), -)