diff --git a/events/changeMatchSettingsEvent.py b/events/changeMatchSettingsEvent.py index 85d79c4..ce3b473 100644 --- a/events/changeMatchSettingsEvent.py +++ b/events/changeMatchSettingsEvent.py @@ -1,8 +1,6 @@ from objects import glob from constants import clientPackets from constants import matchModModes -from helpers import consoleHelper -from constants import bcolors import random from constants import matchTeamTypes from constants import matchTeams diff --git a/events/changeSlotEvent.py b/events/changeSlotEvent.py index 974a4a0..5f2d30b 100644 --- a/events/changeSlotEvent.py +++ b/events/changeSlotEvent.py @@ -1,12 +1,9 @@ from constants import clientPackets from objects import glob -from helpers import consoleHelper -from constants import bcolors def handle(userToken, packetData): # Get usertoken data userID = userToken.userID - username = userToken.username # Read packet data packetData = clientPackets.changeSlot(packetData) diff --git a/events/channelPartEvent.py b/events/channelPartEvent.py index cfcbe66..50d4800 100644 --- a/events/channelPartEvent.py +++ b/events/channelPartEvent.py @@ -1,6 +1,3 @@ -""" -Event called when someone parts a channel -""" from constants import clientPackets from helpers import chatHelper as chat diff --git a/events/loginEvent.py b/events/loginEvent.py index 6138c9c..2a3a5a4 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -2,27 +2,22 @@ from helpers import userHelper from constants import serverPackets from constants import exceptions from objects import glob -from helpers import consoleHelper -from constants import bcolors from helpers import locationHelper from helpers import countryHelper -import time -from helpers import generalFunctions import sys import traceback -from helpers import requestHelper -from helpers import discordBotHelper from helpers import logHelper as log from helpers import chatHelper as chat from constants import privileges +from helpers import requestHelper -def handle(tornadoRequest): +def handle(bottleRequest): # Data to return responseTokenString = "ayy" responseData = bytes() # Get IP from tornado request - requestIP = tornadoRequest.getRequestIP() + requestIP = requestHelper.getRequestIP(bottleRequest) # Avoid exceptions clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"] @@ -30,7 +25,8 @@ def handle(tornadoRequest): # Split POST body so we can get username/password/hardware data # 2:-3 thing is because requestData has some escape stuff that we don't need - loginData = str(tornadoRequest.request.body)[2:-3].split("\\n") + postBody = bottleRequest.body.read() + loginData = str(postBody)[2:-3].split("\\n") try: # If true, print error to console err = False diff --git a/events/logoutEvent.py b/events/logoutEvent.py index 2666f57..5472450 100644 --- a/events/logoutEvent.py +++ b/events/logoutEvent.py @@ -1,6 +1,4 @@ from objects import glob -from helpers import consoleHelper -from constants import bcolors from constants import serverPackets import time from helpers import logHelper as log diff --git a/events/matchFramesEvent.py b/events/matchFramesEvent.py index 5bd2fc0..6a297c9 100644 --- a/events/matchFramesEvent.py +++ b/events/matchFramesEvent.py @@ -1,7 +1,6 @@ from objects import glob from constants import slotStatuses from constants import serverPackets -from helpers import logHelper as log def handle(userToken, packetData): # Get usertoken data diff --git a/events/partLobbyEvent.py b/events/partLobbyEvent.py index e66224e..22f9f27 100644 --- a/events/partLobbyEvent.py +++ b/events/partLobbyEvent.py @@ -1,5 +1,4 @@ from objects import glob -from events import channelPartEvent from helpers import logHelper as log from helpers import chatHelper as chat diff --git a/events/partMatchEvent.py b/events/partMatchEvent.py index ad6f6d8..97aeca0 100644 --- a/events/partMatchEvent.py +++ b/events/partMatchEvent.py @@ -1,5 +1,4 @@ from objects import glob -from helpers import chatHelper as chat def handle(userToken, _): # get data from usertoken diff --git a/events/requestStatusUpdateEvent.py b/events/requestStatusUpdateEvent.py index 58a1f68..cd2021b 100644 --- a/events/requestStatusUpdateEvent.py +++ b/events/requestStatusUpdateEvent.py @@ -1,6 +1,4 @@ -from constants import clientPackets from constants import serverPackets -from helpers import userHelper from helpers import logHelper as log def handle(userToken, packetData): diff --git a/handlers/apiFokabotMessageHandler.py b/handlers/apiFokabotMessageHandler.py new file mode 100644 index 0000000..fc5eee0 --- /dev/null +++ b/handlers/apiFokabotMessageHandler.py @@ -0,0 +1,37 @@ +from constants import exceptions +import json +from objects import glob +from helpers import chatHelper +import bottle + +@bottle.route("/api/v1/fokabotMessage") +def GETApiFokabotMessage(): + statusCode = 400 + data = {"message": "unknown error"} + try: + # Check arguments + if "k" not in bottle.request.query or "to" not in bottle.request.query or "msg" not in bottle.request.query: + raise exceptions.invalidArgumentsException() + + # Check ci key + key = bottle.request.query["k"] + if key is None or key != glob.conf.config["server"]["cikey"]: + raise exceptions.invalidArgumentsException() + + # Send chat message + chatHelper.sendMessage("FokaBot", bottle.request.query["to"], bottle.request.query["msg"]) + + # Status code and message + statusCode = 200 + data["message"] = "ok" + except exceptions.invalidArgumentsException: + statusCode = 400 + data["message"] = "invalid parameters" + finally: + # Add status code to data + data["status"] = statusCode + + # Send response + bottle.response.status = statusCode + bottle.response.add_header("Content-Type", "application/json") + yield json.dumps(data) diff --git a/handlers/apiIsOnlineHandler.py b/handlers/apiIsOnlineHandler.py index cd50757..9664868 100644 --- a/handlers/apiIsOnlineHandler.py +++ b/handlers/apiIsOnlineHandler.py @@ -1,36 +1,32 @@ -from helpers import requestHelper from constants import exceptions import json from objects import glob +import bottle -class handler(requestHelper.asyncRequestHandler): - def asyncGet(self): +@bottle.route("/api/v1/isOnline") +def GETApiIsOnline(): + statusCode = 400 + data = {"message": "unknown error"} + try: + # Check arguments + if "u" not in bottle.request.query: + raise exceptions.invalidArgumentsException() + + # Get online staus + username = bottle.request.query["u"] + data["result"] = True if glob.tokens.getTokenFromUsername(username) != None else False + + # Status code and message + statusCode = 200 + data["message"] = "ok" + except exceptions.invalidArgumentsException: statusCode = 400 - data = {"message": "unknown error"} - try: - # Check arguments - if requestHelper.checkArguments(self.request.arguments, ["u"]) == False: - raise exceptions.invalidArgumentsException() + data["message"] = "missing required arguments" + finally: + # Add status code to data + data["status"] = statusCode - # Get online staus - username = self.get_argument("u") - if username == None: - data["result"] = False - else: - data["result"] = True if glob.tokens.getTokenFromUsername(username) != None else False - - # Status code and message - statusCode = 200 - data["message"] = "ok" - except exceptions.invalidArgumentsException: - statusCode = 400 - data["message"] = "missing required arguments" - finally: - # Add status code to data - data["status"] = statusCode - - # Send response - #self.clear() - self.write(json.dumps(data)) - self.set_status(statusCode) - #self.finish(json.dumps(data)) + # Send response + bottle.response.status = statusCode + bottle.response.add_header("Content-Type", "application/json") + yield json.dumps(data) diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py index d40f17b..6f40aed 100644 --- a/handlers/apiOnlineUsersHandler.py +++ b/handlers/apiOnlineUsersHandler.py @@ -1,24 +1,23 @@ -from helpers import requestHelper import json from objects import glob +import bottle -class handler(requestHelper.asyncRequestHandler): - def asyncGet(self): - statusCode = 400 - data = {"message": "unknown error"} - try: - # Get online users count - data["result"] = len(glob.tokens.tokens) +@bottle.route("/api/v1/onlineUsers") +def GETApiOnlineUsers(): + statusCode = 400 + data = {"message": "unknown error"} + try: + # Get online users count + data["result"] = len(glob.tokens.tokens) - # Status code and message - statusCode = 200 - data["message"] = "ok" - finally: - # Add status code to data - data["status"] = statusCode + # Status code and message + statusCode = 200 + data["message"] = "ok" + finally: + # Add status code to data + data["status"] = statusCode - # Send response - #self.clear() - self.write(json.dumps(data)) - self.set_status(statusCode) - #self.finish(json.dumps(data)) + # Send response + bottle.response.status = statusCode + bottle.response.add_header("Content-Type", "application/json") + yield json.dumps(data) diff --git a/handlers/apiServerStatusHandler.py b/handlers/apiServerStatusHandler.py index 38471bc..7158f75 100644 --- a/handlers/apiServerStatusHandler.py +++ b/handlers/apiServerStatusHandler.py @@ -1,24 +1,23 @@ -from helpers import requestHelper import json from objects import glob +import bottle -class handler(requestHelper.asyncRequestHandler): - def asyncGet(self): - statusCode = 400 - data = {"message": "unknown error"} - try: - # Get online users count - data["result"] = -1 if glob.restarting == True else 1 +@bottle.route("/api/v1/serverStatus") +def GETApiServerStatus(): + statusCode = 400 + data = {"message": "unknown error"} + try: + # Get online users count + data["result"] = -1 if glob.restarting == True else 1 - # Status code and message - statusCode = 200 - data["message"] = "ok" - finally: - # Add status code to data - data["status"] = statusCode + # Status code and message + statusCode = 200 + data["message"] = "ok" + finally: + # Add status code to data + data["status"] = statusCode - # Send response - #self.clear() - self.write(json.dumps(data)) - self.set_status(statusCode) - #self.finish(json.dumps(data)) + # Send response + bottle.response.status = statusCode + bottle.response.add_header("Content-Type", "application/json") + yield json.dumps(data) diff --git a/handlers/apiVerifiedStatusHandler.py b/handlers/apiVerifiedStatusHandler.py index bc23526..4aa2f9e 100644 --- a/handlers/apiVerifiedStatusHandler.py +++ b/handlers/apiVerifiedStatusHandler.py @@ -1,46 +1,48 @@ -from helpers import requestHelper -from helpers import logHelper as log import json from objects import glob from constants import exceptions +import bottle -class handler(requestHelper.asyncRequestHandler): - def asyncGet(self): +@bottle.route("/api/v1/verifiedStatus") +def GETApiVerifiedStatus(): + statusCode = 400 + data = {"message": "unknown error"} + try: + # Check arguments + if "u" not in bottle.request.query: + raise exceptions.invalidArgumentsException() + + # Get userID and its verified cache thing + # -1: Not in cache + # 0: Not verified (multiacc) + # 1: Verified + userID = bottle.request.query["u"] + callback = None + if "callback" in bottle.request.query: + callback = bottle.request.query["callback"] + data["result"] = -1 if userID not in glob.verifiedCache else glob.verifiedCache[userID] + + # Status code and message + statusCode = 200 + data["message"] = "ok" + except exceptions.invalidArgumentsException: statusCode = 400 - data = {"message": "unknown error"} - try: - # Check arguments - if requestHelper.checkArguments(self.request.arguments, ["u"]) == False: - raise exceptions.invalidArgumentsException() + data["message"] = "missing required arguments" + finally: + # Add status code to data + data["status"] = statusCode - # Get userID and its verified cache thing - # -1: Not in cache - # 0: Not verified (multiacc) - # 1: Verified - userID = self.get_argument("u") - data["result"] = -1 if userID not in glob.verifiedCache else glob.verifiedCache[userID] + # Send response + bottle.response.add_header("Access-Control-Allow-Origin", "*") + bottle.response.add_header("Content-Type", "application/json") - # Status code and message - statusCode = 200 - data["message"] = "ok" - except exceptions.invalidArgumentsException: - statusCode = 400 - data["message"] = "missing required arguments" - finally: - # Add status code to data - data["status"] = statusCode + # jquery meme + output = "" + if callback != None: + output += callback+"(" + output += json.dumps(data) + if callback != None: + output += ")" - # Send response - self.add_header("Access-Control-Allow-Origin", "*") - self.add_header("Content-Type", "application/json") - - # jquery meme - output = "" - if "callback" in self.request.arguments: - output += self.get_argument("callback")+"(" - output += json.dumps(data) - if "callback" in self.request.arguments: - output += ")" - - self.write(output) - self.set_status(statusCode) + bottle.response.status = statusCode + yield output diff --git a/handlers/ciTriggerHandler.py b/handlers/ciTriggerHandler.py index 5ffe294..cd44b5d 100644 --- a/handlers/ciTriggerHandler.py +++ b/handlers/ciTriggerHandler.py @@ -1,39 +1,38 @@ -from helpers import requestHelper from constants import exceptions import json from objects import glob from helpers import systemHelper from helpers import logHelper as log +import bottle -class handler(requestHelper.asyncRequestHandler): - def asyncGet(self): - statusCode = 400 - data = {"message": "unknown error"} - try: - # Check arguments - if requestHelper.checkArguments(self.request.arguments, ["k"]) == False: - raise exceptions.invalidArgumentsException() +@bottle.route("/api/v1/ciTrigger") +def GETCiTrigger(): + statusCode = 400 + data = {"message": "unknown error"} + try: + # Check arguments + if "k" not in bottle.request.query: + raise exceptions.invalidArgumentsException() - # Check ci key - key = self.get_argument("k") - if key is None or key != glob.conf.config["server"]["cikey"]: - raise exceptions.invalidArgumentsException() + # Check ci key + key = bottle.request.query["k"] + if key != glob.conf.config["server"]["cikey"]: + raise exceptions.invalidArgumentsException() - log.info("Ci event triggered!!") - systemHelper.scheduleShutdown(5, False, "A new Bancho update is available and the server will be restarted in 5 seconds. Thank you for your patience.") + log.info("Ci event triggered!!") + systemHelper.scheduleShutdown(5, False, "A new Bancho update is available and the server will be restarted in 5 seconds. Thank you for your patience.") - # Status code and message - statusCode = 200 - data["message"] = "ok" - except exceptions.invalidArgumentsException: - statusCode = 400 - data["message"] = "invalid ci key" - finally: - # Add status code to data - data["status"] = statusCode + # Status code and message + statusCode = 200 + data["message"] = "ok" + except exceptions.invalidArgumentsException: + statusCode = 403 + data["message"] = "invalid ci key" + finally: + # Add status code to data + data["status"] = statusCode - # Send response - #self.clear() - self.write(json.dumps(data)) - self.set_status(statusCode) - #self.finish(json.dumps(data)) + # Send response + bottle.response.status = statusCode + bottle.response.add_header("Content-Type", "application/json") + yield json.dumps(data) diff --git a/handlers/fokabotMessageHandler.py b/handlers/fokabotMessageHandler.py deleted file mode 100644 index 04bbf88..0000000 --- a/handlers/fokabotMessageHandler.py +++ /dev/null @@ -1,39 +0,0 @@ -from helpers import requestHelper -from constants import exceptions -import json -from objects import glob -from helpers import chatHelper -from helpers import logHelper as log - -class handler(requestHelper.asyncRequestHandler): - def asyncGet(self): - statusCode = 400 - data = {"message": "unknown error"} - try: - # Check arguments - if requestHelper.checkArguments(self.request.arguments, ["k", "to", "msg"]) == False: - raise exceptions.invalidArgumentsException() - - # Check ci key - key = self.get_argument("k") - if key is None or key != glob.conf.config["server"]["cikey"]: - raise exceptions.invalidArgumentsException() - - log.info("API REQUEST FOR FOKABOT MESSAGE AAAAAAA") - chatHelper.sendMessage("FokaBot", self.get_argument("to"), self.get_argument("msg")) - - # Status code and message - statusCode = 200 - data["message"] = "ok" - except exceptions.invalidArgumentsException: - statusCode = 400 - data["message"] = "invalid parameters" - finally: - # Add status code to data - data["status"] = statusCode - - # Send response - #self.clear() - self.write(json.dumps(data)) - self.set_status(statusCode) - #self.finish(json.dumps(data)) diff --git a/handlers/mainHandler.py b/handlers/mainHandler.py index 660532d..fa67103 100644 --- a/handlers/mainHandler.py +++ b/handlers/mainHandler.py @@ -1,10 +1,11 @@ +import bottle import datetime import gzip -from helpers import requestHelper from objects import glob +from helpers import packetHelper +from helpers import logHelper as log from constants import exceptions from constants import packetIDs -from helpers import packetHelper from constants import serverPackets from events import sendPublicMessageEvent from events import sendPrivateMessageEvent @@ -46,219 +47,198 @@ from events import userStatsRequestEvent from events import requestStatusUpdateEvent from events import userPanelRequestEvent -# Exception tracking -import tornado.web -import tornado.gen -import sys -import traceback -from raven.contrib.tornado import SentryMixin -from helpers import logHelper as log +@bottle.route("/", method="POST") +def POSTMain(): + # Track time if needed + if glob.outputRequestTime == True: + # Start time + st = datetime.datetime.now() -class handler(SentryMixin, requestHelper.asyncRequestHandler): - @tornado.web.asynchronous - @tornado.gen.engine - def asyncPost(self): + # Client's token string and request data + requestTokenString = bottle.request.headers.get("osu-token") + requestData = bottle.request.body.read() + + # Server's token string and request data + responseTokenString = "ayy" + responseData = bytes() + + if requestTokenString == None: + # No token, first request. Handle login. + responseTokenString, responseData = loginEvent.handle(bottle.request) + else: + userToken = None # default value try: - # Track time if needed - if glob.outputRequestTime == True: - # Start time - st = datetime.datetime.now() + # This is not the first packet, send response based on client's request + # Packet start position, used to read stacked packets + pos = 0 - # Client's token string and request data - requestTokenString = self.request.headers.get("osu-token") - requestData = self.request.body + # Make sure the token exists + if requestTokenString not in glob.tokens.tokens: + raise exceptions.tokenNotFoundException() - # Server's token string and request data - responseTokenString = "ayy" - responseData = bytes() + # Token exists, get its object and lock it + userToken = glob.tokens.tokens[requestTokenString] + userToken.lock.acquire() - if requestTokenString == None: - # No token, first request. Handle login. - responseTokenString, responseData = loginEvent.handle(self) - else: - userToken = None # default value - try: - # This is not the first packet, send response based on client's request - # Packet start position, used to read stacked packets - pos = 0 + # Keep reading packets until everything has been read + while pos < len(requestData): + # Get packet from stack starting from new packet + leftData = requestData[pos:] - # Make sure the token exists - if requestTokenString not in glob.tokens.tokens: - raise exceptions.tokenNotFoundException() + # Get packet ID, data length and data + packetID = packetHelper.readPacketID(leftData) + dataLength = packetHelper.readPacketLength(leftData) + packetData = requestData[pos:(pos+dataLength+7)] - # Token exists, get its object and lock it - userToken = glob.tokens.tokens[requestTokenString] - userToken.lock.acquire() + # Console output if needed + if glob.outputPackets == True and packetID != 4: + log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData))) - # Keep reading packets until everything has been read - while pos < len(requestData): - # Get packet from stack starting from new packet - leftData = requestData[pos:] + # Event handler + def handleEvent(ev): + def wrapper(): + ev.handle(userToken, packetData) + return wrapper - # Get packet ID, data length and data - packetID = packetHelper.readPacketID(leftData) - dataLength = packetHelper.readPacketLength(leftData) - packetData = requestData[pos:(pos+dataLength+7)] + eventHandler = { + # TODO: Rename packets and events + # TODO: Host check for multi + packetIDs.client_changeAction: handleEvent(changeActionEvent), + packetIDs.client_logout: handleEvent(logoutEvent), + packetIDs.client_friendAdd: handleEvent(friendAddEvent), + packetIDs.client_friendRemove: handleEvent(friendRemoveEvent), + packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent), + packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent), + packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent), + + packetIDs.client_channelJoin: handleEvent(channelJoinEvent), + packetIDs.client_channelPart: handleEvent(channelPartEvent), + packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent), + packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent), + packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent), - # Console output if needed - if glob.outputPackets == True and packetID != 4: - log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData))) + packetIDs.client_startSpectating: handleEvent(startSpectatingEvent), + packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent), + packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent), + packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent), - # Event handler - def handleEvent(ev): - def wrapper(): - ev.handle(userToken, packetData) - return wrapper + packetIDs.client_joinLobby: handleEvent(joinLobbyEvent), + packetIDs.client_partLobby: handleEvent(partLobbyEvent), + packetIDs.client_createMatch: handleEvent(createMatchEvent), + packetIDs.client_joinMatch: handleEvent(joinMatchEvent), + packetIDs.client_partMatch: handleEvent(partMatchEvent), + packetIDs.client_matchChangeSlot: handleEvent(changeSlotEvent), + packetIDs.client_matchChangeSettings: handleEvent(changeMatchSettingsEvent), + packetIDs.client_matchChangePassword: handleEvent(changeMatchPasswordEvent), + packetIDs.client_matchChangeMods: handleEvent(changeMatchModsEvent), + packetIDs.client_matchReady: handleEvent(matchReadyEvent), + packetIDs.client_matchNotReady: handleEvent(matchReadyEvent), + packetIDs.client_matchLock: handleEvent(matchLockEvent), + packetIDs.client_matchStart: handleEvent(matchStartEvent), + packetIDs.client_matchLoadComplete: handleEvent(matchPlayerLoadEvent), + packetIDs.client_matchSkipRequest: handleEvent(matchSkipEvent), + packetIDs.client_matchScoreUpdate: handleEvent(matchFramesEvent), + packetIDs.client_matchComplete: handleEvent(matchCompleteEvent), + packetIDs.client_matchNoBeatmap: handleEvent(matchNoBeatmapEvent), + packetIDs.client_matchHasBeatmap: handleEvent(matchHasBeatmapEvent), + packetIDs.client_matchTransferHost: handleEvent(matchTransferHostEvent), + packetIDs.client_matchFailed: handleEvent(matchFailedEvent), + packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent), + packetIDs.client_invite: handleEvent(matchInviteEvent), + } - eventHandler = { - # TODO: Rename packets and events - # TODO: Host check for multi - packetIDs.client_changeAction: handleEvent(changeActionEvent), - packetIDs.client_logout: handleEvent(logoutEvent), - packetIDs.client_friendAdd: handleEvent(friendAddEvent), - packetIDs.client_friendRemove: handleEvent(friendRemoveEvent), - packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent), - packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent), - packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent), - - packetIDs.client_channelJoin: handleEvent(channelJoinEvent), - packetIDs.client_channelPart: handleEvent(channelPartEvent), - packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent), - packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent), - packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent), + # Packets processed if in restricted mode. + # All other packets will be ignored if the user is in restricted mode + packetsRestricted = [ + packetIDs.client_logout, + packetIDs.client_userStatsRequest, + packetIDs.client_requestStatusUpdate, + packetIDs.client_userPanelRequest, + packetIDs.client_changeAction, + packetIDs.client_channelJoin, + packetIDs.client_channelPart, + ] - packetIDs.client_startSpectating: handleEvent(startSpectatingEvent), - packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent), - packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent), - packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent), + # Process/ignore packet + if packetID != 4: + if packetID in eventHandler: + if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted): + eventHandler[packetID]() + else: + log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID)) + else: + log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID)) - packetIDs.client_joinLobby: handleEvent(joinLobbyEvent), - packetIDs.client_partLobby: handleEvent(partLobbyEvent), - packetIDs.client_createMatch: handleEvent(createMatchEvent), - packetIDs.client_joinMatch: handleEvent(joinMatchEvent), - packetIDs.client_partMatch: handleEvent(partMatchEvent), - packetIDs.client_matchChangeSlot: handleEvent(changeSlotEvent), - packetIDs.client_matchChangeSettings: handleEvent(changeMatchSettingsEvent), - packetIDs.client_matchChangePassword: handleEvent(changeMatchPasswordEvent), - packetIDs.client_matchChangeMods: handleEvent(changeMatchModsEvent), - packetIDs.client_matchReady: handleEvent(matchReadyEvent), - packetIDs.client_matchNotReady: handleEvent(matchReadyEvent), - packetIDs.client_matchLock: handleEvent(matchLockEvent), - packetIDs.client_matchStart: handleEvent(matchStartEvent), - packetIDs.client_matchLoadComplete: handleEvent(matchPlayerLoadEvent), - packetIDs.client_matchSkipRequest: handleEvent(matchSkipEvent), - packetIDs.client_matchScoreUpdate: handleEvent(matchFramesEvent), - packetIDs.client_matchComplete: handleEvent(matchCompleteEvent), - packetIDs.client_matchNoBeatmap: handleEvent(matchNoBeatmapEvent), - packetIDs.client_matchHasBeatmap: handleEvent(matchHasBeatmapEvent), - packetIDs.client_matchTransferHost: handleEvent(matchTransferHostEvent), - packetIDs.client_matchFailed: handleEvent(matchFailedEvent), - packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent), - packetIDs.client_invite: handleEvent(matchInviteEvent), - } + # 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 - # Packets processed if in restricted mode. - # All other packets will be ignored if the user is in restricted mode - packetsRestricted = [ - packetIDs.client_logout, - packetIDs.client_userStatsRequest, - packetIDs.client_requestStatusUpdate, - packetIDs.client_userPanelRequest, - packetIDs.client_changeAction, - packetIDs.client_channelJoin, - packetIDs.client_channelPart, - ] + # Token queue built, send it + responseTokenString = userToken.token + responseData = userToken.queue + userToken.resetQueue() - # Process/ignore packet - if packetID != 4: - if packetID in eventHandler: - if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted): - eventHandler[packetID]() - else: - log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID)) - else: - log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID)) + # Update ping time for timeout + userToken.updatePingTime() + except exceptions.tokenNotFoundException: + # Token not found. Disconnect that user + responseData = serverPackets.loginError() + responseData += serverPackets.notification("Whoops! Something went wrong, please login again.") + log.warning("Received packet from unknown token ({}).".format(requestTokenString)) + log.info("{} has been disconnected (invalid token)".format(requestTokenString)) + finally: + # Unlock token + if userToken != None: + userToken.lock.release() - # 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 + if glob.outputRequestTime == True: + # End time + et = datetime.datetime.now() - # Token queue built, send it - responseTokenString = userToken.token - responseData = userToken.queue - userToken.resetQueue() + # Total time: + tt = float((et.microsecond-st.microsecond)/1000) + log.debug("Request time: {}ms".format(tt)) - # Update ping time for timeout - userToken.updatePingTime() - except exceptions.tokenNotFoundException: - # Token not found. Disconnect that user - responseData = serverPackets.loginError() - responseData += serverPackets.notification("Whoops! Something went wrong, please login again.") - log.warning("Received packet from unknown token ({}).".format(requestTokenString)) - log.info("{} has been disconnected (invalid token)".format(requestTokenString)) - finally: - # Unlock token - if userToken != None: - userToken.lock.release() + # Send server's response to client + # We don't use token object because we might not have a token (failed login) + if glob.gzip == True: + # First, write the gzipped response + responseData = gzip.compress(responseData, int(glob.conf.config["server"]["gziplevel"])) - if glob.outputRequestTime == True: - # End time - et = datetime.datetime.now() + # Then, add gzip headers + bottle.response.add_header("Vary", "Accept-Encoding") + bottle.response.add_header("Content-Encoding", "gzip") + else: + # First, write the response + responseData = responseData - # Total time: - tt = float((et.microsecond-st.microsecond)/1000) - log.debug("Request time: {}ms".format(tt)) + # Add all the headers AFTER the response has been written + bottle.response.status = 200 + bottle.response.add_header("cho-token", responseTokenString) + bottle.response.add_header("cho-protocol", "19") + bottle.response.add_header("Content-Type", "text/html; charset=UTF-8") + yield responseData - # Send server's response to client - # We don't use token object because we might not have a token (failed login) - if glob.gzip == True: - # First, write the gzipped response - self.write(gzip.compress(responseData, int(glob.conf.config["server"]["gziplevel"]))) - - # Then, add gzip headers - self.add_header("Vary", "Accept-Encoding") - self.add_header("Content-Encoding", "gzip") - else: - # First, write the response - self.write(responseData) - - # Add all the headers AFTER the response has been written - self.set_status(200) - self.add_header("cho-token", responseTokenString) - self.add_header("cho-protocol", "19") - #self.add_header("Keep-Alive", "timeout=5, max=100") - #self.add_header("Connection", "keep-alive") - self.add_header("Content-Type", "text/html; charset=UTF-8") - except: - log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc())) - if glob.sentry: - yield tornado.gen.Task(self.captureException, exc_info=True) - #finally: - # self.finish() - - @tornado.web.asynchronous - @tornado.gen.engine - def asyncGet(self): - html = "MA MAURO ESISTE?
"
-		html += "           _                 __
" - html += " (_) / /
" - html += " ______ __ ____ ____ / /____
" - html += " / ___/ / _ \\/ _ \\/ / _ \\
" - html += " / / / / /_) / /_) / / ____/
" - html += "/__/ /__/ .___/ .___/__/ \\_____/
" - html += " / / / /
" - html += " /__/ /__/
" - html += "PYTHON > ALL VERSION

