Updated submodules
This commit is contained in:
commit
768971e240
2
common
2
common
|
@ -1 +1 @@
|
||||||
Subproject commit 94c7474e395be2b1ef3ff26e61fc9470598eba69
|
Subproject commit 95b2206a20ddcbb44836a92bef3e2570b2cf30e1
|
|
@ -104,4 +104,7 @@ class invalidUserException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class wrongChannelException(Exception):
|
class wrongChannelException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class periodicLoopException(Exception):
|
||||||
pass
|
pass
|
|
@ -10,7 +10,7 @@ from common import generalUtils
|
||||||
from common.constants import mods
|
from common.constants import mods
|
||||||
from common.log import logUtils as log
|
from common.log import logUtils as log
|
||||||
from common.ripple import userUtils
|
from common.ripple import userUtils
|
||||||
from constants import exceptions, slotStatuses, matchModModes, matchTeams, matchTeamTypes
|
from constants import exceptions, slotStatuses, matchModModes, matchTeams, matchTeamTypes, matchScoringTypes
|
||||||
from common.constants import gameModes
|
from common.constants import gameModes
|
||||||
from common.constants import privileges
|
from common.constants import privileges
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
|
@ -20,6 +20,16 @@ from objects import glob
|
||||||
from helpers import chatHelper as chat
|
from helpers import chatHelper as chat
|
||||||
from common.web import cheesegull
|
from common.web import cheesegull
|
||||||
|
|
||||||
|
|
||||||
|
def bloodcatMessage(beatmapID):
|
||||||
|
beatmap = glob.db.fetch("SELECT song_name, beatmapset_id FROM beatmaps WHERE beatmap_id = %s LIMIT 1", [beatmapID])
|
||||||
|
if beatmap is None:
|
||||||
|
return "Sorry, I'm not able to provide a download link for this map :("
|
||||||
|
return "Download [https://bloodcat.com/osu/s/{} {}] from Bloodcat".format(
|
||||||
|
beatmap["beatmapset_id"],
|
||||||
|
beatmap["song_name"],
|
||||||
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Commands callbacks
|
Commands callbacks
|
||||||
|
|
||||||
|
@ -48,7 +58,7 @@ def faq(fro, chan, message):
|
||||||
def roll(fro, chan, message):
|
def roll(fro, chan, message):
|
||||||
maxPoints = 100
|
maxPoints = 100
|
||||||
if len(message) >= 1:
|
if len(message) >= 1:
|
||||||
if message[0].isdigit() == True and int(message[0]) > 0:
|
if message[0].isdigit() and int(message[0]) > 0:
|
||||||
maxPoints = int(message[0])
|
maxPoints = int(message[0])
|
||||||
|
|
||||||
points = random.randrange(0,maxPoints)
|
points = random.randrange(0,maxPoints)
|
||||||
|
@ -433,6 +443,14 @@ def getPPMessage(userID, just_data = False):
|
||||||
|
|
||||||
def tillerinoNp(fro, chan, message):
|
def tillerinoNp(fro, chan, message):
|
||||||
try:
|
try:
|
||||||
|
# Bloodcat trigger for #spect_
|
||||||
|
if chan.startswith("#spect_"):
|
||||||
|
spectatorHostUserID = getSpectatorHostUserIDFromChannel(chan)
|
||||||
|
spectatorHostToken = glob.tokens.getTokenFromUserID(spectatorHostUserID, ignoreIRC=True)
|
||||||
|
if spectatorHostToken is None:
|
||||||
|
return False
|
||||||
|
return bloodcatMessage(spectatorHostToken.beatmapID)
|
||||||
|
|
||||||
# Run the command in PM only
|
# Run the command in PM only
|
||||||
if chan.startswith("#"):
|
if chan.startswith("#"):
|
||||||
return False
|
return False
|
||||||
|
@ -746,18 +764,27 @@ def report(fro, chan, message):
|
||||||
token.enqueue(serverPackets.notification(msg))
|
token.enqueue(serverPackets.notification(msg))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def multiplayer(fro, chan, message):
|
def getMatchIDFromChannel(chan):
|
||||||
def getMatchIDFromChannel(chan):
|
if not chan.lower().startswith("#multi_"):
|
||||||
if not chan.lower().startswith("#multi_"):
|
raise exceptions.wrongChannelException()
|
||||||
raise exceptions.wrongChannelException()
|
parts = chan.lower().split("_")
|
||||||
parts = chan.lower().split("_")
|
if len(parts) < 2 or not parts[1].isdigit():
|
||||||
if len(parts) < 2 or not parts[1].isdigit():
|
raise exceptions.wrongChannelException()
|
||||||
raise exceptions.wrongChannelException()
|
matchID = int(parts[1])
|
||||||
matchID = int(parts[1])
|
if matchID not in glob.matches.matches:
|
||||||
if matchID not in glob.matches.matches:
|
raise exceptions.matchNotFoundException()
|
||||||
raise exceptions.matchNotFoundException()
|
return matchID
|
||||||
return matchID
|
|
||||||
|
|
||||||
|
def getSpectatorHostUserIDFromChannel(chan):
|
||||||
|
if not chan.lower().startswith("#spect_"):
|
||||||
|
raise exceptions.wrongChannelException()
|
||||||
|
parts = chan.lower().split("_")
|
||||||
|
if len(parts) < 2 or not parts[1].isdigit():
|
||||||
|
raise exceptions.wrongChannelException()
|
||||||
|
userID = int(parts[1])
|
||||||
|
return userID
|
||||||
|
|
||||||
|
def multiplayer(fro, chan, message):
|
||||||
def mpMake():
|
def mpMake():
|
||||||
if len(message) < 2:
|
if len(message) < 2:
|
||||||
raise exceptions.invalidArgumentsException("Wrong syntax: !mp make <name>")
|
raise exceptions.invalidArgumentsException("Wrong syntax: !mp make <name>")
|
||||||
|
@ -773,6 +800,12 @@ def multiplayer(fro, chan, message):
|
||||||
raise exceptions.invalidArgumentsException("Wrong syntax: !mp join <id>")
|
raise exceptions.invalidArgumentsException("Wrong syntax: !mp join <id>")
|
||||||
matchID = int(message[1])
|
matchID = int(message[1])
|
||||||
userToken = glob.tokens.getTokenFromUsername(fro, ignoreIRC=True)
|
userToken = glob.tokens.getTokenFromUsername(fro, ignoreIRC=True)
|
||||||
|
if userToken is None:
|
||||||
|
raise exceptions.invalidArgumentsException(
|
||||||
|
"No game clients found for {}, can't join the match. "
|
||||||
|
"If you're a referee and you want to join the chat "
|
||||||
|
"channel from IRC, use /join #multi_{} instead.".format(fro, matchID)
|
||||||
|
)
|
||||||
userToken.joinMatch(matchID)
|
userToken.joinMatch(matchID)
|
||||||
return "Attempting to join match #{}!".format(matchID)
|
return "Attempting to join match #{}!".format(matchID)
|
||||||
|
|
||||||
|
@ -997,6 +1030,8 @@ def multiplayer(fro, chan, message):
|
||||||
newMods |= mods.FLASHLIGHT
|
newMods |= mods.FLASHLIGHT
|
||||||
elif _mod.lower().strip() == "fi":
|
elif _mod.lower().strip() == "fi":
|
||||||
newMods |= mods.FADEIN
|
newMods |= mods.FADEIN
|
||||||
|
elif _mod.lower().strip() == "ez":
|
||||||
|
newMods |= mods.EASY
|
||||||
if _mod.lower().strip() == "none":
|
if _mod.lower().strip() == "none":
|
||||||
newMods = 0
|
newMods = 0
|
||||||
|
|
||||||
|
@ -1028,7 +1063,13 @@ def multiplayer(fro, chan, message):
|
||||||
|
|
||||||
def mpSettings():
|
def mpSettings():
|
||||||
_match = glob.matches.matches[getMatchIDFromChannel(chan)]
|
_match = glob.matches.matches[getMatchIDFromChannel(chan)]
|
||||||
msg = "PLAYERS IN THIS MATCH:\n"
|
single = False if len(message) < 2 else message[1].strip().lower() == "single"
|
||||||
|
msg = "PLAYERS IN THIS MATCH "
|
||||||
|
if not single:
|
||||||
|
msg += "(use !mp settings single for a single-line version):"
|
||||||
|
msg += "\n"
|
||||||
|
else:
|
||||||
|
msg += ": "
|
||||||
empty = True
|
empty = True
|
||||||
for slot in _match.slots:
|
for slot in _match.slots:
|
||||||
if slot.user is None:
|
if slot.user is None:
|
||||||
|
@ -1044,16 +1085,28 @@ def multiplayer(fro, chan, message):
|
||||||
else:
|
else:
|
||||||
readableStatus = readableStatuses[slot.status]
|
readableStatus = readableStatuses[slot.status]
|
||||||
empty = False
|
empty = False
|
||||||
msg += "* [{team}] <{status}> ~ {username}{mods}\n".format(
|
msg += "* [{team}] <{status}> ~ {username}{mods}{nl}".format(
|
||||||
team="red" if slot.team == matchTeams.RED else "blue" if slot.team == matchTeams.BLUE else "!! no team !!",
|
team="red" if slot.team == matchTeams.RED else "blue" if slot.team == matchTeams.BLUE else "!! no team !!",
|
||||||
status=readableStatus,
|
status=readableStatus,
|
||||||
username=glob.tokens.tokens[slot.user].username,
|
username=glob.tokens.tokens[slot.user].username,
|
||||||
mods=" (+ {})".format(generalUtils.readableMods(slot.mods)) if slot.mods > 0 else ""
|
mods=" (+ {})".format(generalUtils.readableMods(slot.mods)) if slot.mods > 0 else "",
|
||||||
|
nl=" | " if single else "\n"
|
||||||
)
|
)
|
||||||
if empty:
|
if empty:
|
||||||
msg += "Nobody.\n"
|
msg += "Nobody.\n"
|
||||||
|
msg = msg.rstrip(" | " if single else "\n")
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def mpScoreV():
|
||||||
|
if len(message) < 2 or message[1] not in ("1", "2"):
|
||||||
|
raise exceptions.invalidArgumentsException("Wrong syntax: !mp scorev <1|2>")
|
||||||
|
_match = glob.matches.matches[getMatchIDFromChannel(chan)]
|
||||||
|
_match.matchScoringType = matchScoringTypes.SCORE_V2 if message[1] == "2" else matchScoringTypes.SCORE
|
||||||
|
_match.sendUpdates()
|
||||||
|
return "Match scoring type set to scorev{}".format(message[1])
|
||||||
|
|
||||||
|
def mpHelp():
|
||||||
|
return "Supported subcommands: !mp <{}>".format("|".join(k for k in subcommands.keys()))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subcommands = {
|
subcommands = {
|
||||||
|
@ -1077,6 +1130,8 @@ def multiplayer(fro, chan, message):
|
||||||
"mods": mpMods,
|
"mods": mpMods,
|
||||||
"team": mpTeam,
|
"team": mpTeam,
|
||||||
"settings": mpSettings,
|
"settings": mpSettings,
|
||||||
|
"scorev": mpScoreV,
|
||||||
|
"help": mpHelp
|
||||||
}
|
}
|
||||||
requestedSubcommand = message[0].lower().strip()
|
requestedSubcommand = message[0].lower().strip()
|
||||||
if requestedSubcommand not in subcommands:
|
if requestedSubcommand not in subcommands:
|
||||||
|
@ -1124,7 +1179,26 @@ def rtx(fro, chan, message):
|
||||||
userToken.enqueue(serverPackets.rtx(message))
|
userToken.enqueue(serverPackets.rtx(message))
|
||||||
return ":ok_hand:"
|
return ":ok_hand:"
|
||||||
|
|
||||||
|
def bloodcat(fro, chan, message):
|
||||||
|
try:
|
||||||
|
matchID = getMatchIDFromChannel(chan)
|
||||||
|
except exceptions.wrongChannelException:
|
||||||
|
matchID = None
|
||||||
|
try:
|
||||||
|
spectatorHostUserID = getSpectatorHostUserIDFromChannel(chan)
|
||||||
|
except exceptions.wrongChannelException:
|
||||||
|
spectatorHostUserID = None
|
||||||
|
|
||||||
|
if matchID is not None:
|
||||||
|
if matchID not in glob.matches.matches:
|
||||||
|
return "This match doesn't seem to exist... Or does it...?"
|
||||||
|
beatmapID = glob.matches.matches[matchID].beatmapID
|
||||||
|
else:
|
||||||
|
spectatorHostToken = glob.tokens.getTokenFromUserID(spectatorHostUserID, ignoreIRC=True)
|
||||||
|
if spectatorHostToken is None:
|
||||||
|
return "The spectator host is offline."
|
||||||
|
beatmapID = spectatorHostToken.beatmapID
|
||||||
|
return bloodcatMessage(beatmapID)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -1276,6 +1350,9 @@ commands = [
|
||||||
"privileges": privileges.ADMIN_MANAGE_USERS,
|
"privileges": privileges.ADMIN_MANAGE_USERS,
|
||||||
"syntax": "<username> <message>",
|
"syntax": "<username> <message>",
|
||||||
"callback": rtx
|
"callback": rtx
|
||||||
|
}, {
|
||||||
|
"trigger": "!bloodcat",
|
||||||
|
"callback": bloodcat
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
# "trigger": "!acc",
|
# "trigger": "!acc",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
SCORE = 0
|
SCORE = 0
|
||||||
ACCURACY = 1
|
ACCURACY = 1
|
||||||
COMBO = 2
|
COMBO = 2
|
||||||
|
SCORE_V2 = 3
|
|
@ -61,9 +61,9 @@ def handle(tornadoRequest):
|
||||||
|
|
||||||
# Make sure we are not banned or locked
|
# Make sure we are not banned or locked
|
||||||
priv = userUtils.getPrivileges(userID)
|
priv = userUtils.getPrivileges(userID)
|
||||||
if userUtils.isBanned(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
if userUtils.isBanned(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
||||||
raise exceptions.loginBannedException()
|
raise exceptions.loginBannedException()
|
||||||
if userUtils.isLocked(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
if userUtils.isLocked(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
||||||
raise exceptions.loginLockedException()
|
raise exceptions.loginLockedException()
|
||||||
|
|
||||||
# 2FA check
|
# 2FA check
|
||||||
|
@ -75,7 +75,7 @@ def handle(tornadoRequest):
|
||||||
|
|
||||||
# Verify this user (if pending activation)
|
# Verify this user (if pending activation)
|
||||||
firstLogin = False
|
firstLogin = False
|
||||||
if priv & privileges.USER_PENDING_VERIFICATION > 0 or userUtils.hasVerifiedHardware(userID) == False:
|
if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(userID):
|
||||||
if userUtils.verifyUser(userID, clientData):
|
if userUtils.verifyUser(userID, clientData):
|
||||||
# Valid account
|
# Valid account
|
||||||
log.info("Account {} verified successfully!".format(userID))
|
log.info("Account {} verified successfully!".format(userID))
|
||||||
|
@ -118,6 +118,9 @@ def handle(tornadoRequest):
|
||||||
expireIn = "{} days".format(expireDays) if expireDays > 1 else "less than 24 hours"
|
expireIn = "{} days".format(expireDays) if expireDays > 1 else "less than 24 hours"
|
||||||
responseToken.enqueue(serverPackets.notification("Your donor tag expires in {}! When your donor tag expires, you won't have any of the donor privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Ripple and you don't want to lose your donor privileges, you can donate again by clicking on 'Support us' on Ripple's website.".format(expireIn)))
|
responseToken.enqueue(serverPackets.notification("Your donor tag expires in {}! When your donor tag expires, you won't have any of the donor privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Ripple and you don't want to lose your donor privileges, you can donate again by clicking on 'Support us' on Ripple's website.".format(expireIn)))
|
||||||
|
|
||||||
|
# Deprecate telegram 2fa and send alert
|
||||||
|
if userUtils.deprecateTelegram2Fa(userID):
|
||||||
|
responseToken.enqueue(serverPackets.notification("As stated on our blog, Telegram 2FA has been deprecated on 29th June 2018. Telegram 2FA has just been disabled from your account. If you want to keep your account secure with 2FA, please enable TOTP-based 2FA from our website https://ripple.moe. Thank you for your patience."))
|
||||||
|
|
||||||
# Set silence end UNIX time in token
|
# Set silence end UNIX time in token
|
||||||
responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)
|
responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)
|
||||||
|
@ -173,7 +176,7 @@ def handle(tornadoRequest):
|
||||||
|
|
||||||
# Output channels info
|
# Output channels info
|
||||||
for key, value in glob.channels.channels.items():
|
for key, value in glob.channels.channels.items():
|
||||||
if value.publicRead == True and value.hidden == False:
|
if value.publicRead and not value.hidden:
|
||||||
responseToken.enqueue(serverPackets.channelInfo(key))
|
responseToken.enqueue(serverPackets.channelInfo(key))
|
||||||
|
|
||||||
# Send friends list
|
# Send friends list
|
||||||
|
|
|
@ -8,4 +8,4 @@ def handle(userToken, packetData):
|
||||||
if matchID not in glob.matches.matches or not userToken.tournament:
|
if matchID not in glob.matches.matches or not userToken.tournament:
|
||||||
return
|
return
|
||||||
userToken.matchID = matchID
|
userToken.matchID = matchID
|
||||||
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID))
|
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID), force=True)
|
|
@ -7,5 +7,5 @@ def handle(userToken, packetData):
|
||||||
matchID = packetData["matchID"]
|
matchID = packetData["matchID"]
|
||||||
if matchID not in glob.matches.matches or not userToken.tournament:
|
if matchID not in glob.matches.matches or not userToken.tournament:
|
||||||
return
|
return
|
||||||
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID))
|
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID), force=True)
|
||||||
userToken.matchID = 0
|
userToken.matchID = 0
|
|
@ -17,7 +17,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"] = -1 if glob.restarting == True else 1
|
data["result"] = -1 if glob.restarting else 1
|
||||||
|
|
||||||
# Status code and message
|
# Status code and message
|
||||||
statusCode = 200
|
statusCode = 200
|
||||||
|
|
|
@ -106,7 +106,7 @@ class handler(requestsManager.asyncRequestHandler):
|
||||||
packetData = requestData[pos:(pos+dataLength+7)]
|
packetData = requestData[pos:(pos+dataLength+7)]
|
||||||
|
|
||||||
# Console output if needed
|
# Console output if needed
|
||||||
if glob.outputPackets == True and packetID != 4:
|
if glob.outputPackets and packetID != 4:
|
||||||
log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData)))
|
log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData)))
|
||||||
|
|
||||||
# Event handler
|
# Event handler
|
||||||
|
@ -179,7 +179,7 @@ class handler(requestsManager.asyncRequestHandler):
|
||||||
# Process/ignore packet
|
# Process/ignore packet
|
||||||
if packetID != 4:
|
if packetID != 4:
|
||||||
if packetID in eventHandler:
|
if packetID in eventHandler:
|
||||||
if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted):
|
if not userToken.restricted or (userToken.restricted and packetID in packetsRestricted):
|
||||||
eventHandler[packetID]()
|
eventHandler[packetID]()
|
||||||
else:
|
else:
|
||||||
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
|
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
|
||||||
|
|
|
@ -8,7 +8,7 @@ from objects import fokabot
|
||||||
from objects import glob
|
from objects import glob
|
||||||
|
|
||||||
|
|
||||||
def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=False):
|
||||||
"""
|
"""
|
||||||
Join a channel
|
Join a channel
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
||||||
:param token: user token object of user that joins the channel. Optional. userID can be used instead.
|
:param token: user token object of user that joins the channel. Optional. userID can be used instead.
|
||||||
:param channel: channel name
|
:param channel: channel name
|
||||||
:param toIRC: if True, send this channel join event to IRC. Must be true if joining from bancho. Default: True
|
:param toIRC: if True, send this channel join event to IRC. Must be true if joining from bancho. Default: True
|
||||||
|
:param force: whether to allow game clients to join #spect_ and #multi_ channels
|
||||||
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -33,11 +34,16 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
||||||
if channel not in glob.channels.channels:
|
if channel not in glob.channels.channels:
|
||||||
raise exceptions.channelUnknownException()
|
raise exceptions.channelUnknownException()
|
||||||
|
|
||||||
|
# Make sure a game client is not trying to join a #multi_ or #spect_ channel manually
|
||||||
|
channelObject = glob.channels.channels[channel]
|
||||||
|
if channelObject.isSpecial and not token.irc and not force:
|
||||||
|
raise exceptions.channelUnknownException()
|
||||||
|
|
||||||
# Add the channel to our joined channel
|
# Add the channel to our joined channel
|
||||||
token.joinChannel(glob.channels.channels[channel])
|
token.joinChannel(channelObject)
|
||||||
|
|
||||||
# Send channel joined (IRC)
|
# Send channel joined (IRC)
|
||||||
if glob.irc == True and toIRC == True:
|
if glob.irc and not toIRC:
|
||||||
glob.ircServer.banchoJoinChannel(token.username, channel)
|
glob.ircServer.banchoJoinChannel(token.username, channel)
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
|
@ -58,7 +64,7 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
||||||
log.warning("User not connected to IRC/Bancho")
|
log.warning("User not connected to IRC/Bancho")
|
||||||
return 403 # idk
|
return 403 # idk
|
||||||
|
|
||||||
def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False):
|
def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False, force=False):
|
||||||
"""
|
"""
|
||||||
Part a channel
|
Part a channel
|
||||||
|
|
||||||
|
@ -67,6 +73,7 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
|
||||||
:param channel: channel name
|
:param channel: channel name
|
||||||
:param toIRC: if True, send this channel join event to IRC. Must be true if joining from bancho. Optional. Default: True
|
:param toIRC: if True, send this channel join event to IRC. Must be true if joining from bancho. Optional. Default: True
|
||||||
:param kick: if True, channel tab will be closed on client. Used when leaving lobby. Optional. Default: False
|
:param kick: if True, channel tab will be closed on client. Used when leaving lobby. Optional. Default: False
|
||||||
|
:param force: whether to allow game clients to part #spect_ and #multi_ channels
|
||||||
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -103,17 +110,21 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
|
||||||
if channel not in glob.channels.channels:
|
if channel not in glob.channels.channels:
|
||||||
raise exceptions.channelUnknownException()
|
raise exceptions.channelUnknownException()
|
||||||
|
|
||||||
|
# Make sure a game client is not trying to join a #multi_ or #spect_ channel manually
|
||||||
|
channelObject = glob.channels.channels[channel]
|
||||||
|
if channelObject.isSpecial and not token.irc and not force:
|
||||||
|
raise exceptions.channelUnknownException()
|
||||||
|
|
||||||
# Make sure the user is in the channel
|
# Make sure the user is in the channel
|
||||||
if channel not in token.joinedChannels:
|
if channel not in token.joinedChannels:
|
||||||
raise exceptions.userNotInChannelException()
|
raise exceptions.userNotInChannelException()
|
||||||
|
|
||||||
# Part channel (token-side and channel-side)
|
# Part channel (token-side and channel-side)
|
||||||
channelObject = glob.channels.channels[channel]
|
|
||||||
token.partChannel(channelObject)
|
token.partChannel(channelObject)
|
||||||
|
|
||||||
# Delete temporary channel if everyone left
|
# Delete temporary channel if everyone left
|
||||||
if "chat/{}".format(channelObject.name) in glob.streams.streams:
|
if "chat/{}".format(channelObject.name) in glob.streams.streams:
|
||||||
if channelObject.temp == True and len(glob.streams.streams["chat/{}".format(channelObject.name)].clients) - 1 == 0:
|
if channelObject.temp and len(glob.streams.streams["chat/{}".format(channelObject.name)].clients) - 1 == 0:
|
||||||
glob.channels.removeChannel(channelObject.name)
|
glob.channels.removeChannel(channelObject.name)
|
||||||
|
|
||||||
# Force close tab if needed
|
# Force close tab if needed
|
||||||
|
@ -122,7 +133,7 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
|
||||||
token.enqueue(serverPackets.channelKicked(channelClient))
|
token.enqueue(serverPackets.channelKicked(channelClient))
|
||||||
|
|
||||||
# IRC part
|
# IRC part
|
||||||
if glob.irc == True and toIRC == True:
|
if glob.irc and toIRC:
|
||||||
glob.ircServer.banchoPartChannel(token.username, channel)
|
glob.ircServer.banchoPartChannel(token.username, channel)
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
|
@ -217,11 +228,17 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
||||||
raise exceptions.channelUnknownException()
|
raise exceptions.channelUnknownException()
|
||||||
|
|
||||||
# Make sure the channel is not in moderated mode
|
# Make sure the channel is not in moderated mode
|
||||||
if glob.channels.channels[to].moderated == True and token.admin == False:
|
if glob.channels.channels[to].moderated and not token.admin:
|
||||||
raise exceptions.channelModeratedException()
|
raise exceptions.channelModeratedException()
|
||||||
|
|
||||||
|
# Make sure we are in the channel
|
||||||
|
if to not in token.joinedChannels:
|
||||||
|
# I'm too lazy to put and test the correct IRC error code here...
|
||||||
|
# but IRC is not strict at all so who cares
|
||||||
|
raise exceptions.channelNoPermissionsException()
|
||||||
|
|
||||||
# Make sure we have write permissions
|
# Make sure we have write permissions
|
||||||
if glob.channels.channels[to].publicWrite == False and token.admin == False:
|
if not glob.channels.channels[to].publicWrite and not token.admin:
|
||||||
raise exceptions.channelNoPermissionsException()
|
raise exceptions.channelNoPermissionsException()
|
||||||
|
|
||||||
# Add message in buffer
|
# Add message in buffer
|
||||||
|
@ -241,7 +258,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
||||||
# raise exceptions.userTournamentException()
|
# raise exceptions.userTournamentException()
|
||||||
|
|
||||||
# Make sure the recipient is not restricted or we are FokaBot
|
# Make sure the recipient is not restricted or we are FokaBot
|
||||||
if recipientToken.restricted == True and fro.lower() != glob.BOT_NAME.lower():
|
if recipientToken.restricted and fro.lower() != glob.BOT_NAME.lower():
|
||||||
raise exceptions.userRestrictedException()
|
raise exceptions.userRestrictedException()
|
||||||
|
|
||||||
# TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend
|
# TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend
|
||||||
|
@ -251,14 +268,14 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
||||||
sendMessage(to, fro, "\x01ACTION is away: {}\x01".format(recipientToken.awayMessage))
|
sendMessage(to, fro, "\x01ACTION is away: {}\x01".format(recipientToken.awayMessage))
|
||||||
|
|
||||||
# Check message templates (mods/admins only)
|
# Check message templates (mods/admins only)
|
||||||
if message in messageTemplates.templates and token.admin == True:
|
if message in messageTemplates.templates and token.admin:
|
||||||
sendMessage(fro, to, messageTemplates.templates[message])
|
sendMessage(fro, to, messageTemplates.templates[message])
|
||||||
|
|
||||||
# Everything seems fine, send packet
|
# Everything seems fine, send packet
|
||||||
recipientToken.enqueue(packet)
|
recipientToken.enqueue(packet)
|
||||||
|
|
||||||
# Send the message to IRC
|
# Send the message to IRC
|
||||||
if glob.irc == True and toIRC == True:
|
if glob.irc and toIRC:
|
||||||
messageSplitInLines = message.encode("latin-1").decode("utf-8").split("\n")
|
messageSplitInLines = message.encode("latin-1").decode("utf-8").split("\n")
|
||||||
for line in messageSplitInLines:
|
for line in messageSplitInLines:
|
||||||
if line == messageSplitInLines[:1] and line == "":
|
if line == messageSplitInLines[:1] and line == "":
|
||||||
|
@ -270,7 +287,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
||||||
token.spamProtection()
|
token.spamProtection()
|
||||||
|
|
||||||
# Fokabot message
|
# Fokabot message
|
||||||
if isChannel == True or to.lower() == glob.BOT_NAME.lower():
|
if isChannel or to.lower() == glob.BOT_NAME.lower():
|
||||||
fokaMessage = fokabot.fokabotResponse(token.username, to, message)
|
fokaMessage = fokabot.fokabotResponse(token.username, to, message)
|
||||||
if fokaMessage:
|
if fokaMessage:
|
||||||
sendMessage(glob.BOT_NAME, to if isChannel else fro, fokaMessage)
|
sendMessage(glob.BOT_NAME, to if isChannel else fro, fokaMessage)
|
||||||
|
|
|
@ -56,8 +56,8 @@ class config:
|
||||||
self.config.get("debug","time")
|
self.config.get("debug","time")
|
||||||
|
|
||||||
self.config.get("sentry","enable")
|
self.config.get("sentry","enable")
|
||||||
self.config.get("sentry","banchodns")
|
self.config.get("sentry","banchodsn")
|
||||||
self.config.get("sentry","ircdns")
|
self.config.get("sentry","ircdsn")
|
||||||
|
|
||||||
self.config.get("discord","enable")
|
self.config.get("discord","enable")
|
||||||
self.config.get("discord","boturl")
|
self.config.get("discord","boturl")
|
||||||
|
@ -118,8 +118,8 @@ class config:
|
||||||
|
|
||||||
self.config.add_section("sentry")
|
self.config.add_section("sentry")
|
||||||
self.config.set("sentry", "enable", "0")
|
self.config.set("sentry", "enable", "0")
|
||||||
self.config.set("sentry", "banchodns", "")
|
self.config.set("sentry", "banchodsn", "")
|
||||||
self.config.set("sentry", "ircdns", "")
|
self.config.set("sentry", "ircdsn", "")
|
||||||
|
|
||||||
self.config.add_section("discord")
|
self.config.add_section("discord")
|
||||||
self.config.set("discord", "enable", "0")
|
self.config.set("discord", "enable", "0")
|
||||||
|
|
|
@ -648,7 +648,7 @@ class Server:
|
||||||
# Sentry
|
# Sentry
|
||||||
sentryClient = None
|
sentryClient = None
|
||||||
if glob.sentry:
|
if glob.sentry:
|
||||||
sentryClient = raven.Client(glob.conf.config["sentry"]["ircdns"])
|
sentryClient = raven.Client(glob.conf.config["sentry"]["ircdsn"])
|
||||||
|
|
||||||
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from common import generalUtils
|
from common import generalUtils
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
from objects import glob
|
from objects import glob
|
||||||
|
from common.log import logUtils as log
|
||||||
|
|
||||||
|
|
||||||
class banchoConfig:
|
class banchoConfig:
|
||||||
|
@ -30,7 +31,12 @@ class banchoConfig:
|
||||||
"""
|
"""
|
||||||
self.config["banchoMaintenance"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"])
|
self.config["banchoMaintenance"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"])
|
||||||
self.config["freeDirect"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'free_direct'")["value_int"])
|
self.config["freeDirect"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'free_direct'")["value_int"])
|
||||||
self.config["menuIcon"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'menu_icon'")["value_string"]
|
mainMenuIcon = glob.db.fetch("SELECT file_id, url FROM main_menu_icons WHERE is_current = 1 LIMIT 1")
|
||||||
|
if mainMenuIcon is None:
|
||||||
|
self.config["menuIcon"] = ""
|
||||||
|
else:
|
||||||
|
imageURL = "https://i.ppy.sh/{}.png".format(mainMenuIcon["file_id"])
|
||||||
|
self.config["menuIcon"] = "{}|{}".format(imageURL, mainMenuIcon["url"])
|
||||||
self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"]
|
self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,5 +63,5 @@ class banchoConfig:
|
||||||
glob.streams.broadcast("main", serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
|
glob.streams.broadcast("main", serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
|
||||||
glob.streams.broadcast("main", serverPackets.channelInfoEnd())
|
glob.streams.broadcast("main", serverPackets.channelInfoEnd())
|
||||||
for key, value in glob.channels.channels.items():
|
for key, value in glob.channels.channels.items():
|
||||||
if value.publicRead == True and value.hidden == False:
|
if value.publicRead and not value.hidden:
|
||||||
glob.streams.broadcast("main", serverPackets.channelInfo(key))
|
glob.streams.broadcast("main", serverPackets.channelInfo(key))
|
|
@ -1,3 +1,6 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from constants import exceptions
|
||||||
from objects import glob
|
from objects import glob
|
||||||
|
|
||||||
class channel:
|
class channel:
|
||||||
|
@ -20,14 +23,22 @@ class channel:
|
||||||
self.temp = temp
|
self.temp = temp
|
||||||
self.hidden = hidden
|
self.hidden = hidden
|
||||||
|
|
||||||
# Client name (#spectator/#multiplayer)
|
|
||||||
self.clientName = self.name
|
|
||||||
if self.name.startswith("#spect_"):
|
|
||||||
self.clientName = "#spectator"
|
|
||||||
elif self.name.startswith("#multi_"):
|
|
||||||
self.clientName = "#multiplayer"
|
|
||||||
|
|
||||||
# Make Foka join the channel
|
# Make Foka join the channel
|
||||||
fokaToken = glob.tokens.getTokenFromUserID(999)
|
fokaToken = glob.tokens.getTokenFromUserID(999)
|
||||||
if fokaToken is not None:
|
if fokaToken is not None:
|
||||||
fokaToken.joinChannel(self)
|
try:
|
||||||
|
fokaToken.joinChannel(self)
|
||||||
|
except exceptions.userAlreadyInChannelException:
|
||||||
|
logging.warning("FokaBot has already joined channel {}".format(self.name))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isSpecial(self):
|
||||||
|
return any(self.name.startswith(x) for x in ("#spect_", "#multi_"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clientName(self):
|
||||||
|
if self.name.startswith("#spect_"):
|
||||||
|
return "#spectator"
|
||||||
|
elif self.name.startswith("#multi_"):
|
||||||
|
return "#multiplayer"
|
||||||
|
return self.name
|
|
@ -66,6 +66,8 @@ class match:
|
||||||
self.isStarting = False
|
self.isStarting = False
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
self.createTime = int(time.time())
|
self.createTime = int(time.time())
|
||||||
|
self.vinseID = None
|
||||||
|
self.bloodcatAlert = False
|
||||||
|
|
||||||
# Create all slots and reset them
|
# Create all slots and reset them
|
||||||
self.slots = []
|
self.slots = []
|
||||||
|
@ -433,9 +435,26 @@ class match:
|
||||||
# Console output
|
# Console output
|
||||||
log.info("MPROOM{}: Match completed".format(self.matchID))
|
log.info("MPROOM{}: Match completed".format(self.matchID))
|
||||||
|
|
||||||
|
# Set vinse id if needed
|
||||||
|
chanName = "#multi_{}".format(self.matchID)
|
||||||
|
if self.vinseID is None:
|
||||||
|
self.vinseID = (int(time.time()) // (60 * 15)) << 32 | self.matchID
|
||||||
|
chat.sendMessage("FokaBot", chanName, "Match history available [{} here]".format(
|
||||||
|
"https://vinse.ripple.moe/match/{}".format(self.vinseID)
|
||||||
|
))
|
||||||
|
if not self.bloodcatAlert:
|
||||||
|
chat.sendMessage(
|
||||||
|
"FokaBot",
|
||||||
|
chanName,
|
||||||
|
"Oh by the way, in case you're playing unranked or broken maps "
|
||||||
|
"that are now available through ripple's osu!direct, you can "
|
||||||
|
"type '!bloodcat' in the chat to get a download link for the "
|
||||||
|
"currently selected map from Bloodcat!"
|
||||||
|
)
|
||||||
|
self.bloodcatAlert = True
|
||||||
|
|
||||||
# If this is a tournament match, then we send a notification in the chat
|
# If this is a tournament match, then we send a notification in the chat
|
||||||
# saying that the match has completed.
|
# saying that the match has completed.
|
||||||
chanName = "#multi_{}".format(self.matchID)
|
|
||||||
if self.isTourney and (chanName in glob.channels.channels):
|
if self.isTourney and (chanName in glob.channels.channels):
|
||||||
chat.sendMessage(glob.BOT_NAME, chanName, "Match has just finished.")
|
chat.sendMessage(glob.BOT_NAME, chanName, "Match has just finished.")
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from common.sentry import sentry
|
||||||
|
from constants.exceptions import periodicLoopException
|
||||||
from objects import match
|
from objects import match
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
|
@ -67,6 +69,7 @@ class matchList:
|
||||||
del self.matches[matchID]
|
del self.matches[matchID]
|
||||||
log.info("MPROOM{}: Room disposed manually".format(_match.matchID))
|
log.info("MPROOM{}: Room disposed manually".format(_match.matchID))
|
||||||
|
|
||||||
|
@sentry.capture()
|
||||||
def cleanupLoop(self):
|
def cleanupLoop(self):
|
||||||
"""
|
"""
|
||||||
Start match cleanup loop.
|
Start match cleanup loop.
|
||||||
|
@ -76,21 +79,34 @@ class matchList:
|
||||||
This method starts an infinite loop, call it only once!
|
This method starts an infinite loop, call it only once!
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
log.debug("Checking empty matches")
|
try:
|
||||||
t = int(time.time())
|
log.debug("Checking empty matches")
|
||||||
emptyMatches = []
|
t = int(time.time())
|
||||||
|
emptyMatches = []
|
||||||
|
exceptions = []
|
||||||
|
|
||||||
# Collect all empty matches
|
# Collect all empty matches
|
||||||
for key, m in self.matches.items():
|
for key, m in self.matches.items():
|
||||||
if [x for x in m.slots if x.user is not None]:
|
if [x for x in m.slots if x.user is not None]:
|
||||||
continue
|
continue
|
||||||
if t - m.createTime >= 120:
|
if t - m.createTime >= 120:
|
||||||
log.debug("Match #{} marked for cleanup".format(m.matchID))
|
log.debug("Match #{} marked for cleanup".format(m.matchID))
|
||||||
emptyMatches.append(m.matchID)
|
emptyMatches.append(m.matchID)
|
||||||
|
|
||||||
# Dispose all empty matches
|
# Dispose all empty matches
|
||||||
for matchID in emptyMatches:
|
for matchID in emptyMatches:
|
||||||
self.disposeMatch(matchID)
|
try:
|
||||||
|
self.disposeMatch(matchID)
|
||||||
|
except Exception as e:
|
||||||
|
exceptions.append(e)
|
||||||
|
log.error(
|
||||||
|
"Something wrong happened while disposing a timed out match. Reporting to Sentry when "
|
||||||
|
"the loop ends."
|
||||||
|
)
|
||||||
|
|
||||||
# Schedule a new check (endless loop)
|
# Re-raise exception if needed
|
||||||
threading.Timer(30, self.cleanupLoop).start()
|
if exceptions:
|
||||||
|
raise periodicLoopException(exceptions)
|
||||||
|
finally:
|
||||||
|
# Schedule a new check (endless loop)
|
||||||
|
threading.Timer(30, self.cleanupLoop).start()
|
||||||
|
|
|
@ -141,7 +141,7 @@ class token:
|
||||||
"""
|
"""
|
||||||
if channelObject.name in self.joinedChannels:
|
if channelObject.name in self.joinedChannels:
|
||||||
raise exceptions.userAlreadyInChannelException()
|
raise exceptions.userAlreadyInChannelException()
|
||||||
if channelObject.publicRead == False and self.admin == False:
|
if not channelObject.publicRead and not self.admin:
|
||||||
raise exceptions.channelNoPermissionsException()
|
raise exceptions.channelNoPermissionsException()
|
||||||
self.joinedChannels.append(channelObject.name)
|
self.joinedChannels.append(channelObject.name)
|
||||||
self.joinStream("chat/{}".format(channelObject.name))
|
self.joinStream("chat/{}".format(channelObject.name))
|
||||||
|
@ -212,10 +212,10 @@ class token:
|
||||||
|
|
||||||
# Create and join #spectator (#spect_userid) channel
|
# Create and join #spectator (#spect_userid) channel
|
||||||
glob.channels.addTempChannel("#spect_{}".format(host.userID))
|
glob.channels.addTempChannel("#spect_{}".format(host.userID))
|
||||||
chat.joinChannel(token=self, channel="#spect_{}".format(host.userID))
|
chat.joinChannel(token=self, channel="#spect_{}".format(host.userID), force=True)
|
||||||
if len(host.spectators) == 1:
|
if len(host.spectators) == 1:
|
||||||
# First spectator, send #spectator join to host too
|
# First spectator, send #spectator join to host too
|
||||||
chat.joinChannel(token=host, channel="#spect_{}".format(host.userID))
|
chat.joinChannel(token=host, channel="#spect_{}".format(host.userID), force=True)
|
||||||
|
|
||||||
# Send fellow spectator join to all clients
|
# Send fellow spectator join to all clients
|
||||||
glob.streams.broadcast(streamName, serverPackets.fellowSpectatorJoined(self.userID))
|
glob.streams.broadcast(streamName, serverPackets.fellowSpectatorJoined(self.userID))
|
||||||
|
@ -265,14 +265,14 @@ class token:
|
||||||
# If nobody is spectating the host anymore, close #spectator channel
|
# If nobody is spectating the host anymore, close #spectator channel
|
||||||
# and remove host from spect stream too
|
# and remove host from spect stream too
|
||||||
if len(hostToken.spectators) == 0:
|
if len(hostToken.spectators) == 0:
|
||||||
chat.partChannel(token=hostToken, channel="#spect_{}".format(hostToken.userID), kick=True)
|
chat.partChannel(token=hostToken, channel="#spect_{}".format(hostToken.userID), kick=True, force=True)
|
||||||
hostToken.leaveStream(streamName)
|
hostToken.leaveStream(streamName)
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
log.info("{} is no longer spectating {}. Current spectators: {}".format(self.username, self.spectatingUserID, hostToken.spectators))
|
log.info("{} is no longer spectating {}. Current spectators: {}".format(self.username, self.spectatingUserID, hostToken.spectators))
|
||||||
|
|
||||||
# Part #spectator channel
|
# Part #spectator channel
|
||||||
chat.partChannel(token=self, channel="#spect_{}".format(self.spectatingUserID), kick=True)
|
chat.partChannel(token=self, channel="#spect_{}".format(self.spectatingUserID), kick=True, force=True)
|
||||||
|
|
||||||
# Set our spectating user to 0
|
# Set our spectating user to 0
|
||||||
self.spectating = None
|
self.spectating = None
|
||||||
|
@ -318,7 +318,7 @@ class token:
|
||||||
# Set matchID, join stream, channel and send packet
|
# Set matchID, join stream, channel and send packet
|
||||||
self.matchID = matchID
|
self.matchID = matchID
|
||||||
self.joinStream(match.streamName)
|
self.joinStream(match.streamName)
|
||||||
chat.joinChannel(token=self, channel="#multi_{}".format(self.matchID))
|
chat.joinChannel(token=self, channel="#multi_{}".format(self.matchID), force=True)
|
||||||
self.enqueue(serverPackets.matchJoinSuccess(matchID))
|
self.enqueue(serverPackets.matchJoinSuccess(matchID))
|
||||||
|
|
||||||
if match.isTourney:
|
if match.isTourney:
|
||||||
|
@ -339,7 +339,7 @@ class token:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Part #multiplayer channel and streams (/ and /playing)
|
# Part #multiplayer channel and streams (/ and /playing)
|
||||||
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True)
|
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True, force=True)
|
||||||
self.leaveStream("multi/{}".format(self.matchID))
|
self.leaveStream("multi/{}".format(self.matchID))
|
||||||
self.leaveStream("multi/{}/playing".format(self.matchID)) # optional
|
self.leaveStream("multi/{}/playing".format(self.matchID)) # optional
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,14 @@ import redis
|
||||||
|
|
||||||
from common.ripple import userUtils
|
from common.ripple import userUtils
|
||||||
from common.log import logUtils as log
|
from common.log import logUtils as log
|
||||||
|
from common.sentry import sentry
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
|
from constants.exceptions import periodicLoopException
|
||||||
from events import logoutEvent
|
from events import logoutEvent
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from objects import osuToken
|
from objects import osuToken
|
||||||
|
|
||||||
|
|
||||||
class tokenList:
|
class tokenList:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tokens = {}
|
self.tokens = {}
|
||||||
|
@ -171,6 +174,7 @@ class tokenList:
|
||||||
for _, value in self.tokens.items():
|
for _, value in self.tokens.items():
|
||||||
value.enqueue(packet)
|
value.enqueue(packet)
|
||||||
|
|
||||||
|
@sentry.capture()
|
||||||
def usersTimeoutCheckLoop(self):
|
def usersTimeoutCheckLoop(self):
|
||||||
"""
|
"""
|
||||||
Start timed out users disconnect loop.
|
Start timed out users disconnect loop.
|
||||||
|
@ -178,27 +182,41 @@ class tokenList:
|
||||||
CALL THIS FUNCTION ONLY ONCE!
|
CALL THIS FUNCTION ONLY ONCE!
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
log.debug("Checking timed out clients")
|
try:
|
||||||
timedOutTokens = [] # timed out users
|
log.debug("Checking timed out clients")
|
||||||
timeoutLimit = int(time.time()) - 100
|
exceptions = []
|
||||||
for key, value in self.tokens.items():
|
timedOutTokens = [] # timed out users
|
||||||
# Check timeout (fokabot is ignored)
|
timeoutLimit = int(time.time()) - 100
|
||||||
if value.pingTime < timeoutLimit and value.userID != 999 and value.irc == False and value.tournament == False:
|
for key, value in self.tokens.items():
|
||||||
# That user has timed out, add to disconnected tokens
|
# Check timeout (fokabot is ignored)
|
||||||
# We can't delete it while iterating or items() throws an error
|
if value.pingTime < timeoutLimit and value.userID != 999 and not value.irc and not value.tournament:
|
||||||
timedOutTokens.append(key)
|
# That user has timed out, add to disconnected tokens
|
||||||
|
# We can't delete it while iterating or items() throws an error
|
||||||
|
timedOutTokens.append(key)
|
||||||
|
|
||||||
# Delete timed out users from self.tokens
|
# Delete timed out users from self.tokens
|
||||||
# i is token string (dictionary key)
|
# i is token string (dictionary key)
|
||||||
for i in timedOutTokens:
|
for i in timedOutTokens:
|
||||||
log.debug("{} timed out!!".format(self.tokens[i].username))
|
log.debug("{} timed out!!".format(self.tokens[i].username))
|
||||||
self.tokens[i].enqueue(serverPackets.notification("Your connection to the server timed out."))
|
self.tokens[i].enqueue(serverPackets.notification("Your connection to the server timed out."))
|
||||||
logoutEvent.handle(self.tokens[i], None)
|
try:
|
||||||
del timedOutTokens
|
logoutEvent.handle(self.tokens[i], None)
|
||||||
|
except Exception as e:
|
||||||
|
exceptions.append(e)
|
||||||
|
log.error(
|
||||||
|
"Something wrong happened while disconnecting a timed out client. Reporting to Sentry "
|
||||||
|
"when the loop ends."
|
||||||
|
)
|
||||||
|
del timedOutTokens
|
||||||
|
|
||||||
# Schedule a new check (endless loop)
|
# Re-raise exceptions if needed
|
||||||
threading.Timer(100, self.usersTimeoutCheckLoop).start()
|
if exceptions:
|
||||||
|
raise periodicLoopException(exceptions)
|
||||||
|
finally:
|
||||||
|
# Schedule a new check (endless loop)
|
||||||
|
threading.Timer(100, self.usersTimeoutCheckLoop).start()
|
||||||
|
|
||||||
|
@sentry.capture()
|
||||||
def spamProtectionResetLoop(self):
|
def spamProtectionResetLoop(self):
|
||||||
"""
|
"""
|
||||||
Start spam protection reset loop.
|
Start spam protection reset loop.
|
||||||
|
@ -207,12 +225,13 @@ class tokenList:
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
# Reset spamRate for every token
|
try:
|
||||||
for _, value in self.tokens.items():
|
# Reset spamRate for every token
|
||||||
value.spamRate = 0
|
for _, value in self.tokens.items():
|
||||||
|
value.spamRate = 0
|
||||||
# Schedule a new check (endless loop)
|
finally:
|
||||||
threading.Timer(10, self.spamProtectionResetLoop).start()
|
# Schedule a new check (endless loop)
|
||||||
|
threading.Timer(10, self.spamProtectionResetLoop).start()
|
||||||
|
|
||||||
def deleteBanchoSessions(self):
|
def deleteBanchoSessions(self):
|
||||||
"""
|
"""
|
||||||
|
|
11
pep.py
11
pep.py
|
@ -10,7 +10,7 @@ import tornado.web
|
||||||
from raven.contrib.tornado import AsyncSentryClient
|
from raven.contrib.tornado import AsyncSentryClient
|
||||||
import redis
|
import redis
|
||||||
|
|
||||||
from common import generalUtils
|
from common import generalUtils, agpl
|
||||||
from common.constants import bcolors
|
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
|
||||||
|
@ -41,6 +41,7 @@ from pubSubHandlers import notificationHandler
|
||||||
from pubSubHandlers import updateSilenceHandler
|
from pubSubHandlers import updateSilenceHandler
|
||||||
from pubSubHandlers import updateStatsHandler
|
from pubSubHandlers import updateStatsHandler
|
||||||
|
|
||||||
|
|
||||||
def make_app():
|
def make_app():
|
||||||
return tornado.web.Application([
|
return tornado.web.Application([
|
||||||
(r"/", mainHandler.handler),
|
(r"/", mainHandler.handler),
|
||||||
|
@ -53,7 +54,15 @@ def make_app():
|
||||||
(r"/stress", heavyHandler.handler)
|
(r"/stress", heavyHandler.handler)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# AGPL license agreement
|
||||||
|
try:
|
||||||
|
agpl.check_license("ripple", "pep.py")
|
||||||
|
except agpl.LicenseError as e:
|
||||||
|
print(str(e))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Server start
|
# Server start
|
||||||
consoleHelper.printServerStartHeader(True)
|
consoleHelper.printServerStartHeader(True)
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -7,7 +7,7 @@ import os
|
||||||
cythonExt = []
|
cythonExt = []
|
||||||
for root, dirs, files in os.walk(os.getcwd()):
|
for root, dirs, files in os.walk(os.getcwd()):
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.endswith(".pyx"):
|
if file.endswith(".pyx") and ".pyenv" not in root: # im sorry
|
||||||
filePath = os.path.relpath(os.path.join(root, file))
|
filePath = os.path.relpath(os.path.join(root, file))
|
||||||
cythonExt.append(Extension(filePath.replace("/", ".")[:-4], [filePath]))
|
cythonExt.append(Extension(filePath.replace("/", ".")[:-4], [filePath]))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user