21 Commits

Author SHA1 Message Date
Nyo
e92cbe47bd ⬆️ 1.11.0 ⬆️ 2016-12-09 13:19:04 +01:00
Nyo
6ca2016f7b .BANCHO. Disabled datadog ram usage tracking 2016-12-09 11:47:00 +01:00
Nyo
2f54a56b7a Add full build script 2016-12-08 15:46:21 +01:00
Nyo
cf9e506875 .HIDE. Update submodules 2016-12-08 12:04:59 +01:00
Nyo
5c93d692ea .BANCHO. Cythonized mainHandler 2016-12-08 11:44:27 +01:00
Nyo
a8a1dfb1bc .HIDE. Update .gitignore 2016-12-08 11:44:12 +01:00
Nyo
9d562e7acd .BANCHO. Dynamic setup.py file 2016-12-08 11:43:23 +01:00
Nyo
4f4253afce .HIDE. Update README.txt 2016-12-07 22:31:28 +01:00
Nyo
04898c24ae .BANCHO. Ported packet encoder/decoder to Cython, add distutils setup file, update .gitignore, README and requirements.txt 2016-12-07 22:25:16 +01:00
Nyo
1b94936092 .BANCHO. .FIX. Fix wrong default configuration file 2016-12-07 21:15:55 +01:00
Nyo
d4591b42a3 .BANCHO. !kickall command now requires 'manage server' privilege 2016-12-07 21:15:23 +01:00
Nyo
69508f9a0e Add Google auth 2fa check at login 2016-11-30 23:33:56 +01:00
Nyo
5cf8c1bde8 Merge branch 'master' of git.zxq.co:ripple/pep.py 2016-11-30 20:08:54 +01:00
Nyo
20be60d9db Update submodules 2016-11-30 20:08:43 +01:00
Howl
61935f323c add link to github mirror 2016-11-29 17:21:24 +01:00
Nyo
cecef18d13 .HIDE. Update submodules 2016-11-20 14:17:35 +01:00
Nyo
5723c0e68f .BANCHO. Move online users count to redis 2016-11-20 14:17:05 +01:00
Nyo
525235a27e .BANCHO. Move bancho sessions to redis 2016-11-20 13:03:07 +01:00
Nyo
3bc390e3e6 .HIDE. Update submodules 2016-11-20 12:32:45 +01:00
Nyo
f6ae673401 .HIDE. Update submodules 2016-11-20 11:32:21 +01:00
Nyo
aa32e8bea6 .BANCHO. Add pubsub handlers for username changes, bans, restrictions, silences, stats update, kicks and bancho settings reload. 2016-11-20 11:31:51 +01:00
24 changed files with 319 additions and 95 deletions

6
.gitignore vendored
View File

@@ -1,9 +1,9 @@
**/__pycache__
**/build
config.ini
filters.txt
.data
.idea
common_funzia
common_refractor
common_memato
redistest.py
*.c
*.so

View File

@@ -1,4 +1,8 @@
## pep.py
- Origin: https://git.zxq.co/ripple/pep.py
- Mirror: https://github.com/osuripple/pep.py
This is Ripple's bancho server. It handles:
- Client login
- Online users listing and statuses
@@ -9,6 +13,8 @@ This is Ripple's bancho server. It handles:
## Requirements
- Python 3.5
- Cython
- C compiler
- MySQLdb (`mysqlclient`)
- Tornado
- Bcrypt
@@ -23,9 +29,14 @@ afterwards, install the required dependencies with pip
```
$ pip install -r requirements.txt
```
then, run pep.py once to create the default config file and edit it
then, compile all `*.pyx` files to `*.so` or `*.dll` files using `setup.py` (distutils file)
```
$ python3 setup.py build_ext --inplace
```
finally, run pep.py once to create the default config file and edit it
```
$ python3 pep.py
...
$ nano config.ini
```
you can run pep.py by typing

2
common

Submodule common updated: db36e8d589...3288420cd8

View File