" - html += "
" - html += " .. o .
" - html += " o.o o . o
" - html += " oo...
" - html += " __[]__
" - html += " phwr--> _\\:D/_/o_o_o_|__ u wot m8
" - html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/
" - html += " \\ . .. .. . /
" - html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
" - html += "

reverse engineering a protocol impossible to reverse engineer since always
we are actually reverse engineering bancho successfully. for the third time.

© Ripple team, 2016
" - self.write(html) - #yield tornado.gen.Task(self.captureMessage, "test") - #self.finish() +@bottle.route("/", method="GET") +def GETMain(): + html = "MA MAURO ESISTE?
"
+	html += "           _                 __
" + html += " (_) / /
" + html += " ______ __ ____ ____ / /____
" + html += " / ___/ / _ \\/ _ \\/ / _ \\
" + html += " / / / / /_) / /_) / / ____/
" + html += "/__/ /__/ .___/ .___/__/ \\_____/
" + html += " / / / /
" + html += " /__/ /__/
" + html += "PYTHON > ALL VERSION

" + html += "
" + html += " .. o .
" + html += " o.o o . o
" + html += " oo...
" + html += " __[]__
" + html += " phwr--> _\\:D/_/o_o_o_|__ u wot m8
" + html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/
" + html += " \\ . .. .. . /
" + html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
" + html += "

