Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e92cbe47bd | ||
|
6ca2016f7b | ||
|
2f54a56b7a | ||
|
cf9e506875 | ||
|
5c93d692ea | ||
|
a8a1dfb1bc | ||
|
9d562e7acd | ||
|
4f4253afce | ||
|
04898c24ae | ||
|
1b94936092 | ||
|
d4591b42a3 | ||
|
69508f9a0e | ||
|
5cf8c1bde8 | ||
|
20be60d9db | ||
|
61935f323c | ||
|
cecef18d13 | ||
|
5723c0e68f | ||
|
525235a27e | ||
|
3bc390e3e6 | ||
|
f6ae673401 | ||
|
aa32e8bea6 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,9 +1,9 @@
|
|||||||
**/__pycache__
|
**/__pycache__
|
||||||
|
**/build
|
||||||
config.ini
|
config.ini
|
||||||
filters.txt
|
filters.txt
|
||||||
.data
|
.data
|
||||||
.idea
|
.idea
|
||||||
common_funzia
|
|
||||||
common_refractor
|
|
||||||
common_memato
|
|
||||||
redistest.py
|
redistest.py
|
||||||
|
*.c
|
||||||
|
*.so
|
13
README.md
13
README.md
@@ -1,4 +1,8 @@
|
|||||||
## pep.py
|
## 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:
|
This is Ripple's bancho server. It handles:
|
||||||
- Client login
|
- Client login
|
||||||
- Online users listing and statuses
|
- Online users listing and statuses
|
||||||
@@ -9,6 +13,8 @@ This is Ripple's bancho server. It handles:
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
- Python 3.5
|
- Python 3.5
|
||||||
|
- Cython
|
||||||
|
- C compiler
|
||||||
- MySQLdb (`mysqlclient`)
|
- MySQLdb (`mysqlclient`)
|
||||||
- Tornado
|
- Tornado
|
||||||
- Bcrypt
|
- Bcrypt
|
||||||
@@ -23,9 +29,14 @@ afterwards, install the required dependencies with pip
|
|||||||
```
|
```
|
||||||
$ pip install -r requirements.txt
|
$ 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
|
$ python3 pep.py
|
||||||
|
...
|
||||||
$ nano config.ini
|
$ nano config.ini
|
||||||
```
|
```
|
||||||
you can run pep.py by typing
|
you can run pep.py by typing
|
||||||
|
2
common
2
common
Submodule common updated: db36e8d589...3288420cd8
@@ -304,22 +304,7 @@ def systemShutdown(fro, chan, message):
|
|||||||
return restartShutdown(False)
|
return restartShutdown(False)
|
||||||
|
|
||||||
def systemReload(fro, chan, message):
|
def systemReload(fro, chan, message):
|
||||||
# Reload settings from bancho_settings
|
glob.banchoConf.reload()
|
||||||
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))
|
|
||||||
|
|
||||||
return "Bancho settings reloaded!"
|
return "Bancho settings reloaded!"
|
||||||
|
|
||||||
def systemMaintenance(fro, chan, message):
|
def systemMaintenance(fro, chan, message):
|
||||||
@@ -753,7 +738,7 @@ commands = [
|
|||||||
"callback": moderated
|
"callback": moderated
|
||||||
}, {
|
}, {
|
||||||
"trigger": "!kickall",
|
"trigger": "!kickall",
|
||||||
"privileges": privileges.ADMIN_KICK_USERS,
|
"privileges": privileges.ADMIN_MANAGE_SERVERS,
|
||||||
"callback": kickAll
|
"callback": kickAll
|
||||||
}, {
|
}, {
|
||||||
"trigger": "!kick",
|
"trigger": "!kick",
|
||||||
|
@@ -5,20 +5,19 @@ from constants import clientPackets
|
|||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
from objects import glob
|
from objects import glob
|
||||||
|
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
# Get usertoken data
|
# Get usertoken data
|
||||||
userID = userToken.userID
|
userID = userToken.userID
|
||||||
username = userToken.username
|
username = userToken.username
|
||||||
|
|
||||||
# Make sure we are not banned
|
# Make sure we are not banned
|
||||||
if userUtils.isBanned(userID):
|
#if userUtils.isBanned(userID):
|
||||||
userToken.enqueue(serverPackets.loginBanned())
|
# userToken.enqueue(serverPackets.loginBanned())
|
||||||
return
|
# return
|
||||||
|
|
||||||
# Send restricted message if needed
|
# Send restricted message if needed
|
||||||
if userToken.restricted:
|
#if userToken.restricted:
|
||||||
userToken.checkRestricted(True)
|
# userToken.checkRestricted(True)
|
||||||
|
|
||||||
# Change action packet
|
# Change action packet
|
||||||
packetData = clientPackets.userActionChange(packetData)
|
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
|
# 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"]):
|
#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
|
|
||||||
|
# Update cached stats if we've changed gamemode
|
||||||
|
if userToken.gameMode != packetData["gameMode"]:
|
||||||
userToken.gameMode = packetData["gameMode"]
|
userToken.gameMode = packetData["gameMode"]
|
||||||
userToken.updateCachedStats()
|
userToken.updateCachedStats()
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import time
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
from common.log import logUtils as log
|
from common.log import logUtils as log
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
@@ -6,7 +7,7 @@ from helpers import chatHelper as chat
|
|||||||
from objects import glob
|
from objects import glob
|
||||||
|
|
||||||
|
|
||||||
def handle(userToken, _=None):
|
def handle(userToken, _=None, deleteToken=True):
|
||||||
# get usertoken data
|
# get usertoken data
|
||||||
userID = userToken.userID
|
userID = userToken.userID
|
||||||
username = userToken.username
|
username = userToken.username
|
||||||
@@ -38,7 +39,19 @@ def handle(userToken, _=None):
|
|||||||
glob.ircServer.forceDisconnection(userToken.username)
|
glob.ircServer.forceDisconnection(userToken.username)
|
||||||
|
|
||||||
# Delete token
|
# 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
|
# Console output
|
||||||
log.info("{} has been disconnected. (logout)".format(username))
|
log.info("{} has been disconnected. (logout)".format(username))
|
||||||
|
4
full_build.sh
Normal file
4
full_build.sh
Normal 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
|
@@ -10,7 +10,7 @@ class handler(requestsManager.asyncRequestHandler):
|
|||||||
data = {"message": "unknown error"}
|
data = {"message": "unknown error"}
|
||||||
try:
|
try:
|
||||||
# Get online users count
|
# 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
|
# Status code and message
|
||||||
statusCode = 200
|
statusCode = 200
|
||||||
|
@@ -122,7 +122,7 @@ class handler(SentryMixin, requestsManager.asyncRequestHandler):
|
|||||||
packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent),
|
packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent),
|
||||||
packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent),
|
packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent),
|
||||||
packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent),
|
packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent),
|
||||||
|
|
||||||
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
|
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
|
||||||
packetIDs.client_channelPart: handleEvent(channelPartEvent),
|
packetIDs.client_channelPart: handleEvent(channelPartEvent),
|
||||||
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
|
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
|
||||||
@@ -206,6 +206,9 @@ class handler(SentryMixin, requestsManager.asyncRequestHandler):
|
|||||||
userToken.updatePingTime()
|
userToken.updatePingTime()
|
||||||
# Release token lock
|
# Release token lock
|
||||||
userToken.lock.release()
|
userToken.lock.release()
|
||||||
|
# Delete token if kicked
|
||||||
|
if userToken.kicked:
|
||||||
|
glob.tokens.deleteToken(userToken)
|
||||||
|
|
||||||
if glob.outputRequestTime:
|
if glob.outputRequestTime:
|
||||||
# End time
|
# End time
|
@@ -127,9 +127,9 @@ class config:
|
|||||||
self.config.set("discord", "devgroup", "")
|
self.config.set("discord", "devgroup", "")
|
||||||
|
|
||||||
self.config.add_section("datadog")
|
self.config.add_section("datadog")
|
||||||
self.config.set("datadog", "enable")
|
self.config.set("datadog", "enable", "0")
|
||||||
self.config.set("datadog", "apikey")
|
self.config.set("datadog", "apikey", "")
|
||||||
self.config.set("datadog", "appkey")
|
self.config.set("datadog", "appkey", "")
|
||||||
|
|
||||||
self.config.add_section("irc")
|
self.config.add_section("irc")
|
||||||
self.config.set("irc", "enable", "1")
|
self.config.set("irc", "enable", "1")
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
import struct
|
import struct
|
||||||
from constants import dataTypes
|
from constants import dataTypes
|
||||||
|
|
||||||
def uleb128Encode(num):
|
cpdef bytearray uleb128Encode(int num):
|
||||||
"""
|
"""
|
||||||
Encode an int to uleb128
|
Encode an int to uleb128
|
||||||
|
|
||||||
:param num: int to encode
|
:param num: int to encode
|
||||||
:return: bytearray with encoded number
|
:return: bytearray with encoded number
|
||||||
"""
|
"""
|
||||||
arr = bytearray()
|
cdef bytearray arr = bytearray()
|
||||||
length = 0
|
cdef int length = 0
|
||||||
|
|
||||||
if num == 0:
|
if num == 0:
|
||||||
return bytearray(b"\x00")
|
return bytearray(b"\x00")
|
||||||
@@ -23,15 +23,16 @@ def uleb128Encode(num):
|
|||||||
|
|
||||||
return arr
|
return arr
|
||||||
|
|
||||||
def uleb128Decode(num):
|
cpdef list uleb128Decode(bytes num):
|
||||||
"""
|
"""
|
||||||
Decode a uleb128 to int
|
Decode a uleb128 to int
|
||||||
|
|
||||||
:param num: encoded uleb128 int
|
:param num: encoded uleb128 int
|
||||||
:return: (total, length)
|
:return: (total, length)
|
||||||
"""
|
"""
|
||||||
shift = 0
|
cdef int shift = 0
|
||||||
arr = [0,0] #total, length
|
cdef list arr = [0,0] #total, length
|
||||||
|
cdef int b
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
b = num[arr[1]]
|
b = num[arr[1]]
|
||||||
@@ -43,7 +44,7 @@ def uleb128Decode(num):
|
|||||||
|
|
||||||
return arr
|
return arr
|
||||||
|
|
||||||
def unpackData(data, dataType):
|
cpdef unpackData(bytes data, int dataType):
|
||||||
"""
|
"""
|
||||||
Unpacks a single section of a packet.
|
Unpacks a single section of a packet.
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ def unpackData(data, dataType):
|
|||||||
# Unpack
|
# Unpack
|
||||||
return struct.unpack(unpackType, bytes(data))[0]
|
return struct.unpack(unpackType, bytes(data))[0]
|
||||||
|
|
||||||
def packData(__data, dataType):
|
cpdef bytes packData(__data, int dataType):
|
||||||
"""
|
"""
|
||||||
Packs a single section of a packet.
|
Packs a single section of a packet.
|
||||||
|
|
||||||
@@ -82,8 +83,9 @@ def packData(__data, dataType):
|
|||||||
:param dataType: data type
|
:param dataType: data type
|
||||||
:return: packed bytes
|
:return: packed bytes
|
||||||
"""
|
"""
|
||||||
data = bytes() # data to return
|
cdef bytes data = bytes() # data to return
|
||||||
pack = True # if True, use pack. False only with strings
|
cdef bint pack = True # if True, use pack. False only with strings
|
||||||
|
cdef str packType
|
||||||
|
|
||||||
# Get right pack Type
|
# Get right pack Type
|
||||||
if dataType == dataTypes.BBYTES:
|
if dataType == dataTypes.BBYTES:
|
||||||
@@ -134,7 +136,7 @@ def packData(__data, dataType):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def buildPacket(__packet, __packetData=None):
|
cpdef bytes buildPacket(int __packet, list __packetData = []):
|
||||||
"""
|
"""
|
||||||
Builds a packet
|
Builds a packet
|
||||||
|
|
||||||
@@ -143,13 +145,12 @@ def buildPacket(__packet, __packetData=None):
|
|||||||
:return: packet bytes
|
:return: packet bytes
|
||||||
"""
|
"""
|
||||||
# Set some variables
|
# Set some variables
|
||||||
if __packetData is None:
|
cdef bytes packetData = bytes()
|
||||||
__packetData = []
|
cdef int packetLength = 0
|
||||||
packetData = bytes()
|
cdef bytes packetBytes = bytes()
|
||||||
packetLength = 0
|
|
||||||
packetBytes = bytes()
|
|
||||||
|
|
||||||
# Pack packet data
|
# Pack packet data
|
||||||
|
cdef list i
|
||||||
for i in __packetData:
|
for i in __packetData:
|
||||||
packetData += packData(i[0], i[1])
|
packetData += packData(i[0], i[1])
|
||||||
|
|
||||||
@@ -163,7 +164,7 @@ def buildPacket(__packet, __packetData=None):
|
|||||||
packetBytes += packetData # packet data
|
packetBytes += packetData # packet data
|
||||||
return packetBytes
|
return packetBytes
|
||||||
|
|
||||||
def readPacketID(stream):
|
cpdef int readPacketID(bytes stream):
|
||||||
"""
|
"""
|
||||||
Read packetID (first two bytes) from a packet
|
Read packetID (first two bytes) from a packet
|
||||||
|
|
||||||
@@ -172,7 +173,7 @@ def readPacketID(stream):
|
|||||||
"""
|
"""
|
||||||
return unpackData(stream[0:2], dataTypes.UINT16)
|
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
|
Read packet data length (3:7 bytes) from a packet
|
||||||
|
|
||||||
@@ -182,7 +183,7 @@ def readPacketLength(stream):
|
|||||||
return unpackData(stream[3:7], dataTypes.UINT32)
|
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`
|
Read packet data from `stream` according to `structure`
|
||||||
:param stream: packet bytes
|
:param stream: packet bytes
|
||||||
@@ -192,11 +193,10 @@ def readPacketData(stream, structure=None, hasFirstBytes = True):
|
|||||||
:return: {name: unpackedValue, ...}
|
:return: {name: unpackedValue, ...}
|
||||||
"""
|
"""
|
||||||
# Read packet ID (first 2 bytes)
|
# Read packet ID (first 2 bytes)
|
||||||
if structure is None:
|
cdef dict data = {}
|
||||||
structure = []
|
|
||||||
data = {}
|
|
||||||
|
|
||||||
# Skip packet ID and packet length if needed
|
# Skip packet ID and packet length if needed
|
||||||
|
cdef start, end
|
||||||
if hasFirstBytes:
|
if hasFirstBytes:
|
||||||
end = 7
|
end = 7
|
||||||
start = 7
|
start = 7
|
||||||
@@ -205,6 +205,8 @@ def readPacketData(stream, structure=None, hasFirstBytes = True):
|
|||||||
start = 0
|
start = 0
|
||||||
|
|
||||||
# Read packet
|
# Read packet
|
||||||
|
cdef list i
|
||||||
|
cdef bint unpack
|
||||||
for i in structure:
|
for i in structure:
|
||||||
start = end
|
start = end
|
||||||
unpack = True
|
unpack = True
|
||||||
@@ -239,7 +241,10 @@ def readPacketData(stream, structure=None, hasFirstBytes = True):
|
|||||||
end = start+length[0]+length[1]+1
|
end = start+length[0]+length[1]+1
|
||||||
|
|
||||||
# Read bytes
|
# 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:
|
elif i[1] == dataTypes.BYTE:
|
||||||
end = start+1
|
end = start+1
|
||||||
elif i[1] == dataTypes.UINT16 or i[1] == dataTypes.SINT16:
|
elif i[1] == dataTypes.UINT16 or i[1] == dataTypes.SINT16:
|
@@ -1,5 +1,6 @@
|
|||||||
# TODO: Rewrite this shit
|
# TODO: Rewrite this shit
|
||||||
from common import generalUtils
|
from common import generalUtils
|
||||||
|
from constants import serverPackets
|
||||||
from objects import glob
|
from objects import glob
|
||||||
|
|
||||||
|
|
||||||
@@ -41,3 +42,20 @@ class banchoConfig:
|
|||||||
"""
|
"""
|
||||||
self.config["banchoMaintenance"] = maintenance
|
self.config["banchoMaintenance"] = maintenance
|
||||||
glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(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))
|
@@ -31,6 +31,7 @@ class token:
|
|||||||
self.privileges = userUtils.getPrivileges(self.userID)
|
self.privileges = userUtils.getPrivileges(self.userID)
|
||||||
self.admin = userUtils.isInPrivilegeGroup(self.userID, "developer") or userUtils.isInPrivilegeGroup(self.userID, "community manager")
|
self.admin = userUtils.isInPrivilegeGroup(self.userID, "developer") or userUtils.isInPrivilegeGroup(self.userID, "community manager")
|
||||||
self.irc = irc
|
self.irc = irc
|
||||||
|
self.kicked = False
|
||||||
self.restricted = userUtils.isRestricted(self.userID)
|
self.restricted = userUtils.isRestricted(self.userID)
|
||||||
self.loginTime = int(time.time())
|
self.loginTime = int(time.time())
|
||||||
self.pingTime = self.loginTime
|
self.pingTime = self.loginTime
|
||||||
@@ -323,22 +324,28 @@ class token:
|
|||||||
self.enqueue(serverPackets.loginFailed())
|
self.enqueue(serverPackets.loginFailed())
|
||||||
|
|
||||||
# Logout event
|
# 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)
|
Silences this user (db, packet and token)
|
||||||
|
|
||||||
:param seconds: silence length in seconds
|
:param seconds: silence length in seconds. If None, get it from db. Default: None
|
||||||
:param reason: silence reason
|
:param reason: silence reason. Default: empty string
|
||||||
:param author: userID of who has silenced the user. Default: 999 (FokaBot)
|
:param author: userID of who has silenced the user. Default: 999 (FokaBot)
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
# Silence in db and token
|
if seconds is None:
|
||||||
self.silenceEndTime = int(time.time())+seconds
|
# Get silence expire from db if needed
|
||||||
userUtils.silence(self.userID, seconds, reason, author)
|
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))
|
self.enqueue(serverPackets.silenceEndTime(seconds))
|
||||||
|
|
||||||
# Send silenced packet to everyone else
|
# Send silenced packet to everyone else
|
||||||
@@ -394,18 +401,29 @@ class token:
|
|||||||
self.gameRank = stats["gameRank"]
|
self.gameRank = stats["gameRank"]
|
||||||
self.pp = stats["pp"]
|
self.pp = stats["pp"]
|
||||||
|
|
||||||
def checkRestricted(self, force=False):
|
def checkRestricted(self):
|
||||||
"""
|
"""
|
||||||
Check if this token is restricted. If so, send fokabot message
|
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:
|
:return:
|
||||||
"""
|
"""
|
||||||
if force:
|
oldRestricted = self.restricted
|
||||||
self.restricted = userUtils.isRestricted(self.userID)
|
self.restricted = userUtils.isRestricted(self.userID)
|
||||||
if self.restricted:
|
if self.restricted:
|
||||||
self.setRestricted()
|
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):
|
def setRestricted(self):
|
||||||
"""
|
"""
|
||||||
@@ -415,7 +433,16 @@ class token:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
self.restricted = True
|
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):
|
def joinStream(self, name):
|
||||||
"""
|
"""
|
||||||
|
@@ -24,6 +24,7 @@ class tokenList:
|
|||||||
"""
|
"""
|
||||||
newToken = osuToken.token(userID, ip=ip, irc=irc, timeOffset=timeOffset, tournament=tournament)
|
newToken = osuToken.token(userID, ip=ip, irc=irc, timeOffset=timeOffset, tournament=tournament)
|
||||||
self.tokens[newToken.token] = newToken
|
self.tokens[newToken.token] = newToken
|
||||||
|
glob.redis.incr("ripple:online_users")
|
||||||
return newToken
|
return newToken
|
||||||
|
|
||||||
def deleteToken(self, token):
|
def deleteToken(self, token):
|
||||||
@@ -34,12 +35,10 @@ class tokenList:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if token in self.tokens:
|
if token in self.tokens:
|
||||||
# Delete session from DB
|
|
||||||
if self.tokens[token].ip != "":
|
if self.tokens[token].ip != "":
|
||||||
userUtils.deleteBanchoSessions(self.tokens[token].userID, self.tokens[token].ip)
|
userUtils.deleteBanchoSessions(self.tokens[token].userID, self.tokens[token].ip)
|
||||||
|
|
||||||
# Pop token from list
|
|
||||||
self.tokens.pop(token)
|
self.tokens.pop(token)
|
||||||
|
glob.redis.decr("ripple:online_users")
|
||||||
|
|
||||||
def getUserIDFromToken(self, token):
|
def getUserIDFromToken(self, token):
|
||||||
"""
|
"""
|
||||||
@@ -104,10 +103,15 @@ class tokenList:
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
# Delete older tokens
|
# Delete older tokens
|
||||||
|
delete = []
|
||||||
for key, value in list(self.tokens.items()):
|
for key, value in list(self.tokens.items()):
|
||||||
if value.userID == userID:
|
if value.userID == userID:
|
||||||
# Delete this token from the dictionary
|
# 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):
|
def multipleEnqueue(self, packet, who, but = False):
|
||||||
"""
|
"""
|
||||||
@@ -185,12 +189,16 @@ class tokenList:
|
|||||||
|
|
||||||
def deleteBanchoSessions(self):
|
def deleteBanchoSessions(self):
|
||||||
"""
|
"""
|
||||||
Truncate bancho_sessions table.
|
Remove all `peppy:sessions:*` redis keys.
|
||||||
Call at bancho startup to delete old cached sessions
|
Call at bancho startup to delete old cached sessions
|
||||||
|
|
||||||
:return:
|
: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):
|
def tokenExists(self, username = "", userID = -1):
|
||||||
|
42
pep.py
42
pep.py
@@ -16,7 +16,7 @@ from common.constants import bcolors
|
|||||||
from common.db import dbConnector
|
from common.db import dbConnector
|
||||||
from common.ddog import datadogClient
|
from common.ddog import datadogClient
|
||||||
from common.log import logUtils as log
|
from common.log import logUtils as log
|
||||||
from common.ripple import userUtils
|
from common.redis import pubSub
|
||||||
from common.web import schiavo
|
from common.web import schiavo
|
||||||
from handlers import apiFokabotMessageHandler
|
from handlers import apiFokabotMessageHandler
|
||||||
from handlers import apiIsOnlineHandler
|
from handlers import apiIsOnlineHandler
|
||||||
@@ -34,6 +34,12 @@ from objects import banchoConfig
|
|||||||
from objects import chatFilters
|
from objects import chatFilters
|
||||||
from objects import fokabot
|
from objects import fokabot
|
||||||
from objects import glob
|
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():
|
def make_app():
|
||||||
@@ -108,6 +114,8 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# Empty redis cache
|
# Empty redis cache
|
||||||
try:
|
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:*")
|
glob.redis.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "peppy:*")
|
||||||
except redis.exceptions.ResponseError:
|
except redis.exceptions.ResponseError:
|
||||||
# Script returns error if there are no keys starting with peppy:*
|
# 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("online_users", lambda: len(glob.tokens.tokens)),
|
||||||
datadogClient.periodicCheck("multiplayer_matches", lambda: len(glob.matches.matches)),
|
datadogClient.periodicCheck("multiplayer_matches", lambda: len(glob.matches.matches)),
|
||||||
|
|
||||||
datadogClient.periodicCheck("ram_clients", lambda: generalUtils.getTotalSize(glob.tokens)),
|
#datadogClient.periodicCheck("ram_clients", lambda: generalUtils.getTotalSize(glob.tokens)),
|
||||||
datadogClient.periodicCheck("ram_matches", lambda: generalUtils.getTotalSize(glob.matches)),
|
#datadogClient.periodicCheck("ram_matches", lambda: generalUtils.getTotalSize(glob.matches)),
|
||||||
datadogClient.periodicCheck("ram_channels", lambda: generalUtils.getTotalSize(glob.channels)),
|
#datadogClient.periodicCheck("ram_channels", lambda: generalUtils.getTotalSize(glob.channels)),
|
||||||
datadogClient.periodicCheck("ram_file_buffers", lambda: generalUtils.getTotalSize(glob.fileBuffers)),
|
#datadogClient.periodicCheck("ram_file_buffers", lambda: generalUtils.getTotalSize(glob.fileBuffers)),
|
||||||
datadogClient.periodicCheck("ram_file_locks", lambda: generalUtils.getTotalSize(glob.fLocks)),
|
#datadogClient.periodicCheck("ram_file_locks", lambda: generalUtils.getTotalSize(glob.fLocks)),
|
||||||
datadogClient.periodicCheck("ram_datadog", lambda: generalUtils.getTotalSize(glob.datadogClient)),
|
#datadogClient.periodicCheck("ram_datadog", lambda: generalUtils.getTotalSize(glob.datadogClient)),
|
||||||
datadogClient.periodicCheck("ram_verified_cache", lambda: generalUtils.getTotalSize(glob.verifiedCache)),
|
#datadogClient.periodicCheck("ram_verified_cache", lambda: generalUtils.getTotalSize(glob.verifiedCache)),
|
||||||
#datadogClient.periodicCheck("ram_userid_cache", lambda: generalUtils.getTotalSize(glob.userIDCache)),
|
#datadogClient.periodicCheck("ram_irc", lambda: generalUtils.getTotalSize(glob.ircServer)),
|
||||||
#datadogClient.periodicCheck("ram_pool", lambda: generalUtils.getTotalSize(glob.pool)),
|
#datadogClient.periodicCheck("ram_tornado", lambda: generalUtils.getTotalSize(glob.application)),
|
||||||
datadogClient.periodicCheck("ram_irc", lambda: generalUtils.getTotalSize(glob.ircServer)),
|
#datadogClient.periodicCheck("ram_db", lambda: generalUtils.getTotalSize(glob.db)),
|
||||||
datadogClient.periodicCheck("ram_tornado", lambda: generalUtils.getTotalSize(glob.application)),
|
|
||||||
datadogClient.periodicCheck("ram_db", lambda: generalUtils.getTotalSize(glob.db)),
|
|
||||||
])
|
])
|
||||||
else:
|
else:
|
||||||
consoleHelper.printColored("[!] Warning! Datadog stats tracking is disabled!", bcolors.YELLOW)
|
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)
|
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)
|
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
|
# Start tornado
|
||||||
glob.application.listen(serverPort)
|
glob.application.listen(serverPort)
|
||||||
tornado.ioloop.IOLoop.instance().start()
|
tornado.ioloop.IOLoop.instance().start()
|
||||||
|
0
pubSubHandlers/__init__.py
Normal file
0
pubSubHandlers/__init__.py
Normal file
18
pubSubHandlers/banHandler.py
Normal file
18
pubSubHandlers/banHandler.py
Normal 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()
|
49
pubSubHandlers/changeUsernameHandler.py
Normal file
49
pubSubHandlers/changeUsernameHandler.py
Normal 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"])
|
18
pubSubHandlers/disconnectHandler.py
Normal file
18
pubSubHandlers/disconnectHandler.py
Normal 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")
|
15
pubSubHandlers/updateSilenceHandler.py
Normal file
15
pubSubHandlers/updateSilenceHandler.py
Normal 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()
|
15
pubSubHandlers/updateStatsHandler.py
Normal file
15
pubSubHandlers/updateStatsHandler.py
Normal 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()
|
@@ -5,4 +5,5 @@ psutil
|
|||||||
raven
|
raven
|
||||||
bcrypt>=3.1.1
|
bcrypt>=3.1.1
|
||||||
dill
|
dill
|
||||||
redis
|
redis
|
||||||
|
cython
|
17
setup.py
Normal file
17
setup.py
Normal 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),
|
||||||
|
)
|
Reference in New Issue
Block a user