Skip to content

Commit

Permalink
.BANCHO. Add pubsub handlers for username changes, bans, restrictions…
Browse files Browse the repository at this point in the history
…, silences, stats update, kicks and bancho settings reload.
  • Loading branch information
xnyo committed Nov 20, 2016
1 parent fb00063 commit aa32e8b
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 42 deletions.
17 changes: 1 addition & 16 deletions constants/fokabotCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
17 changes: 9 additions & 8 deletions events/changeActionEvent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@
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)
Expand All @@ -34,8 +33,10 @@ def handle(userToken, packetData):
'''

# 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()

Expand Down
17 changes: 15 additions & 2 deletions events/logoutEvent.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import time
import json

from common.log import logUtils as log
from constants import serverPackets
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
Expand Down Expand Up @@ -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))
5 changes: 4 additions & 1 deletion handlers/mainHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def wrapper():
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),
Expand Down Expand Up @@ -206,6 +206,9 @@ def wrapper():
userToken.updatePingTime()
# Release token lock
userToken.lock.release()
# Delete token if kicked
if userToken.kicked:
glob.tokens.deleteToken(userToken)

if glob.outputRequestTime:
# End time
Expand Down
18 changes: 18 additions & 0 deletions objects/banchoConfig.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# TODO: Rewrite this shit
from common import generalUtils
from constants import serverPackets
from objects import glob


Expand Down Expand Up @@ -41,3 +42,20 @@ def setMaintenance(self, maintenance):
"""
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))
55 changes: 41 additions & 14 deletions objects/osuToken.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(self, userID, token_ = None, ip ="", irc = False, timeOffset = 0, t
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
Expand Down Expand Up @@ -323,22 +324,28 @@ def kick(self, message="You have been kicked from the server. Please login again
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)

# Silence token
self.silenceEndTime = int(time.time()) + seconds

# Send silence packet to target
# Send silence packet to user
self.enqueue(serverPackets.silenceEndTime(seconds))

# Send silenced packet to everyone else
Expand Down Expand Up @@ -394,18 +401,29 @@ def updateCachedStats(self):
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):
"""
Expand All @@ -415,7 +433,16 @@ def setRestricted(self):
: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):
"""
Expand Down
18 changes: 17 additions & 1 deletion pep.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
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
Expand All @@ -34,6 +34,12 @@
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():
Expand Down Expand Up @@ -264,6 +270,16 @@ def make_app():
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()
Expand Down
Empty file added pubSubHandlers/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions pubSubHandlers/banHandler.py
Original file line number Diff line number Diff line change
@@ -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 changes: 49 additions & 0 deletions pubSubHandlers/changeUsernameHandler.py
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions pubSubHandlers/disconnectHandler.py
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions pubSubHandlers/updateSilenceHandler.py
Original file line number Diff line number Diff line change
@@ -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()
Loading

0 comments on commit aa32e8b

Please sign in to comment.