.BANCHO. Bancho now uses mysqldb instead of pymysql

This commit is contained in:
Nyo 2016-05-31 22:49:30 +02:00
parent 26f64e8610
commit aebf910890
11 changed files with 474 additions and 319 deletions

View File

@ -481,7 +481,7 @@ def tillerinoLast(fro, chan, message):
FROM scores
LEFT JOIN beatmaps ON beatmaps.beatmap_md5=scores.beatmap_md5
LEFT JOIN users ON users.id = scores.userid
WHERE users.username = ?
WHERE users.username = %s
ORDER BY scores.time DESC
LIMIT 1""", [fro])
if data == None:

View File

@ -11,23 +11,22 @@ from helpers import generalFunctions
from events import channelJoinEvent
import sys
import traceback
from helpers import requestHelper
def handle(flaskRequest):
def handle(tornadoRequest):
# Data to return
responseTokenString = "ayy"
responseData = bytes()
# Get IP from flask request
requestIP = flaskRequest.headers.get('X-Real-IP')
if requestIP == None:
requestIP = flaskRequest.remote_addr
requestIP = tornadoRequest.getRequestIP()
# Console output
print("> Accepting connection from {}...".format(requestIP))
# 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(flaskRequest.data)[2:-3].split("\\n")
loginData = str(tornadoRequest.request.body)[2:-3].split("\\n")
# Process login
print("> Processing login request for {}...".format(loginData[0]))

215
handlers/mainHandler.py Normal file
View File

@ -0,0 +1,215 @@
import datetime
import gzip
from helpers import requestHelper
from objects import glob
from helpers import consoleHelper
from constants import bcolors
from constants import exceptions
from constants import packetIDs
from helpers import packetHelper
from constants import serverPackets
from events import sendPublicMessageEvent
from events import sendPrivateMessageEvent
from events import channelJoinEvent
from events import channelPartEvent
from events import changeActionEvent
from events import cantSpectateEvent
from events import startSpectatingEvent
from events import stopSpectatingEvent
from events import spectateFramesEvent
from events import friendAddEvent
from events import friendRemoveEvent
from events import logoutEvent
from events import loginEvent
from events import setAwayMessageEvent
from events import joinLobbyEvent
from events import createMatchEvent
from events import partLobbyEvent
from events import changeSlotEvent
from events import joinMatchEvent
from events import partMatchEvent
from events import changeMatchSettingsEvent
from events import changeMatchPasswordEvent
from events import changeMatchModsEvent
from events import matchReadyEvent
from events import matchLockEvent
from events import matchStartEvent
from events import matchPlayerLoadEvent
from events import matchSkipEvent
from events import matchFramesEvent
from events import matchCompleteEvent
from events import matchNoBeatmapEvent
from events import matchHasBeatmapEvent
from events import matchTransferHostEvent
from events import matchFailedEvent
from events import matchInviteEvent
from events import matchChangeTeamEvent
from helpers import discordBotHelper
import sys
import traceback
class handler(requestHelper.asyncRequestHandler):
def asyncPost(self):
try:
# Track time if needed
if glob.requestTime == True:
# Start time
st = datetime.datetime.now()
# Client's token string and request data
requestTokenString = self.request.headers.get('osu-token')
requestData = self.request.body
# Server's token string and request data
responseTokenString = "ayy"
responseData = bytes()
if requestTokenString == None:
# No token, first request. Handle login.
responseTokenString, responseData = loginEvent.handle(self)
else:
try:
# This is not the first packet, send response based on client's request
# Packet start position, used to read stacked packets
pos = 0
# Make sure the token exists
if requestTokenString not in glob.tokens.tokens:
raise exceptions.tokenNotFoundException()
# Token exists, get its object
userToken = glob.tokens.tokens[requestTokenString]
# Keep reading packets until everything has been read
while pos < len(requestData):
# Get packet from stack starting from new packet
leftData = requestData[pos:]
# Get packet ID, data length and data
packetID = packetHelper.readPacketID(leftData)
dataLength = packetHelper.readPacketLength(leftData)
packetData = requestData[pos:(pos+dataLength+7)]
# Console output if needed
if glob.conf.config["server"]["outputpackets"] == True and packetID != 4:
consoleHelper.printColored("Incoming packet ({})({}):".format(requestTokenString, userToken.username), bcolors.GREEN)
consoleHelper.printColored("Packet code: {}\nPacket length: {}\nSingle packet data: {}\n".format(str(packetID), str(dataLength), str(packetData)), bcolors.YELLOW)
# Event handler
def handleEvent(ev):
def wrapper():
ev.handle(userToken, packetData)
return wrapper
eventHandler = {
# TODO: Rename packets and events
# TODO: Host check for multi
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent),
packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent),
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
packetIDs.client_channelPart: handleEvent(channelPartEvent),
packetIDs.client_changeAction: handleEvent(changeActionEvent),
packetIDs.client_startSpectating: handleEvent(startSpectatingEvent),
packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent),
packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent),
packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent),
packetIDs.client_friendAdd: handleEvent(friendAddEvent),
packetIDs.client_friendRemove: handleEvent(friendRemoveEvent),
packetIDs.client_logout: handleEvent(logoutEvent),
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_invite: handleEvent(matchInviteEvent),
packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent)
}
if packetID != 4:
if packetID in eventHandler:
eventHandler[packetID]()
else:
consoleHelper.printColored("[!] Unknown packet id from {} ({})".format(requestTokenString, packetID), bcolors.RED)
# 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
# Token queue built, send it
responseTokenString = userToken.token
responseData = userToken.queue
userToken.resetQueue()
# 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.")
consoleHelper.printColored("[!] Received packet from unknown token ({}).".format(requestTokenString), bcolors.RED)
consoleHelper.printColored("> {} have been disconnected (invalid token)".format(requestTokenString), bcolors.YELLOW)
if glob.requestTime == True:
# End time
et = datetime.datetime.now()
# Total time:
tt = float((et.microsecond-st.microsecond)/1000)
consoleHelper.printColored("Request time: {}ms".format(tt), bcolors.PINK)
# Send server's response to client
# We don't use token object because we might not have a token (failed login)
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")
self.add_header("Vary", "Accept-Encoding")
self.add_header("Content-Encoding", "gzip")
self.write(gzip.compress(responseData, 6))
self.finish()
except:
msg = "**asyncppytornadovroom error** *(aka test server, ignore this)*\nUnhandled exception in mainHandler:\n```\n{}\n{}\n```".format(sys.exc_info(), traceback.format_exc())
consoleHelper.printColored("[!] {}".format(msg), bcolors.RED)
discordBotHelper.sendConfidential(msg)
def asyncGet(self):
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%}</style></head><body><pre>"
html += " _ __<br>"
html += " (_) / /<br>"
html += " ______ __ ____ ____ / /____<br>"
html += " / ___/ / _ \\/ _ \\/ / _ \\<br>"
html += " / / / / /_) / /_) / / ____/<br>"
html += "/__/ /__/ .___/ .___/__/ \\_____/<br>"
html += " / / / /<br>"
html += " /__/ /__/<br>"
html += "<b>PYTHON > ALL VERSION</b><br><br>"
html += "<marquee style='white-space:pre;'><br>"
html += " .. o .<br>"
html += " o.o o . o<br>"
html += " oo...<br>"
html += " __[]__<br>"
html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
html += " \\ . .. .. . /<br>"
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.</pre></body></html>"
self.write(html)
self.finish()

View File

@ -46,10 +46,9 @@ class config:
self.config.get("db","username")
self.config.get("db","password")
self.config.get("db","database")
self.config.get("db","pingtime")
self.config.get("db","workers")
self.config.get("server","server")
self.config.get("server","host")
self.config.get("server","threads")
self.config.get("server","port")
self.config.get("server","localizeusers")
self.config.get("server","outputpackets")
@ -57,12 +56,6 @@ class config:
self.config.get("server","timeouttime")
self.config.get("server","timeoutlooptime")
if self.config["server"]["server"] == "flask":
# Flask only config
self.config.get("flask","threaded")
self.config.get("flask","debug")
self.config.get("flask","logger")
self.config.get("discord","enable")
self.config.get("discord","boturl")
@ -85,11 +78,10 @@ class config:
self.config.set("db", "username", "root")
self.config.set("db", "password", "")
self.config.set("db", "database", "ripple")
self.config.set("db", "pingtime", "600")
self.config.set("db", "workers", "4")
self.config.add_section("server")
self.config.set("server", "server", "tornado")
self.config.set("server", "host", "0.0.0.0")
self.config.set("server", "threads", "16")
self.config.set("server", "port", "5001")
self.config.set("server", "localizeusers", "1")
self.config.set("server", "outputpackets", "0")
@ -97,11 +89,6 @@ class config:
self.config.set("server", "timeoutlooptime", "100")
self.config.set("server", "timeouttime", "100")
self.config.add_section("flask")
self.config.set("flask", "threaded", "1")
self.config.set("flask", "debug", "0")
self.config.set("flask", "logger", "0")
self.config.add_section("ci")
self.config.set("ci", "key", "changeme")

View File

@ -0,0 +1,118 @@
import MySQLdb
import threading
class mysqlWorker:
"""
Instance of a pettirosso meme
"""
def __init__(self, wid, host, username, password, database):
"""
Create a pettirosso meme (mysql worker)
wid -- worker id
host -- hostname
username -- MySQL username
password -- MySQL password
database -- MySQL database name
"""
self.wid = wid
self.connection = MySQLdb.connect(host, username, password, database)
self.connection.autocommit(True)
self.ready = True
self.lock = threading.Lock()
class db:
"""
A MySQL db connection with multiple workers
"""
def __init__(self, host, username, password, database, workers):
"""
Create MySQL workers aka pettirossi meme
host -- hostname
username -- MySQL username
password -- MySQL password
database -- MySQL database name
workers -- Number of workers to spawn
"""
#self.lock = threading.Lock()
#self.connection = MySQLdb.connect(host, username, password, database)
self.workers = []
self.lastWorker = 0
self.workersNumber = workers
for i in range(0,self.workersNumber):
print("> Spawning MySQL pettirosso meme {}".format(i))
self.workers.append(mysqlWorker(i, host, username, password, database))
def getWorker(self):
"""
Return a worker object (round-robin way)
return -- worker object
"""
if self.lastWorker >= self.workersNumber-1:
self.lastWorker = 0
else:
self.lastWorker += 1
#print("Using worker {}".format(self.lastWorker))
return self.workers[self.lastWorker]
def execute(self, query, params = ()):
"""
Executes a query
query -- Query to execute. You can bind parameters with %s
params -- Parameters list. First element replaces first %s and so on. Optional.
"""
# Get a worker and acquire its lock
worker = self.getWorker()
worker.lock.acquire()
try:
# Create cursor, execute query and commit
cursor = worker.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute(query, params)
return cursor.lastrowid
finally:
# Close the cursor and release worker's lock
if cursor:
cursor.close()
worker.lock.release()
def fetch(self, query, params = (), all = False):
"""
Fetch a single value from db that matches given query
query -- Query to execute. You can bind parameters with %s
params -- Parameters list. First element replaces first %s and so on. Optional.
all -- Fetch one or all values. Used internally. Use fetchAll if you want to fetch all values.
"""
# Get a worker and acquire its lock
worker = self.getWorker()
worker.lock.acquire()
try:
# Create cursor, execute the query and fetch one/all result(s)
cursor = worker.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute(query, params)
if all == True:
return cursor.fetchall()
else:
return cursor.fetchone()
finally:
# Close the cursor and release worker's lock
if cursor:
cursor.close()
worker.lock.release()
def fetchAll(self, query, params = ()):
"""
Fetch all values from db that matche given query.
Calls self.fetch with all = True.
query -- Query to execute. You can bind parameters with %s
params -- Parameters list. First element replaces first %s and so on. Optional.
"""
return self.fetch(query, params, True)

72
helpers/requestHelper.py Normal file
View File

@ -0,0 +1,72 @@
import tornado
import tornado.web
import tornado.gen
from tornado.ioloop import IOLoop
from objects import glob
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):
yield tornado.gen.Task(runBackground, (self.asyncGet, tuple(args), dict(kwargs)))
@tornado.web.asynchronous
@tornado.gen.engine
def post(self, *args, **kwargs):
yield tornado.gen.Task(runBackground, (self.asyncPost, tuple(args), dict(kwargs)))
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-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)))

View File

@ -1,47 +0,0 @@
import flask
import gzip
def generateResponse(token, data = None):
"""
Return a flask response with required headers for osu! client, token and gzip compressed data
token -- user token
data -- plain response body
return -- flask response
"""
resp = flask.Response(gzip.compress(data, 6))
resp.headers['cho-token'] = token
resp.headers['cho-protocol'] = '19'
resp.headers['Keep-Alive'] = 'timeout=5, max=100'
resp.headers['Connection'] = 'keep-alive'
resp.headers['Content-Type'] = 'text/html; charset=UTF-8'
resp.headers['Vary'] = 'Accept-Encoding'
resp.headers['Content-Encoding'] = 'gzip'
return resp
def HTMLResponse():
"""Return HTML bancho meme response"""
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%}</style></head><body><pre>"
html += " _ __<br>"
html += " (_) / /<br>"
html += " ______ __ ____ ____ / /____<br>"
html += " / ___/ / _ \\/ _ \\/ / _ \\<br>"
html += " / / / / /_) / /_) / / ____/<br>"
html += "/__/ /__/ .___/ .___/__/ \\_____/<br>"
html += " / / / /<br>"
html += " /__/ /__/<br>"
html += "<b>PYTHON > ALL VERSION</b><br><br>"
html += "<marquee style='white-space:pre;'><br>"
html += " .. o .<br>"
html += " o.o o . o<br>"
html += " oo...<br>"
html += " __[]__<br>"
html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
html += " \\ . .. .. . /<br>"
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.</pre></body></html>"
return html

View File

@ -13,7 +13,7 @@ def getID(username):
"""
# Get user ID from db
userID = glob.db.fetch("SELECT id FROM users WHERE username = ?", [username])
userID = glob.db.fetch("SELECT id FROM users WHERE username = %s", [username])
# Make sure the query returned something
if userID == None:
@ -34,7 +34,7 @@ def checkLogin(userID, password):
"""
# Get password data
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = ?", [userID])
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = %s", [userID])
# Make sure the query returned something
if passwordData == None:
@ -48,7 +48,7 @@ def checkLogin(userID, password):
ok = passwordHelper.checkOldPassword(password, passwordData["salt"], passwordData["password_md5"])
if not ok: return False
newpass = passwordHelper.genBcrypt(password)
glob.db.execute("UPDATE users SET password_md5=?, salt='', password_version='2' WHERE id = ?", [newpass, userID])
glob.db.execute("UPDATE users SET password_md5=%s, salt='', password_version='2' WHERE id = %s", [newpass, userID])
def exists(userID):
@ -59,7 +59,7 @@ def exists(userID):
return -- bool
"""
result = glob.db.fetch("SELECT id FROM users WHERE id = ?", [userID])
result = glob.db.fetch("SELECT id FROM users WHERE id = %s", [userID])
if result == None:
return False
else:
@ -74,7 +74,7 @@ def getAllowed(userID):
return -- allowed int
"""
return glob.db.fetch("SELECT allowed FROM users WHERE id = ?", [userID])["allowed"]
return glob.db.fetch("SELECT allowed FROM users WHERE id = %s", [userID])["allowed"]
def getRankPrivileges(userID):
@ -83,7 +83,7 @@ def getRankPrivileges(userID):
If you want to get that rank, user getUserGameRank instead
"""
return glob.db.fetch("SELECT rank FROM users WHERE id = ?", [userID])["rank"]
return glob.db.fetch("SELECT rank FROM users WHERE id = %s", [userID])["rank"]
def getSilenceEnd(userID):
@ -95,7 +95,7 @@ def getSilenceEnd(userID):
return -- UNIX time
"""
return glob.db.fetch("SELECT silence_end FROM users WHERE id = ?", [userID])["silence_end"]
return glob.db.fetch("SELECT silence_end FROM users WHERE id = %s", [userID])["silence_end"]
def silence(userID, silenceEndTime, silenceReason):
@ -108,7 +108,7 @@ def silence(userID, silenceEndTime, silenceReason):
silenceReason -- Silence reason shown on website
"""
glob.db.execute("UPDATE users SET silence_end = ?, silence_reason = ? WHERE id = ?", [silenceEndTime, silenceReason, userID])
glob.db.execute("UPDATE users SET silence_end = %s, silence_reason = %s WHERE id = %s", [silenceEndTime, silenceReason, userID])
def getRankedScore(userID, gameMode):
"""
@ -120,7 +120,7 @@ def getRankedScore(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["ranked_score_"+modeForDB]
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["ranked_score_"+modeForDB]
def getTotalScore(userID, gameMode):
@ -133,7 +133,7 @@ def getTotalScore(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["total_score_"+modeForDB]
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["total_score_"+modeForDB]
def getAccuracy(userID, gameMode):
@ -146,7 +146,7 @@ def getAccuracy(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["avg_accuracy_"+modeForDB]
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["avg_accuracy_"+modeForDB]
def getGameRank(userID, gameMode):
@ -159,7 +159,7 @@ def getGameRank(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = ?", [userID])
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = %s", [userID])
if result == None:
return 0
else:
@ -176,7 +176,7 @@ def getPlaycount(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["playcount_"+modeForDB]
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["playcount_"+modeForDB]
def getUsername(userID):
@ -187,7 +187,7 @@ def getUsername(userID):
return -- username
"""
return glob.db.fetch("SELECT username FROM users WHERE id = ?", [userID])["username"]
return glob.db.fetch("SELECT username FROM users WHERE id = %s", [userID])["username"]
def getFriendList(userID):
@ -199,7 +199,7 @@ def getFriendList(userID):
"""
# Get friends from db
friends = glob.db.fetchAll("SELECT user2 FROM users_relationships WHERE user1 = ?", [userID])
friends = glob.db.fetchAll("SELECT user2 FROM users_relationships WHERE user1 = %s", [userID])
if friends == None or len(friends) == 0:
# We have no friends, return 0 list
@ -225,11 +225,11 @@ def addFriend(userID, friendID):
return
# check user isn't already a friend of ours
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = ? AND user2 = ?", [userID, friendID]) != None:
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID]) != None:
return
# Set new value
glob.db.execute("INSERT INTO users_relationships (user1, user2) VALUES (?, ?)", [userID, friendID])
glob.db.execute("INSERT INTO users_relationships (user1, user2) VALUES (%s, %s)", [userID, friendID])
def removeFriend(userID, friendID):
@ -242,7 +242,7 @@ def removeFriend(userID, friendID):
# Delete user relationship. We don't need to check if the relationship was there, because who gives a shit,
# if they were not friends and they don't want to be anymore, be it. ¯\_(ツ)_/¯
glob.db.execute("DELETE FROM users_relationships WHERE user1 = ? AND user2 = ?", [userID, friendID])
glob.db.execute("DELETE FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID])
def getCountry(userID):
@ -255,7 +255,7 @@ def getCountry(userID):
return -- country code (two letters)
"""
return glob.db.fetch("SELECT country FROM users_stats WHERE id = ?", [userID])["country"]
return glob.db.fetch("SELECT country FROM users_stats WHERE id = %s", [userID])["country"]
def getPP(userID, gameMode):
"""
@ -266,7 +266,7 @@ def getPP(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = ?".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = %s".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
def setAllowed(userID, allowed):
"""
@ -275,7 +275,7 @@ def setAllowed(userID, allowed):
userID -- user
allowed -- allowed status. 1: normal, 0: banned
"""
glob.db.execute("UPDATE users SET allowed = ? WHERE id = ?", [allowed, userID])
glob.db.execute("UPDATE users SET allowed = %s WHERE id = %s", [allowed, userID])
def setCountry(userID, country):
"""
@ -284,7 +284,7 @@ def setCountry(userID, country):
userID -- userID
country -- country letters
"""
glob.db.execute("UPDATE users_stats SET country = ? WHERE id = ?", [country, userID])
glob.db.execute("UPDATE users_stats SET country = %s WHERE id = %s", [country, userID])
def getShowCountry(userID):
"""
@ -293,7 +293,7 @@ def getShowCountry(userID):
userID -- userID
return -- True if country is shown, False if it's hidden
"""
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = ?", [userID])
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = %s", [userID])
if country == None:
return False
return generalFunctions.stringToBool(country)
@ -303,5 +303,5 @@ def IPLog(userID, ip):
Botnet the user
(log his ip for multiaccount detection)
"""
glob.db.execute("""INSERT INTO ip_user (userid, ip, occurencies) VALUES (?, ?, '1')
glob.db.execute("""INSERT INTO ip_user (userid, ip, occurencies) VALUES (%s, %s, '1')
ON DUPLICATE KEY UPDATE occurencies = occurencies + 1""", [userID, ip])

View File

@ -39,4 +39,4 @@ class banchoConfig:
"""
self.config["banchoMaintenance"] = __maintenance
glob.db.execute("UPDATE bancho_settings SET value_int = ? WHERE name = 'bancho_maintenance'", [int(__maintenance)])
glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(__maintenance)])