@@ -304,22 +304,7 @@ def systemShutdown(fro, chan, message):
return restartShutdown(False)
def systemReload(fro, chan, message):
# Reload settings from bancho_settings
glob.banchoConf.loadSettings()
# Reload channels too
glob.channels.loadChannels()
# And chat filters
glob.chatFilters.loadFilters()
# Send new channels and new bottom icon to everyone
glob.streams.broadcast("main", serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
glob.streams.broadcast("main", serverPackets.channelInfoEnd())
for key, value in glob.channels.channels.items():
if value.publicRead == True and value.hidden == False:
glob.streams.broadcast("main", serverPackets.channelInfo(key))
glob.banchoConf.reload()
return "Bancho settings reloaded!"
def systemMaintenance(fro, chan, message):
@@ -753,7 +738,7 @@ commands = [
"callback": moderated
}, {
"trigger": "!kickall",
"privileges": privileges.ADMIN_KICK_USERS,
"privileges": privileges.ADMIN_MANAGE_SERVERS,
"callback": kickAll
}, {
"trigger": "!kick",

View File

@@ -5,20 +5,19 @@ from constants import clientPackets
from constants import serverPackets
from objects import glob
def handle(userToken, packetData):
# Get usertoken data
userID = userToken.userID
username = userToken.username
# Make sure we are not banned
if userUtils.isBanned(userID):
userToken.enqueue(serverPackets.loginBanned())
return
#if userUtils.isBanned(userID):
# userToken.enqueue(serverPackets.loginBanned())
# return
# Send restricted message if needed
if userToken.restricted:
userToken.checkRestricted(True)
#if userToken.restricted:
# userToken.checkRestricted(True)
# Change action packet
packetData = clientPackets.userActionChange(packetData)
@@ -34,8 +33,10 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us
'''
# Update cached stats if our pp changed if we've just submitted a score or we've changed gameMode
if (userToken.actionID == actions.PLAYING or userToken.actionID == actions.MULTIPLAYING) or (userToken.pp != userUtils.getPP(userID, userToken.gameMode)) or (userToken.gameMode != packetData["gameMode"]):
# Always update game mode, or we'll cache stats from the wrong game mode if we've changed it
#if (userToken.actionID == actions.PLAYING or userToken.actionID == actions.MULTIPLAYING) or (userToken.pp != userUtils.getPP(userID, userToken.gameMode)) or (userToken.gameMode != packetData["gameMode"]):
# Update cached stats if we've changed gamemode
if userToken.gameMode != packetData["gameMode"]:
userToken.gameMode = packetData["gameMode"]
userToken.updateCachedStats()

View File

@@ -1,4 +1,5 @@
import time
import json
from common.log import logUtils as log
from constants import serverPackets
@@ -6,7 +7,7 @@ from helpers import chatHelper as chat
from objects import glob
def handle(userToken, _=None):
def handle(userToken, _=None, deleteToken=True):
# get usertoken data
userID = userToken.userID
username = userToken.username
@@ -38,7 +39,19 @@ def handle(userToken, _=None):
glob.ircServer.forceDisconnection(userToken.username)
# Delete token
glob.tokens.deleteToken(requestToken)
if deleteToken:
glob.tokens.deleteToken(requestToken)
else:
userToken.kicked = True
# Change username if needed
newUsername = glob.redis.get("ripple:change_username_pending:{}".format(userID))
if newUsername is not None:
log.debug("Sending username change request for user {}".format(userID))
glob.redis.publish("peppy:change_username", json.dumps({
"userID": userID,
"newUsername": newUsername.decode("utf-8")
}))
# Console output
log.info("{} has been disconnected. (logout)".format(username))

4
full_build.sh Normal file
View File

@@ -0,0 +1,4 @@
find . -name "*.c" -type f -delete
find . -name "*.o" -type f -delete
find . -name "*.so" -type f -delete
python3 setup.py build_ext --inplace

View File

@@ -10,7 +10,7 @@ class handler(requestsManager.asyncRequestHandler):
data = {"message": "unknown error"}
try:
# Get online users count
data["result"] = len(glob.tokens.tokens)
data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8"))
# Status code and message
statusCode = 200

View File

@@ -122,7 +122,7 @@ class handler(SentryMixin, requestsManager.asyncRequestHandler):
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),
@@ -206,6 +206,9 @@ class handler(SentryMixin, requestsManager.asyncRequestHandler):
userToken.updatePingTime()
# Release token lock
userToken.lock.release()
# Delete token if kicked
if userToken.kicked:
glob.tokens.deleteToken(userToken)
if glob.outputRequestTime:
# End time

View File

@@ -127,9 +127,9 @@ class config:
self.config.set("discord", "devgroup", "")
self.config.add_section("datadog")
self.config.set("datadog", "enable")
self.config.set("datadog", "apikey")
self.config.set("datadog", "appkey")
self.config.set("datadog", "enable", "0")
self.config.set("datadog", "apikey", "")
self.config.set("datadog", "appkey", "")
self.config.add_section("irc")
self.config.set("irc", "enable", "1")

View File

@@ -1,15 +1,15 @@
import struct
from constants import dataTypes
def uleb128Encode(num):
cpdef bytearray uleb128Encode(int num):
"""
Encode an int to uleb128
:param num: int to encode
:return: bytearray with encoded number
"""
arr = bytearray()
length = 0
cdef bytearray arr = bytearray()
cdef int length = 0
if num == 0:
return bytearray(b"\x00")
@@ -23,15 +23,16 @@ def uleb128Encode(num):
return arr
def uleb128Decode(num):
cpdef list uleb128Decode(bytes num):
"""
Decode a uleb128 to int
:param num: encoded uleb128 int
:return: (total, length)
"""
shift = 0
arr = [0,0] #total, length
cdef int shift = 0
cdef list arr = [0,0] #total, length
cdef int b
while True:
b = num[arr[1]]
@@ -43,7 +44,7 @@ def uleb128Decode(num):
return arr
def unpackData(data, dataType):
cpdef unpackData(bytes data, int dataType):
"""
Unpacks a single section of a packet.
@@ -74,7 +75,7 @@ def unpackData(data, dataType):
# Unpack
return struct.unpack(unpackType, bytes(data))[0]
def packData(__data, dataType):
cpdef bytes packData(__data, int dataType):
"""
Packs a single section of a packet.
@@ -82,8 +83,9 @@ def packData(__data, dataType):
:param dataType: data type
:return: packed bytes
"""
data = bytes() # data to return
pack = True # if True, use pack. False only with strings
cdef bytes data = bytes() # data to return
cdef bint pack = True # if True, use pack. False only with strings
cdef str packType
# Get right pack Type
if dataType == dataTypes.BBYTES:
@@ -134,7 +136,7 @@ def packData(__data, dataType):
return data
def buildPacket(__packet, __packetData=None):
cpdef bytes buildPacket(int __packet, list __packetData = []):
"""
Builds a packet
@@ -143,13 +145,12 @@ def buildPacket(__packet, __packetData=None):
:return: packet bytes
"""
# Set some variables
if __packetData is None:
__packetData = []
packetData = bytes()
packetLength = 0
packetBytes = bytes()
cdef bytes packetData = bytes()
cdef int packetLength = 0
cdef bytes packetBytes = bytes()
# Pack packet data
cdef list i
for i in __packetData:
packetData += packData(i[0], i[1])
@@ -163,7 +164,7 @@ def buildPacket(__packet, __packetData=None):
packetBytes += packetData # packet data
return packetBytes
def readPacketID(stream):
cpdef int readPacketID(bytes stream):
"""
Read packetID (first two bytes) from a packet
@@ -172,7 +173,7 @@ def readPacketID(stream):
"""
return unpackData(stream[0:2], dataTypes.UINT16)
def readPacketLength(stream):
cpdef int readPacketLength(bytes stream):
"""
Read packet data length (3:7 bytes) from a packet
@@ -182,7 +183,7 @@ def readPacketLength(stream):
return unpackData(stream[3:7], dataTypes.UINT32)
def readPacketData(stream, structure=None, hasFirstBytes = True):
cpdef readPacketData(bytes stream, list structure=[], bint hasFirstBytes = True):
"""
Read packet data from `stream` according to `structure`
:param stream: packet bytes
@@ -192,11 +193,10 @@ def readPacketData(stream, structure=None, hasFirstBytes = True):
:return: {name: unpackedValue, ...}
"""
# Read packet ID (first 2 bytes)
if structure is None:
structure = []
data = {}
cdef dict data = {}
# Skip packet ID and packet length if needed
cdef start, end
if hasFirstBytes:
end = 7
start = 7
@@ -205,6 +205,8 @@ def readPacketData(stream, structure=None, hasFirstBytes = True):
start = 0
# Read packet
cdef list i
cdef bint unpack
for i in structure:
start = end
unpack = True
@@ -239,7 +241,10 @@ def readPacketData(stream, structure=None, hasFirstBytes = True):
end = start+length[0]+length[1]+1
# Read bytes
data[i[0]] = ''.join(chr(j) for j in stream[start+1+length[1]:end])
#data[i[0]] = ''.join(chr(j) for j in stream[start+1+length[1]:end])
data[i[0]] = ""
for j in stream[start+1+length[1]:end]:
data[i[0]] += chr(j)
elif i[1] == dataTypes.BYTE:
end = start+1
elif i[1] == dataTypes.UINT16 or i[1] == dataTypes.SINT16:

View File

@@ -1,5 +1,6 @@
# TODO: Rewrite this shit
from common import generalUtils
from constants import serverPackets
from objects import glob
@@ -41,3 +42,20 @@ class banchoConfig:
"""
self.config["banchoMaintenance"] = maintenance
glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(maintenance)])
def reload(self):
# Reload settings from bancho_settings
glob.banchoConf.loadSettings()
# Reload channels too
glob.channels.loadChannels()
# And chat filters
glob.chatFilters.loadFilters()
# Send new channels and new bottom icon to everyone
glob.streams.broadcast("main", serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
glob.streams.broadcast("main", serverPackets.channelInfoEnd())
for key, value in glob.channels.channels.items():
if value.publicRead == True and value.hidden == False:
glob.streams.broadcast("main", serverPackets.channelInfo(key))

View File

@@ -31,6 +31,7 @@ class token:
self.privileges = userUtils.getPrivileges(self.userID)
self.admin = userUtils.isInPrivilegeGroup(self.userID, "developer") or userUtils.isInPrivilegeGroup(self.userID, "community manager")
self.irc = irc
self.kicked = False
self.restricted = userUtils.isRestricted(self.userID)
self.loginTime = int(time.time())
self.pingTime = self.loginTime
@@ -323,22 +324,28 @@ class token:
self.enqueue(serverPackets.loginFailed())
# Logout event
logoutEvent.handle(self, None)
logoutEvent.handle(self, deleteToken=False)
def silence(self, seconds, reason, author = 999):
def silence(self, seconds = None, reason = "", author = 999):
"""
Silences this user (db, packet and token)
:param seconds: silence length in seconds
:param reason: silence reason
:param seconds: silence length in seconds. If None, get it from db. Default: None
:param reason: silence reason. Default: empty string
:param author: userID of who has silenced the user. Default: 999 (FokaBot)
:return:
"""
# Silence in db and token
self.silenceEndTime = int(time.time())+seconds
userUtils.silence(self.userID, seconds, reason, author)
if seconds is None:
# Get silence expire from db if needed
seconds = max(0, userUtils.getSilenceEnd(self.userID) - int(time.time()))
else:
# Silence in db and token
userUtils.silence(self.userID, seconds, reason, author)
# Send silence packet to target
# Silence token
self.silenceEndTime = int(time.time()) + seconds
# Send silence packet to user
self.enqueue(serverPackets.silenceEndTime(seconds))
# Send silenced packet to everyone else
@@ -394,18 +401,29 @@ class token:
self.gameRank = stats["gameRank"]
self.pp = stats["pp"]
def checkRestricted(self, force=False):
def checkRestricted(self):
"""
Check if this token is restricted. If so, send fokabot message
:param force: If True, get restricted value from db.
If False, get the cached one. Default: False
:return:
"""
if force:
self.restricted = userUtils.isRestricted(self.userID)
oldRestricted = self.restricted
self.restricted = userUtils.isRestricted(self.userID)
if self.restricted:
self.setRestricted()
elif not self.restricted and oldRestricted != self.restricted:
self.resetRestricted()
def checkBanned(self):
"""
Check if this user is banned. If so, disconnect it.
:return:
"""
if userUtils.isBanned(self.userID):
self.enqueue(serverPackets.loginBanned())
logoutEvent.handle(self, deleteToken=False)
def setRestricted(self):
"""
@@ -415,7 +433,16 @@ class token:
:return:
"""
self.restricted = True
chat.sendMessage("FokaBot",self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
chat.sendMessage("FokaBot", self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
def resetRestricted(self):
"""
Send FokaBot message to alert the user that he has been unrestricted
and he has to log in again.
:return:
"""
chat.sendMessage("FokaBot", self.username, "Your account has been unrestricted! Please log in again.")
def joinStream(self, name):
"""

View File

@@ -24,6 +24,7 @@ class tokenList:
"""
newToken = osuToken.token(userID, ip=ip, irc=irc, timeOffset=timeOffset, tournament=tournament)
self.tokens[newToken.token] = newToken
glob.redis.incr("ripple:online_users")
return newToken
def deleteToken(self, token):
@@ -34,12 +35,10 @@ class tokenList:
:return:
"""
if token in self.tokens:
# Delete session from DB
if self.tokens[token].ip != "":
userUtils.deleteBanchoSessions(self.tokens[token].userID, self.tokens[token].ip)
# Pop token from list
self.tokens.pop(token)
glob.redis.decr("ripple:online_users")
def getUserIDFromToken(self, token):
"""
@@ -104,10 +103,15 @@ class tokenList:
:return:
"""
# Delete older tokens
delete = []
for key, value in list(self.tokens.items()):
if value.userID == userID:
# Delete this token from the dictionary
self.tokens[key].kick("You have logged in from somewhere else. You can't connect to Bancho/IRC from more than one device at the same time.", "kicked, multiple clients")
#self.tokens[key].kick("You have logged in from somewhere else. You can't connect to Bancho/IRC from more than one device at the same time.", "kicked, multiple clients")
delete.append(self.tokens[key])
for i in delete:
logoutEvent.handle(i)
def multipleEnqueue(self, packet, who, but = False):
"""
@@ -185,12 +189,16 @@ class tokenList:
def deleteBanchoSessions(self):
"""
Truncate bancho_sessions table.
Remove all `peppy:sessions:*` redis keys.
Call at bancho startup to delete old cached sessions
:return:
"""
glob.db.execute("TRUNCATE TABLE bancho_sessions")
try:
# TODO: Make function or some redis meme
glob.redis.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "peppy:sessions:*")
except:
pass
def tokenExists(self, username = "", userID = -1):

42
pep.py
View File

@@ -16,7 +16,7 @@ from common.constants import bcolors
from common.db import dbConnector
from common.ddog import datadogClient
from common.log import logUtils as log
from common.ripple import userUtils
from common.redis import pubSub
from common.web import schiavo
from handlers import apiFokabotMessageHandler
from handlers import apiIsOnlineHandler
@@ -34,6 +34,12 @@ from objects import banchoConfig
from objects import chatFilters
from objects import fokabot
from objects import glob
from pubSubHandlers import changeUsernameHandler
from pubSubHandlers import disconnectHandler
from pubSubHandlers import banHandler
from pubSubHandlers import updateSilenceHandler
from pubSubHandlers import updateStatsHandler
def make_app():
@@ -108,6 +114,8 @@ if __name__ == "__main__":
# Empty redis cache
try:
# TODO: Make function or some redis meme
glob.redis.set("ripple:online_users", 0)
glob.redis.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "peppy:*")
except redis.exceptions.ResponseError:
# Script returns error if there are no keys starting with peppy:*
@@ -222,18 +230,16 @@ if __name__ == "__main__":
datadogClient.periodicCheck("online_users", lambda: len(glob.tokens.tokens)),
datadogClient.periodicCheck("multiplayer_matches", lambda: len(glob.matches.matches)),
datadogClient.periodicCheck("ram_clients", lambda: generalUtils.getTotalSize(glob.tokens)),
datadogClient.periodicCheck("ram_matches", lambda: generalUtils.getTotalSize(glob.matches)),
datadogClient.periodicCheck("ram_channels", lambda: generalUtils.getTotalSize(glob.channels)),
datadogClient.periodicCheck("ram_file_buffers", lambda: generalUtils.getTotalSize(glob.fileBuffers)),
datadogClient.periodicCheck("ram_file_locks", lambda: generalUtils.getTotalSize(glob.fLocks)),
datadogClient.periodicCheck("ram_datadog", lambda: generalUtils.getTotalSize(glob.datadogClient)),
datadogClient.periodicCheck("ram_verified_cache", lambda: generalUtils.getTotalSize(glob.verifiedCache)),
#datadogClient.periodicCheck("ram_userid_cache", lambda: generalUtils.getTotalSize(glob.userIDCache)),
#datadogClient.periodicCheck("ram_pool", lambda: generalUtils.getTotalSize(glob.pool)),
datadogClient.periodicCheck("ram_irc", lambda: generalUtils.getTotalSize(glob.ircServer)),
datadogClient.periodicCheck("ram_tornado", lambda: generalUtils.getTotalSize(glob.application)),
datadogClient.periodicCheck("ram_db", lambda: generalUtils.getTotalSize(glob.db)),
#datadogClient.periodicCheck("ram_clients", lambda: generalUtils.getTotalSize(glob.tokens)),
#datadogClient.periodicCheck("ram_matches", lambda: generalUtils.getTotalSize(glob.matches)),
#datadogClient.periodicCheck("ram_channels", lambda: generalUtils.getTotalSize(glob.channels)),
#datadogClient.periodicCheck("ram_file_buffers", lambda: generalUtils.getTotalSize(glob.fileBuffers)),
#datadogClient.periodicCheck("ram_file_locks", lambda: generalUtils.getTotalSize(glob.fLocks)),
#datadogClient.periodicCheck("ram_datadog", lambda: generalUtils.getTotalSize(glob.datadogClient)),
#datadogClient.periodicCheck("ram_verified_cache", lambda: generalUtils.getTotalSize(glob.verifiedCache)),
#datadogClient.periodicCheck("ram_irc", lambda: generalUtils.getTotalSize(glob.ircServer)),
#datadogClient.periodicCheck("ram_tornado", lambda: generalUtils.getTotalSize(glob.application)),
#datadogClient.periodicCheck("ram_db", lambda: generalUtils.getTotalSize(glob.db)),
])
else:
consoleHelper.printColored("[!] Warning! Datadog stats tracking is disabled!", bcolors.YELLOW)
@@ -264,6 +270,16 @@ if __name__ == "__main__":
log.logMessage("**pep.py** Server started!", discord="bunker", of="info.txt", stdout=False)
consoleHelper.printColored("> Tornado listening for HTTP(s) clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
# Connect to pubsub channels
pubSub.listener(glob.redis, {
"peppy:disconnect": disconnectHandler.handler(),
"peppy:change_username": changeUsernameHandler.handler(),
"peppy:reload_settings": lambda x: x == b"reload" and glob.banchoConf.reload(),
"peppy:update_cached_stats": updateStatsHandler.handler(),
"peppy:silence": updateSilenceHandler.handler(),
"peppy:ban": banHandler.handler(),
}).start()
# Start tornado
glob.application.listen(serverPort)
tornado.ioloop.IOLoop.instance().start()

View File

View File

@@ -0,0 +1,18 @@
from common.redis import generalPubSubHandler
from common.ripple import userUtils
from objects import glob
class handler(generalPubSubHandler.generalPubSubHandler):
def __init__(self):
super().__init__()
self.type = "int"
def handle(self, userID):
userID = super().parseData(userID)
if userID is None:
return
targetToken = glob.tokens.getTokenFromUserID(userID)
if targetToken is not None:
targetToken.privileges = userUtils.getPrivileges(userID)
targetToken.checkBanned()
targetToken.checkRestricted()

View File

@@ -0,0 +1,49 @@
from common.redis import generalPubSubHandler
from common.ripple import userUtils
from common.log import logUtils as log
from common.constants import actions
from objects import glob
def handleUsernameChange(userID, newUsername, targetToken=None):
try:
userUtils.changeUsername(userID, newUsername=newUsername)
if targetToken is not None:
targetToken.kick("Your username has been changed to {}. Please log in again.".format(newUsername), "username_change")
except userUtils.usernameAlreadyInUseError:
log.rap(999, "Username change: {} is already in use!", through="Bancho")
if targetToken is not None:
targetToken.kick("There was a critical error while trying to change your username. Please contact a developer.", "username_change_fail")
except userUtils.invalidUsernameError:
log.rap(999, "Username change: {} is not a valid username!", through="Bancho")
if targetToken is not None:
targetToken.kick("There was a critical error while trying to change your username. Please contact a developer.", "username_change_fail")
class handler(generalPubSubHandler.generalPubSubHandler):
def __init__(self):
super().__init__()
self.structure = {
"userID": 0,
"newUsername": ""
}
def handle(self, data):
data = super().parseData(data)
if data is None:
return
# Get the user's token
targetToken = glob.tokens.getTokenFromUserID(data["userID"])
if targetToken is None:
# If the user is offline change username immediately
handleUsernameChange(data["userID"], data["newUsername"])
else:
if targetToken.irc or (targetToken.actionID != actions.PLAYING and targetToken.actionID != actions.MULTIPLAYING):
# If the user is online and he's connected through IRC or he's not playing,
# change username and kick the user immediately
handleUsernameChange(data["userID"], data["newUsername"], targetToken)
else:
# If the user is playing, delay the username change until he submits the score
# On submit modular, lets will send the username change request again
# through redis once the score has been submitted
# The check is performed on bancho logout too, so if the user disconnects
# without submitting a score, the username gets changed on bancho logout
glob.redis.set("ripple:change_username_pending:{}".format(data["userID"]), data["newUsername"])

View File

@@ -0,0 +1,18 @@
from common.redis import generalPubSubHandler
from objects import glob
class handler(generalPubSubHandler.generalPubSubHandler):
def __init__(self):
super().__init__()
self.structure = {
"userID": 0,
"reason": ""
}
def handle(self, data):
data = super().parseData(data)
if data is None:
return
targetToken = glob.tokens.getTokenFromUserID(data["userID"])
if targetToken is not None:
targetToken.kick(data["reason"], "pubsub_kick")

View File

@@ -0,0 +1,15 @@
from common.redis import generalPubSubHandler
from objects import glob
class handler(generalPubSubHandler.generalPubSubHandler):
def __init__(self):
super().__init__()
self.type = "int"
def handle(self, userID):
userID = super().parseData(userID)
if userID is None:
return
targetToken = glob.tokens.getTokenFromUserID(userID)
if targetToken is not None:
targetToken.silence()

View File

@@ -0,0 +1,15 @@
from common.redis import generalPubSubHandler
from objects import glob
class handler(generalPubSubHandler.generalPubSubHandler):
def __init__(self):
super().__init__()
self.type = "int"
def handle(self, userID):
userID = super().parseData(userID)
if userID is None:
return
targetToken = glob.tokens.getTokenFromUserID(userID)
if targetToken is not None:
targetToken.updateCachedStats()

View File

@@ -5,4 +5,5 @@ psutil
raven
bcrypt>=3.1.1
dill
redis
redis
cython

17
setup.py Normal file
View File

@@ -0,0 +1,17 @@
"""Cython build file"""
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
import os
cythonExt = []
for root, dirs, files in os.walk(os.getcwd()):
for file in files:
if file.endswith(".pyx"):
filePath = os.path.relpath(os.path.join(root, file))
cythonExt.append(Extension(filePath.replace("/", ".")[:-4], [filePath]))
setup(
name = "pep.pyx modules",
ext_modules = cythonize(cythonExt, nthreads = 4),
)

View File

@@ -1 +1 @@
1.10.0
1.11.0