reverse engineering a protocol impossible to reverse engineer since always
we are actually reverse engineering bancho successfully. for the third time.

© Ripple team, 2016
" + yield html diff --git a/helpers/configHelper.py b/helpers/configHelper.py index 4953075..75bf7c3 100644 --- a/helpers/configHelper.py +++ b/helpers/configHelper.py @@ -14,14 +14,14 @@ class config: default = True # Check if config.ini exists and load/generate it - def __init__(self, __file): + def __init__(self, file): """ Initialize a config object - __file -- filename + file -- filename """ - self.fileName = __file + self.fileName = file if os.path.isfile(self.fileName): # config.ini found, load it self.config.read(self.fileName) @@ -49,7 +49,6 @@ class config: self.config.get("db","workers") self.config.get("server","port") - self.config.get("server","threads") self.config.get("server","gzip") self.config.get("server","gziplevel") self.config.get("server","cikey") @@ -94,7 +93,6 @@ class config: self.config.add_section("server") self.config.set("server", "port", "5001") - self.config.set("server", "threads", "16") self.config.set("server", "gzip", "1") self.config.set("server", "gziplevel", "6") self.config.set("server", "cikey", "changeme") diff --git a/helpers/databaseHelper.py b/helpers/databaseHelper.py deleted file mode 100644 index 6a7d17d..0000000 --- a/helpers/databaseHelper.py +++ /dev/null @@ -1,138 +0,0 @@ -import pymysql -from constants import bcolors -from helpers import consoleHelper -import threading -from objects import glob - -class db: - """A MySQL database connection""" - - connection = None - disconnected = False - pingTime = 600 - - def __init__(self, __host, __username, __password, __database, __pingTime = 600): - """ - Connect to MySQL database - - __host -- MySQL host name - __username -- MySQL username - __password -- MySQL password - __database -- MySQL database name - __pingTime -- MySQL database ping time (default: 600) - """ - - self.connection = pymysql.connect(host=__host, user=__username, password=__password, db=__database, cursorclass=pymysql.cursors.DictCursor, autocommit=True) - self.pingTime = __pingTime - self.pingLoop() - - - def bindParams(self, __query, __params): - """ - Replace every ? with the respective **escaped** parameter in array - - __query -- query with ?s - __params -- array with params - - return -- new query - """ - - for i in __params: - escaped = self.connection.escape(i) - __query = __query.replace("?", str(escaped), 1) - - return __query - - - def execute(self, __query, __params = None): - """ - Execute a SQL query - - __query -- query, can contain ?s - __params -- array with params. Optional - """ - - log.debug(query) - with self.connection.cursor() as cursor: - try: - # Bind params if needed - if __params != None: - __query = self.bindParams(__query, __params) - - # Execute the query - cursor.execute(__query) - finally: - # Close this connection - cursor.close() - - - def fetch(self, __query, __params = None, __all = False): - """ - Fetch the first (or all) element(s) of SQL query result - - __query -- query, can contain ?s - __params -- array with params. Optional - __all -- if true, will fetch all values. Same as fetchAll - - return -- dictionary with result data or False if failed - """ - - log.debug(query) - with self.connection.cursor() as cursor: - try: - # Bind params if needed - if __params != None: - __query = self.bindParams(__query, __params) - - # Execute the query with binded params - cursor.execute(__query) - - # Get first result and return it - if __all == False: - return cursor.fetchone() - else: - return cursor.fetchall() - finally: - # Close this connection - cursor.close() - - - def fetchAll(self, __query, __params = None): - """ - Fetch the all elements of SQL query result - - __query -- query, can contain ?s - __params -- array with params. Optional - - return -- dictionary with result data - """ - - return self.fetch(__query, __params, True) - - def pingLoop(self): - """ - Pings MySQL server. We need to ping/execute a query at least once every 8 hours - or the connection will die. - If called once, will recall after 30 minutes and so on, forever - CALL THIS FUNCTION ONLY ONCE! - """ - - # Default loop time - time = self.pingTime - - # Make sure the connection is alive - try: - # Try to ping and reconnect if not connected - self.connection.ping() - if self.disconnected == True: - # If we were disconnected, set disconnected to false and print message - self.disconnected = False - log.error("> Reconnected to MySQL server!", bcolors.GREEN) - except: - # Can't ping MySQL server. Show error and call loop in 5 seconds - log.error("[!] CRITICAL!! MySQL connection died! Make sure your MySQL server is running! Checking again in 5 seconds...", bcolors.RED) - self.disconnected = True - time = 5 - - # Schedule a new check (endless loop) - threading.Timer(time, self.pingLoop).start() diff --git a/helpers/logHelper.py b/helpers/logHelper.py index 447fc35..963377a 100644 --- a/helpers/logHelper.py +++ b/helpers/logHelper.py @@ -72,42 +72,42 @@ def logMessage(message, alertType = "INFO", messageColor = bcolors.ENDC, discord def warning(message, discord = None, alertDev = False): """ - Log a warning to stdout, warnings.log (always) and discord (optional) + Log a warning to stdout (always) and discord (optional) message -- warning message discord -- if not None, send message to that discord channel through schiavo. Optional. Default = None alertDev -- if True, send al hl to devs on discord. Optional. Default = False. """ - logMessage(message, "WARNING", bcolors.YELLOW, discord, alertDev, "warnings.txt") + logMessage(message, "WARNING", bcolors.YELLOW, discord, alertDev) def error(message, discord = None, alertDev = True): """ - Log an error to stdout, errors.log (always) and discord (optional) + Log an error to stdout (always) and discord (optional) message -- error message discord -- if not None, send message to that discord channel through schiavo. Optional. Default = None alertDev -- if True, send al hl to devs on discord. Optional. Default = False. """ - logMessage(message, "ERROR", bcolors.RED, discord, alertDev, "errors.txt") + logMessage(message, "ERROR", bcolors.RED, discord, alertDev) def info(message, discord = None, alertDev = False): """ - Log an error to stdout (and info.log) + Log an info message to stdout message -- info message discord -- if not None, send message to that discord channel through schiavo. Optional. Default = None alertDev -- if True, send al hl to devs on discord. Optional. Default = False. """ - logMessage(message, "INFO", bcolors.ENDC, discord, alertDev, "info.txt") + logMessage(message, "INFO", bcolors.ENDC, discord, alertDev) def debug(message): """ - Log a debug message to stdout and debug.log if server is running in debug mode + Log a debug message to stdout if server is running in debug mode message -- debug message """ if glob.debug == True: - logMessage(message, "DEBUG", bcolors.PINK, of="debug.txt") + logMessage(message, "DEBUG", bcolors.PINK) def chat(message): """ diff --git a/helpers/requestHelper.py b/helpers/requestHelper.py index 0599180..0365631 100644 --- a/helpers/requestHelper.py +++ b/helpers/requestHelper.py @@ -1,86 +1,7 @@ -import tornado -import tornado.web -import tornado.gen -from tornado.ioloop import IOLoop from objects import glob -from raven.contrib.tornado import SentryMixin -from raven.contrib.tornado import AsyncSentryClient -class asyncRequestHandler(tornado.web.RequestHandler): - """ - Tornado asynchronous request handler - create a class that extends this one (requestHelper.asyncRequestHandler) - use asyncGet() and asyncPost() instad of get() and post(). - Done. I'm not kidding. - """ - @tornado.web.asynchronous - @tornado.gen.engine - def get(self, *args, **kwargs): - try: - yield tornado.gen.Task(runBackground, (self.asyncGet, tuple(args), dict(kwargs))) - except Exception as e: - yield tornado.gen.Task(self.captureException, exc_info=True) - finally: - if not self._finished: - self.finish() - - @tornado.web.asynchronous - @tornado.gen.engine - def post(self, *args, **kwargs): - try: - yield tornado.gen.Task(runBackground, (self.asyncPost, tuple(args), dict(kwargs))) - except Exception as e: - yield tornado.gen.Task(self.captureException, exc_info=True) - finally: - if not self._finished: - self.finish() - - def asyncGet(self, *args, **kwargs): - self.send_error(405) - self.finish() - - def asyncPost(self, *args, **kwargs): - self.send_error(405) - self.finish() - - def getRequestIP(self): - realIP = self.request.headers.get("X-Forwarded-For") if glob.cloudflare == True else self.request.headers.get("X-Real-IP") - if realIP != None: - return realIP - return self.request.remote_ip - - -def runBackground(data, callback): - """ - Run a function in the background. - Used to handle multiple requests at the same time - """ - func, args, kwargs = data - def _callback(result): - IOLoop.instance().add_callback(lambda: callback(result)) - glob.pool.apply_async(func, args, kwargs, _callback) - - -def checkArguments(arguments, requiredArguments): - """ - Check that every requiredArguments elements are in arguments - - arguments -- full argument list, from tornado - requiredArguments -- required arguments list es: ["u", "ha"] - handler -- handler string name to print in exception. Optional - return -- True if all arguments are passed, none if not - """ - for i in requiredArguments: - if i not in arguments: - return False - return True - -def printArguments(t): - """ - Print passed arguments, for debug purposes - - t -- tornado object (self) - """ - print("ARGS::") - for i in t.request.arguments: - print ("{}={}".format(i, t.get_argument(i))) +def getRequestIP(bottleRequest): + realIP = bottleRequest.headers.get("X-Forwarded-For") if glob.cloudflare == True else bottleRequest.headers.get("X-Real-IP") + if realIP != None: + return realIP + return bottleRequest.environ.get("REMOTE_ADDR") \ No newline at end of file diff --git a/objects/banchoConfig.py b/objects/banchoConfig.py index 4294b59..b1aaafc 100644 --- a/objects/banchoConfig.py +++ b/objects/banchoConfig.py @@ -8,14 +8,14 @@ class banchoConfig: config = {"banchoMaintenance": False, "freeDirect": True, "menuIcon": "", "loginNotification": ""} - def __init__(self, __loadFromDB = True): + def __init__(self, loadFromDB = True): """ Initialize a banchoConfig object (and load bancho_settings from db) - [__loadFromDB -- if True, load values from db. If False, don't load values. Default: True] + [loadFromDB -- if True, load values from db. If False, don't load values. Default: True] """ - if __loadFromDB: + if loadFromDB: try: self.loadSettings() except: @@ -31,12 +31,12 @@ class banchoConfig: 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 setMaintenance(self, __maintenance): + def setMaintenance(self, maintenance): """ Turn on/off bancho maintenance mode. Write new value to db too - __maintenance -- if True, turn on maintenance mode. If false, turn it off + maintenance -- if True, turn on maintenance mode. If false, turn it off """ - self.config["banchoMaintenance"] = __maintenance - glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(__maintenance)]) + self.config["banchoMaintenance"] = maintenance + glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(maintenance)]) diff --git a/objects/channel.py b/objects/channel.py index b5289dc..134e6bd 100644 --- a/objects/channel.py +++ b/objects/channel.py @@ -5,22 +5,22 @@ class channel: A chat channel """ - def __init__(self, __name, __description, __publicRead, __publicWrite, temp, hidden): + def __init__(self, name, description, publicRead, publicWrite, temp, hidden): """ Create a new chat channel object - __name -- channel name - __description -- channel description - __publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins - __publicWrite -- bool, same as public read but relative to write permissions + name -- channel name + description -- channel description + publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins + publicWrite -- bool, same as public read but relative to write permissions temp -- if True, channel will be deleted when there's no one in the channel hidden -- if True, channel won't be shown in channels list """ - self.name = __name - self.description = __description - self.publicRead = __publicRead - self.publicWrite = __publicWrite + self.name = name + self.description = description + self.publicRead = publicRead + self.publicWrite = publicWrite self.moderated = False self.temp = temp self.connectedUsers = [999] # Fokabot is always connected to every channels (otherwise it doesn't show up in IRC users list) @@ -34,26 +34,26 @@ class channel: self.clientName = "#multiplayer" - def userJoin(self, __userID): + def userJoin(self, userID): """ Add a user to connected users - __userID -- user ID that joined the channel + userID -- user ID that joined the channel """ - if __userID not in self.connectedUsers: - self.connectedUsers.append(__userID) + if userID not in self.connectedUsers: + self.connectedUsers.append(userID) - def userPart(self, __userID): + def userPart(self, userID): """ Remove a user from connected users - __userID -- user ID that left the channel + userID -- user ID that left the channel """ - if __userID in self.connectedUsers: - self.connectedUsers.remove(__userID) + if userID in self.connectedUsers: + self.connectedUsers.remove(userID) # Remove temp channels if empty or there's only fokabot connected l = len(self.connectedUsers) diff --git a/objects/channelList.py b/objects/channelList.py index 22b73a6..93b6973 100644 --- a/objects/channelList.py +++ b/objects/channelList.py @@ -56,6 +56,7 @@ class channelList: self.channels[name] = channel.channel(name, "Chat", True, True, True, True) log.info("Created temp channel {}".format(name)) + def removeChannel(self, name): """ Removes a channel from channels list diff --git a/objects/fokabot.py b/objects/fokabot.py index 2f186fb..f3f6dbe 100644 --- a/objects/fokabot.py +++ b/objects/fokabot.py @@ -12,23 +12,17 @@ npRegex = re.compile("^https?:\\/\\/osu\\.ppy\\.sh\\/b\\/(\\d*)") def connect(): """Add FokaBot to connected users and send userpanel/stats packet to everyone""" - token = glob.tokens.addToken(999) token.actionID = actions.idle glob.tokens.enqueueAll(serverPackets.userPanel(999)) - ####glob.tokens.enqueueAll(serverPackets.userStats(999)) + glob.tokens.enqueueAll(serverPackets.userStats(999)) - # NOTE: Debug thing to set all users as connected - #users = glob.db.fetchAll("SELECT id FROM users") - #for i in users: - # t = glob.tokens.addToken(i["id"]) - # t.actionID = actions.idle def disconnect(): """Remove FokaBot from connected users""" - glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(999)) + def fokabotResponse(fro, chan, message): """ Check if a message has triggered fokabot (and return its response) @@ -39,7 +33,6 @@ def fokabotResponse(fro, chan, message): return -- fokabot's response string or False """ - for i in fokabotCommands.commands: # Loop though all commands #if i["trigger"] in message: diff --git a/objects/glob.py b/objects/glob.py index 9aac829..4182d26 100644 --- a/objects/glob.py +++ b/objects/glob.py @@ -20,7 +20,6 @@ tokens = tokenList.tokenList() channels = channelList.channelList() matches = matchList.matchList() restarting = False -pool = None fLocks = fileLocks.fileLocks() verifiedCache = {} cloudflare = False diff --git a/objects/logThread.py b/objects/logThread.py deleted file mode 100644 index 299dd9c..0000000 --- a/objects/logThread.py +++ /dev/null @@ -1,24 +0,0 @@ -''' -import threading - -class task: - def __init__(self, function, args = (), kwargs = {}): - self.function = function - self.args = args - self.kwargs = kwargs - -class logThread: - def __init__(self): - self.thread = threading.Thread() - self.queue = [] - - def enqueue(self, function, args = (), kwargs = {}): - self.queue.append(task(function, args, kwargs)) - - def run(self): - for i in self.queue: - self.thread = threading.Thread(i.function, i.args, i.kwargs) - self.thread.run() - self.thread.join() - self.queue = [] -''' diff --git a/objects/match.py b/objects/match.py index f5543ad..4b260ab 100644 --- a/objects/match.py +++ b/objects/match.py @@ -29,29 +29,29 @@ class match: matchModMode = matchModModes.normal seed = 0 - def __init__(self, __matchID, __matchName, __matchPassword, __beatmapID, __beatmapName, __beatmapMD5, __gameMode, __hostUserID): + def __init__(self, matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID): """ Create a new match object - __matchID -- match progressive identifier - __matchName -- match name, string - __matchPassword -- match md5 password. Leave empty for no password - __beatmapID -- beatmap ID - __beatmapName -- beatmap name, string - __beatmapMD5 -- beatmap md5 hash, string - __gameMode -- game mode ID. See gameModes.py - __hostUserID -- user id of the host + matchID -- match progressive identifier + matchName -- match name, string + matchPassword -- match md5 password. Leave empty for no password + beatmapID -- beatmap ID + beatmapName -- beatmap name, string + beatmapMD5 -- beatmap md5 hash, string + gameMode -- game mode ID. See gameModes.py + hostUserID -- user id of the host """ - self.matchID = __matchID + self.matchID = matchID self.inProgress = False self.mods = 0 - self.matchName = __matchName - self.matchPassword = __matchPassword - self.beatmapID = __beatmapID - self.beatmapName = __beatmapName - self.beatmapMD5 = __beatmapMD5 - self.hostUserID = __hostUserID - self.gameMode = __gameMode + self.matchName = matchName + self.matchPassword = matchPassword + self.beatmapID = beatmapID + self.beatmapName = beatmapName + self.beatmapMD5 = beatmapMD5 + self.hostUserID = hostUserID + self.gameMode = gameMode self.matchScoringTypes = matchScoringTypes.score # default values self.matchTeamType = matchTeamTypes.headToHead # default value self.matchModMode = matchModModes.normal # default value @@ -618,8 +618,6 @@ class match: self.setSlot(slotID, None, newTeam) self.sendUpdate() - - def sendUpdate(self): # Send to users in room for i in range(0,16): diff --git a/objects/matchList.py b/objects/matchList.py index f3e0961..7546487 100644 --- a/objects/matchList.py +++ b/objects/matchList.py @@ -13,50 +13,50 @@ class matchList: self.usersInLobby = [] self.lastID = 1 - def createMatch(self, __matchName, __matchPassword, __beatmapID, __beatmapName, __beatmapMD5, __gameMode, __hostUserID): + def createMatch(self, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID): """ Add a new match to matches list - __matchName -- match name, string - __matchPassword -- match md5 password. Leave empty for no password - __beatmapID -- beatmap ID - __beatmapName -- beatmap name, string - __beatmapMD5 -- beatmap md5 hash, string - __gameMode -- game mode ID. See gameModes.py - __hostUserID -- user id of who created the match + matchName -- match name, string + matchPassword -- match md5 password. Leave empty for no password + beatmapID -- beatmap ID + beatmapName -- beatmap name, string + beatmapMD5 -- beatmap md5 hash, string + gameMode -- game mode ID. See gameModes.py + hostUserID -- user id of who created the match return -- match ID """ # Add a new match to matches list matchID = self.lastID self.lastID+=1 - self.matches[matchID] = match.match(matchID, __matchName, __matchPassword, __beatmapID, __beatmapName, __beatmapMD5, __gameMode, __hostUserID) + self.matches[matchID] = match.match(matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID) return matchID - def lobbyUserJoin(self, __userID): + def lobbyUserJoin(self, userID): """ Add userID to users in lobby - __userID -- user who joined mp lobby + userID -- user who joined mp lobby """ # Make sure the user is not already in mp lobby - if __userID not in self.usersInLobby: + if userID not in self.usersInLobby: # We don't need to join #lobby, client will automatically send a packet for it - self.usersInLobby.append(__userID) + self.usersInLobby.append(userID) - def lobbyUserPart(self, __userID): + def lobbyUserPart(self, userID): """ Remove userID from users in lobby - __userID -- user who left mp lobby + userID -- user who left mp lobby """ # Make sure the user is in mp lobby - if __userID in self.usersInLobby: + if userID in self.usersInLobby: # Part lobby and #lobby channel - self.usersInLobby.remove(__userID) + self.usersInLobby.remove(userID) def disposeMatch(self, __matchID): diff --git a/objects/osuToken.py b/objects/osuToken.py index 4f8b23f..cb3efa0 100644 --- a/objects/osuToken.py +++ b/objects/osuToken.py @@ -35,11 +35,11 @@ class token: """ - def __init__(self, __userID, token = None, ip = "", irc = False, timeOffset = 0): + def __init__(self, userID, token = None, ip = "", irc = False, timeOffset = 0): """ Create a token object and set userID and token - __userID -- user associated to this token + userID -- user associated to this token token -- if passed, set token to that value if not passed, token will be generated ip -- client ip. optional. @@ -47,7 +47,7 @@ class token: """ # Set stuff - self.userID = __userID + self.userID = userID self.username = userHelper.getUsername(self.userID) self.privileges = userHelper.getPrivileges(self.userID) self.admin = userHelper.isInPrivilegeGroup(self.userID, "developer") or userHelper.isInPrivilegeGroup(self.userID, "community manager") @@ -102,12 +102,6 @@ class token: if ip != "": userHelper.saveBanchoSession(self.userID, self.ip) - # If we are restricted, send message from FokaBot to user - # NOTE: Sent later - #if self.restricted == True: - # self.setRestricted() - - def enqueue(self, __bytes): """ Add bytes (packets) to queue @@ -164,11 +158,11 @@ class token: return self.location[1] - def startSpectating(self, __userID): - """Set the spectating user to __userID + def startSpectating(self, userID): + """Set the spectating user to userID - __userID -- target userID""" - self.spectating = __userID + userID -- target userID""" + self.spectating = userID def stopSpectating(self): @@ -176,24 +170,24 @@ class token: self.spectating = 0 - def addSpectator(self, __userID): - """Add __userID to our spectators + def addSpectator(self, userID): + """Add userID to our spectators userID -- new spectator userID""" # Add userID to spectators if not already in - if __userID not in self.spectators: - self.spectators.append(__userID) + if userID not in self.spectators: + self.spectators.append(userID) - def removeSpectator(self, __userID): - """Remove __userID from our spectators + def removeSpectator(self, userID): + """Remove userID from our spectators userID -- old spectator userID""" # Remove spectator - if __userID in self.spectators: - self.spectators.remove(__userID) + if userID in self.spectators: + self.spectators.remove(userID) def setCountry(self, __countryID): diff --git a/pep.py b/pep.py index 236a07e..fb1a1ce 100644 --- a/pep.py +++ b/pep.py @@ -1,17 +1,12 @@ """Hello, pep.py here, ex-owner of ripple and prime minister of Ripwot.""" import sys import os -from multiprocessing.pool import ThreadPool import threading -# Tornado -import tornado.ioloop -import tornado.web -import tornado.httpserver -import tornado.gen - -# Raven -from raven.contrib.tornado import AsyncSentryClient +# Bottle +import bottle +from gevent import monkey as brit_monkey +brit_monkey.patch_all() # pep.py files from constants import bcolors @@ -25,27 +20,13 @@ from helpers import databaseHelperNew from helpers import generalFunctions from helpers import logHelper as log -from handlers import mainHandler -from handlers import apiIsOnlineHandler -from handlers import apiOnlineUsersHandler -from handlers import apiServerStatusHandler -from handlers import ciTriggerHandler -from handlers import apiVerifiedStatusHandler -from handlers import fokabotMessageHandler +# Raven +from raven import Client +from raven.contrib.bottle import Sentry +# IRC from irc import ircserver -def make_app(): - return tornado.web.Application([ - (r"/", mainHandler.handler), - (r"/api/v1/isOnline", apiIsOnlineHandler.handler), - (r"/api/v1/onlineUsers", apiOnlineUsersHandler.handler), - (r"/api/v1/serverStatus", apiServerStatusHandler.handler), - (r"/api/v1/ciTrigger", ciTriggerHandler.handler), - (r"/api/v1/verifiedStatus", apiVerifiedStatusHandler.handler), - (r"/api/v1/fokabotMessage", fokabotMessageHandler.handler) - ]) - if __name__ == "__main__": # Server start consoleHelper.printServerStartHeader(True) @@ -98,15 +79,6 @@ if __name__ == "__main__": glob.tokens.deleteBanchoSessions() consoleHelper.printDone() - # 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) - try: consoleHelper.printNoNl("> Loading chat filters... ") glob.chatFilters = chatFilters.chatFilters() @@ -168,13 +140,21 @@ if __name__ == "__main__": consoleHelper.printColored("[!] Warning! Server running in debug mode!", bcolors.YELLOW) # Make app - application = make_app() + app = bottle.app() + app.catchall = False + from handlers import mainHandler + from handlers import apiIsOnlineHandler + from handlers import apiServerStatusHandler + from handlers import ciTriggerHandler + from handlers import apiVerifiedStatusHandler + from handlers import apiFokabotMessageHandler # Set up sentry try: glob.sentry = generalFunctions.stringToBool(glob.conf.config["sentry"]["enable"]) if glob.sentry == True: - application.sentry_client = AsyncSentryClient(glob.conf.config["sentry"]["banchodns"], release=glob.VERSION) + client = Client(glob.conf.config["sentry"]["banchodns"], release=glob.VERSION) + app = Sentry(app, client) else: consoleHelper.printColored("[!] Warning! Sentry logging is disabled!", bcolors.YELLOW) except: @@ -204,9 +184,8 @@ if __name__ == "__main__": consoleHelper.printColored("[!] Invalid server port! Please check your config.ini and run the server again", bcolors.RED) # Server start message and console output - log.logMessage("Server started!", discord=True, of="info.txt", stdout=False) - consoleHelper.printColored("> Tornado listening for HTTP(s) clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN) + log.logMessage("Server started!", discord=True, stdout=False) + consoleHelper.printColored("> Bottle listening for HTTP(s) clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN) - # Start tornado - application.listen(serverPort) - tornado.ioloop.IOLoop.instance().start() + # Start bottle + bottle.run(app=app, host="0.0.0.0", port=serverPort, server="gevent", quiet=True) diff --git a/version b/version index e69de29..9dbb0c0 100644 --- a/version +++ b/version @@ -0,0 +1 @@ +1.7.0 \ No newline at end of file