View File

@ -14,3 +14,5 @@ channels = channelList.channelList()
matches = matchList.matchList()
memes = True
restarting = False
pool = None
requestTime = False

255
pep.py
View File

@ -1,81 +1,33 @@
"""Hello, pep.py here, ex-owner of ripple and prime minister of Ripwot."""
import logging
import sys
import flask
import datetime
import os
from multiprocessing.pool import ThreadPool
# Tornado server
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
# Tornado
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.gen
# pep.py files
from constants import bcolors
from constants import packetIDs
from constants import serverPackets
from helpers import configHelper
from helpers import discordBotHelper
from constants import exceptions
from objects import glob
from objects import fokabot
from objects import banchoConfig
from events import sendPublicMessageEvent
from events import sendPrivateMessageEvent
from events import channelJoinEvent
from events import channelPartEvent
from events import changeActionEvent
from events import cantSpectateEvent
from events import startSpectatingEvent
from events import stopSpectatingEvent
from events import spectateFramesEvent
from events import friendAddEvent
from events import friendRemoveEvent
from events import logoutEvent
from events import loginEvent
from events import setAwayMessageEvent
from events import joinLobbyEvent
from events import createMatchEvent
from events import partLobbyEvent
from events import changeSlotEvent
from events import joinMatchEvent
from events import partMatchEvent
from events import changeMatchSettingsEvent
from events import changeMatchPasswordEvent
from events import changeMatchModsEvent
from events import matchReadyEvent
from events import matchLockEvent
from events import matchStartEvent
from events import matchPlayerLoadEvent
from events import matchSkipEvent
from events import matchFramesEvent
from events import matchCompleteEvent
from events import matchNoBeatmapEvent
from events import matchHasBeatmapEvent
from events import matchTransferHostEvent
from events import matchFailedEvent
from events import matchInviteEvent
from events import matchChangeTeamEvent
# pep.py helpers
from helpers import packetHelper
from handlers import mainHandler
from helpers import consoleHelper
from helpers import databaseHelper
from helpers import responseHelper
from helpers import databaseHelperNew
from helpers import generalFunctions
from helpers import systemHelper
from flask import request
# Create flask instance
app = flask.Flask(__name__)
# Get flask logger
flaskLogger = logging.getLogger("werkzeug")
def make_app():
return tornado.web.Application([
(r"/", mainHandler.handler)
])
# Ci trigger
@app.route("/ci-trigger")
'''@app.route("/ci-trigger")
@app.route("/api/ci-trigger")
def ciTrigger():
# Ci restart trigger
@ -133,135 +85,10 @@ def apiOnlineUsers():
def banchoServer():
if flask.request.method == 'POST':
# Track time if needed
if serverOutputRequestTime == True:
# Start time
st = datetime.datetime.now()
# Client's token string and request data
requestTokenString = flask.request.headers.get('osu-token')
requestData = flask.request.data
# Server's token string and request data
responseTokenString = "ayy"
responseData = bytes()
if requestTokenString == None:
# No token, first request. Handle login.
responseTokenString, responseData = loginEvent.handle(flask.request)
else:
try:
# This is not the first packet, send response based on client's request
# Packet start position, used to read stacked packets
pos = 0
# Make sure the token exists
if requestTokenString not in glob.tokens.tokens:
raise exceptions.tokenNotFoundException()
# Token exists, get its object
userToken = glob.tokens.tokens[requestTokenString]
# Keep reading packets until everything has been read
while pos < len(requestData):
# Get packet from stack starting from new packet
leftData = requestData[pos:]
# Get packet ID, data length and data
packetID = packetHelper.readPacketID(leftData)
dataLength = packetHelper.readPacketLength(leftData)
packetData = requestData[pos:(pos+dataLength+7)]
# Console output if needed
if serverOutputPackets == True and packetID != 4:
consoleHelper.printColored("Incoming packet ({})({}):".format(requestTokenString, userToken.username), bcolors.GREEN)
consoleHelper.printColored("Packet code: {}\nPacket length: {}\nSingle packet data: {}\n".format(str(packetID), str(dataLength), str(packetData)), bcolors.YELLOW)
# Event handler
def handleEvent(ev):
def wrapper():
ev.handle(userToken, packetData)
return wrapper
eventHandler = {
# TODO: Rename packets and events
# TODO: Host check for multi
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent),
packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent),
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
packetIDs.client_channelPart: handleEvent(channelPartEvent),
packetIDs.client_changeAction: handleEvent(changeActionEvent),
packetIDs.client_startSpectating: handleEvent(startSpectatingEvent),
packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent),
packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent),
packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent),
packetIDs.client_friendAdd: handleEvent(friendAddEvent),
packetIDs.client_friendRemove: handleEvent(friendRemoveEvent),
packetIDs.client_logout: handleEvent(logoutEvent),
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_invite: handleEvent(matchInviteEvent),
packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent)
}
if packetID != 4:
if packetID in eventHandler:
eventHandler[packetID]()
else:
consoleHelper.printColored("[!] Unknown packet id from {} ({})".format(requestTokenString, packetID), bcolors.RED)
# 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
# Token queue built, send it
responseTokenString = userToken.token
responseData = userToken.queue
userToken.resetQueue()
# 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.")
consoleHelper.printColored("[!] Received packet from unknown token ({}).".format(requestTokenString), bcolors.RED)
consoleHelper.printColored("> {} have been disconnected (invalid token)".format(requestTokenString), bcolors.YELLOW)
if serverOutputRequestTime == True:
# End time
et = datetime.datetime.now()
# Total time:
tt = float((et.microsecond-st.microsecond)/1000)
consoleHelper.printColored("Request time: {}ms".format(tt), bcolors.PINK)
# Send server's response to client
# We don't use token object because we might not have a token (failed login)
return responseHelper.generateResponse(responseTokenString, responseData)
else:
# Not a POST request, send html page
return responseHelper.HTMLResponse()
return responseHelper.HTMLResponse()'''
if __name__ == "__main__":
@ -291,8 +118,8 @@ if __name__ == "__main__":
# Connect to db
try:
consoleHelper.printNoNl("> Connecting to MySQL db... ")
glob.db = databaseHelper.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["pingtime"]))
print("> Connecting to MySQL db... ")
glob.db = databaseHelperNew.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"]))
consoleHelper.printDone()
except:
# Exception while connecting to db
@ -300,6 +127,15 @@ if __name__ == "__main__":
consoleHelper.printColored("[!] Error while connection to database. Please check your config.ini and run the server again", bcolors.RED)
raise
# Create threads pool
try:
consoleHelper.printNoNl("> Creating threads pool... ")
glob.pool = ThreadPool(int(glob.conf.config["server"]["threads"]))
consoleHelper.printDone()
except:
consoleHelper.printError()
consoleHelper.printColored("[!] Error while creating threads pool. Please check your config.ini and run the server again", bcolors.RED)
# Load bancho_settings
try:
consoleHelper.printNoNl("> Loading bancho settings from DB... ")
@ -344,41 +180,14 @@ if __name__ == "__main__":
consoleHelper.printColored("[!] Warning! users localization is disabled!", bcolors.YELLOW)
# Get server parameters from config.ini
serverName = glob.conf.config["server"]["server"]
serverHost = glob.conf.config["server"]["host"]
serverPort = int(glob.conf.config["server"]["port"])
serverOutputPackets = generalFunctions.stringToBool(glob.conf.config["server"]["outputpackets"])
serverOutputRequestTime = generalFunctions.stringToBool(glob.conf.config["server"]["outputrequesttime"])
glob.requestTime = generalFunctions.stringToBool(glob.conf.config["server"]["outputrequesttime"])
# Send server start message
# Server start message and console output
discordBotHelper.sendConfidential("w00t p00t! (pep.py started)")
consoleHelper.printColored("> Tornado listening for clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
# Run server sanic way
if serverName == "tornado":
# Tornado server
consoleHelper.printColored("> Tornado listening for clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
webServer = HTTPServer(WSGIContainer(app))
webServer.listen(serverPort)
IOLoop.instance().start()
elif serverName == "flask":
# Flask server
# Get flask settings
flaskThreaded = generalFunctions.stringToBool(glob.conf.config["flask"]["threaded"])
flaskDebug = generalFunctions.stringToBool(glob.conf.config["flask"]["debug"])
flaskLoggerStatus = not generalFunctions.stringToBool(glob.conf.config["flask"]["logger"])
# Set flask debug mode and logger
app.debug = flaskDebug
flaskLogger.disabled = flaskLoggerStatus
# Console output
if flaskDebug == False:
consoleHelper.printColored("> Flask listening for clients on {}.{}...".format(serverHost, serverPort), bcolors.GREEN)
else:
consoleHelper.printColored("> Flask "+bcolors.YELLOW+"(debug mode)"+bcolors.ENDC+" listening for clients on {}:{}...".format(serverHost, serverPort), bcolors.GREEN)
# Run flask server
app.run(host=serverHost, port=serverPort, threaded=flaskThreaded)
else:
print(bcolors.RED+"[!] Unknown server. Please set the server key in config.ini to "+bcolors.ENDC+bcolors.YELLOW+"tornado"+bcolors.ENDC+bcolors.RED+" or "+bcolors.ENDC+bcolors.YELLOW+"flask"+bcolors.ENDC)
sys.exit()
# Start tornado
app = tornado.httpserver.HTTPServer(make_app())
app.listen(serverPort)
tornado.ioloop.IOLoop.instance().start()