Compare commits

..

1 Commits

Author SHA1 Message Date
Sunpy 4291a69d54 Added raw packet builder command 2018-04-09 07:56:13 +02:00
26 changed files with 160 additions and 345 deletions

2
common

@ -1 +1 @@
Subproject commit 6103fe96a79cd8f5cbabe24b5fac9cf2a5cacb4a
Subproject commit 94c7474e395be2b1ef3ff26e61fc9470598eba69

18
config.json Normal file
View File

@ -0,0 +1,18 @@
{
"support-email": "support@ripple.moe",
"faq":
{
"rules": "Please make sure to check (Ripple's rules)[http://ripple.moe/?p=23].",
"swearing": "Please don't abuse swearing",
"spam": "Please don't spam",
"offend": "Please don't offend other players",
"github": "(Ripple's Github page!)[https://github.com/osuripple/ripple]",
"discord": "(Join Ripple's Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]",
"blog": "You can find the latest Ripple news on the (blog)[https://ripple.moe/blog/]!",
"changelog": "Check the (changelog)[https://ripple.moe/index.php?p=17] !",
"status": "Check the server status (here!)[https://ripple.moe/index.php?p=27]",
"english": "Please keep this channel in english.",
"topic": "Can you please drop the topic and talk about something else?",
"lines": "Please try to keep your sentences on a single line to avoid getting silenced."
}
}

View File

@ -104,7 +104,4 @@ class invalidUserException(Exception):
pass
class wrongChannelException(Exception):
pass
class periodicLoopException(Exception):
pass

View File

@ -10,7 +10,7 @@ from common import generalUtils
from common.constants import mods
from common.log import logUtils as log
from common.ripple import userUtils
from constants import exceptions, slotStatuses, matchModModes, matchTeams, matchTeamTypes, matchScoringTypes
from constants import exceptions, slotStatuses, matchModModes, matchTeams, matchTeamTypes
from common.constants import gameModes
from common.constants import privileges
from constants import serverPackets
@ -18,18 +18,9 @@ from helpers import systemHelper
from objects import fokabot
from objects import glob
from helpers import chatHelper as chat
from helpers import packetHelper
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
@ -51,14 +42,14 @@ def instantRestart(fro, chan, message):
def faq(fro, chan, message):
key = message[0].lower()
if key not in glob.conf.extra["pep.py"]["faq"]:
if key not in glob.conf.extra["faq"]:
return False
return glob.conf.extra["pep.py"]["faq"][key]
return glob.conf.extra["faq"][key]
def roll(fro, chan, message):
maxPoints = 100
if len(message) >= 1:
if message[0].isdigit() and int(message[0]) > 0:
if message[0].isdigit() == True and int(message[0]) > 0:
maxPoints = int(message[0])
points = random.randrange(0,maxPoints)
@ -443,14 +434,6 @@ def getPPMessage(userID, just_data = False):
def tillerinoNp(fro, chan, message):
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
if chan.startswith("#"):
return False
@ -764,27 +747,18 @@ def report(fro, chan, message):
token.enqueue(serverPackets.notification(msg))
return False
def getMatchIDFromChannel(chan):
if not chan.lower().startswith("#multi_"):
raise exceptions.wrongChannelException()
parts = chan.lower().split("_")
if len(parts) < 2 or not parts[1].isdigit():
raise exceptions.wrongChannelException()
matchID = int(parts[1])
if matchID not in glob.matches.matches:
raise exceptions.matchNotFoundException()
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 getMatchIDFromChannel(chan):
if not chan.lower().startswith("#multi_"):
raise exceptions.wrongChannelException()
parts = chan.lower().split("_")
if len(parts) < 2 or not parts[1].isdigit():
raise exceptions.wrongChannelException()
matchID = int(parts[1])
if matchID not in glob.matches.matches:
raise exceptions.matchNotFoundException()
return matchID
def mpMake():
if len(message) < 2:
raise exceptions.invalidArgumentsException("Wrong syntax: !mp make <name>")
@ -800,12 +774,6 @@ def multiplayer(fro, chan, message):
raise exceptions.invalidArgumentsException("Wrong syntax: !mp join <id>")
matchID = int(message[1])
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)
return "Attempting to join match #{}!".format(matchID)
@ -1030,8 +998,6 @@ def multiplayer(fro, chan, message):
newMods |= mods.FLASHLIGHT
elif _mod.lower().strip() == "fi":
newMods |= mods.FADEIN
elif _mod.lower().strip() == "ez":
newMods |= mods.EASY
if _mod.lower().strip() == "none":
newMods = 0
@ -1063,13 +1029,7 @@ def multiplayer(fro, chan, message):
def mpSettings():
_match = glob.matches.matches[getMatchIDFromChannel(chan)]
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 += ": "
msg = "PLAYERS IN THIS MATCH:\n"
empty = True
for slot in _match.slots:
if slot.user is None:
@ -1085,28 +1045,16 @@ def multiplayer(fro, chan, message):
else:
readableStatus = readableStatuses[slot.status]
empty = False
msg += "* [{team}] <{status}> ~ {username}{mods}{nl}".format(
msg += "* [{team}] <{status}> ~ {username}{mods}\n".format(
team="red" if slot.team == matchTeams.RED else "blue" if slot.team == matchTeams.BLUE else "!! no team !!",
status=readableStatus,
username=glob.tokens.tokens[slot.user].username,
mods=" (+ {})".format(generalUtils.readableMods(slot.mods)) if slot.mods > 0 else "",
nl=" | " if single else "\n"
mods=" (+ {})".format(generalUtils.readableMods(slot.mods)) if slot.mods > 0 else ""
)
if empty:
msg += "Nobody.\n"
msg = msg.rstrip(" | " if single else "\n")
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:
subcommands = {
@ -1130,8 +1078,6 @@ def multiplayer(fro, chan, message):
"mods": mpMods,
"team": mpTeam,
"settings": mpSettings,
"scorev": mpScoreV,
"help": mpHelp
}
requestedSubcommand = message[0].lower().strip()
if requestedSubcommand not in subcommands:
@ -1179,26 +1125,26 @@ def rtx(fro, chan, message):
userToken.enqueue(serverPackets.rtx(message))
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
def rawPacket(fro, chan, message):
target = message[0]
message = " ".join(message[1:]).strip()
if not message:
return "Invalid message"
targetUserID = userUtils.getIDSafe(target)
if not targetUserID:
return "{}: user not found".format(target)
userToken = glob.tokens.getTokenFromUserID(targetUserID, ignoreIRC=True, _all=False)
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)
p = message.split(" ", 1)
try:
packetID = int(p[0])
packetData = eval(p[1])
except Exception:
return "Error"
userToken.enqueue(packetHelper.buildPacket(packetID, packetData))
return ":thinking:"
"""
@ -1351,8 +1297,10 @@ commands = [
"syntax": "<username> <message>",
"callback": rtx
}, {
"trigger": "!bloodcat",
"callback": bloodcat
"trigger": "!raw",
"privileges": privileges.ADMIN_MANAGE_SERVERS,
"syntax": "<username> <byte> <array>",
"callback": rawPacket
}
#
# "trigger": "!acc",

View File

@ -1,4 +1,3 @@
SCORE = 0
ACCURACY = 1
COMBO = 2
SCORE_V2 = 3

View File

@ -16,12 +16,12 @@ def forceUpdate():
def loginBanned():
packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
packets += notification("You are banned. You can appeal after one month since your ban by sending an email to {} from the email address you've used to sign up.".format(glob.conf.extra["pep.py"]["support-email"]))
packets += notification("You are banned. You can appeal after one month since your ban by sending an email to {} from the email address you've used to sign up.".format(glob.conf.extra["support-email"]))
return packets
def loginLocked():
packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
packets += notification("Your account is locked. You can't log in, but your profile and scores are still visible from the website. If you want to unlock your account, send an email to {} from the email address you've used to sign up.".format(glob.conf.extra["pep.py"]["support-email"]))
packets += notification("Your account is locked. You can't log in, but your profile and scores are still visible from the website. If you want to unlock your account, send an email to {} from the email address you've used to sign up.".format(glob.conf.extra["support-email"]))
return packets
def loginError():

View File

@ -18,7 +18,7 @@ def handle(userToken, packetData):
raise exceptions.matchCreateError()
# Create a match object
# TODO: Player number check (Dirty hack below)
# TODO: Player number check
matchID = glob.matches.createMatch(matchName, packetData["matchPassword"].strip(), packetData["beatmapID"], packetData["beatmapName"], packetData["beatmapMD5"], packetData["gameMode"], userID)
# Make sure the match has been created
@ -29,11 +29,6 @@ def handle(userToken, packetData):
# Join that match
userToken.joinMatch(matchID)
# Disable slots (Dirty)
for i in range(0,16):
if match.slots[i].status is not 4:
match.slots[i].status = packetData["slot{}Status".format(i)]
# Give host to match creator
match.setHost(userID)
match.sendUpdates()

View File

@ -61,9 +61,9 @@ def handle(tornadoRequest):
# Make sure we are not banned or locked
priv = userUtils.getPrivileges(userID)
if userUtils.isBanned(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
if userUtils.isBanned(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
raise exceptions.loginBannedException()
if userUtils.isLocked(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
if userUtils.isLocked(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
raise exceptions.loginLockedException()
# 2FA check
@ -75,7 +75,7 @@ def handle(tornadoRequest):
# Verify this user (if pending activation)
firstLogin = False
if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(userID):
if priv & privileges.USER_PENDING_VERIFICATION > 0 or userUtils.hasVerifiedHardware(userID) == False:
if userUtils.verifyUser(userID, clientData):
# Valid account
log.info("Account {} verified successfully!".format(userID))
@ -118,9 +118,6 @@ def handle(tornadoRequest):
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)))
# 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
responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)
@ -176,7 +173,7 @@ def handle(tornadoRequest):
# Output channels info
for key, value in glob.channels.channels.items():
if value.publicRead and not value.hidden:
if value.publicRead == True and value.hidden == False:
responseToken.enqueue(serverPackets.channelInfo(key))
# Send friends list

View File

@ -8,4 +8,4 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches or not userToken.tournament:
return
userToken.matchID = matchID
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID), force=True)
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID))

View File

@ -7,5 +7,5 @@ def handle(userToken, packetData):
matchID = packetData["matchID"]
if matchID not in glob.matches.matches or not userToken.tournament:
return
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID), force=True)
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID))
userToken.matchID = 0

View File

@ -17,7 +17,7 @@ class handler(requestsManager.asyncRequestHandler):
data = {"message": "unknown error"}
try:
# Get online users count
data["result"] = -1 if glob.restarting else 1
data["result"] = -1 if glob.restarting == True else 1
# Status code and message
statusCode = 200

View File

@ -106,7 +106,7 @@ class handler(requestsManager.asyncRequestHandler):
packetData = requestData[pos:(pos+dataLength+7)]
# Console output if needed
if glob.outputPackets and packetID != 4:
if glob.outputPackets == True 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)))
# Event handler
@ -179,7 +179,7 @@ class handler(requestsManager.asyncRequestHandler):
# Process/ignore packet
if packetID != 4:
if packetID in eventHandler:
if not userToken.restricted or (userToken.restricted and packetID in packetsRestricted):
if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted):
eventHandler[packetID]()
else:
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
@ -243,7 +243,7 @@ class handler(requestsManager.asyncRequestHandler):
@tornado.web.asynchronous
@tornado.gen.engine
def asyncGet(self):
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%;background:#222;color:#fff;}</style></head><body><pre>"
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%}</style></head><body><pre>"
html += " _ __<br>"
html += " (_) / /<br>"
html += " ______ __ ____ ____ / /____<br>"
@ -262,5 +262,5 @@ class handler(requestsManager.asyncRequestHandler):
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
html += " \\ . .. .. . /<br>"
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br>Running osufx branch.<br><i>&copy; Ripple team, 2016</i></pre></body></html>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br><i>&copy; Ripple team, 2016</i></pre></body></html>"
self.write(html)

View File

@ -8,7 +8,7 @@ from objects import fokabot
from objects import glob
def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=False):
def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
"""
Join a channel
@ -16,7 +16,6 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=Fals
:param token: user token object of user that joins the channel. Optional. userID can be used instead.
: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 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
"""
try:
@ -34,16 +33,11 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=Fals
if channel not in glob.channels.channels:
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
token.joinChannel(channelObject)
token.joinChannel(glob.channels.channels[channel])
# Send channel joined (IRC)
if glob.irc and not toIRC:
if glob.irc == True and toIRC == True:
glob.ircServer.banchoJoinChannel(token.username, channel)
# Console output
@ -64,7 +58,7 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=Fals
log.warning("User not connected to IRC/Bancho")
return 403 # idk
def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False, force=False):
def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False):
"""
Part a channel
@ -73,7 +67,6 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
: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 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
"""
try:
@ -110,21 +103,17 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
if channel not in glob.channels.channels:
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
if channel not in token.joinedChannels:
raise exceptions.userNotInChannelException()
# Part channel (token-side and channel-side)
channelObject = glob.channels.channels[channel]
token.partChannel(channelObject)
# Delete temporary channel if everyone left
if "chat/{}".format(channelObject.name) in glob.streams.streams:
if channelObject.temp and len(glob.streams.streams["chat/{}".format(channelObject.name)].clients) - 1 == 0:
if channelObject.temp == True and len(glob.streams.streams["chat/{}".format(channelObject.name)].clients) - 1 == 0:
glob.channels.removeChannel(channelObject.name)
# Force close tab if needed
@ -133,7 +122,7 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
token.enqueue(serverPackets.channelKicked(channelClient))
# IRC part
if glob.irc and toIRC:
if glob.irc == True and toIRC == True:
glob.ircServer.banchoPartChannel(token.username, channel)
# Console output
@ -228,17 +217,11 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
raise exceptions.channelUnknownException()
# Make sure the channel is not in moderated mode
if glob.channels.channels[to].moderated and not token.admin:
if glob.channels.channels[to].moderated == True and token.admin == False:
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
if not glob.channels.channels[to].publicWrite and not token.admin:
if glob.channels.channels[to].publicWrite == False and token.admin == False:
raise exceptions.channelNoPermissionsException()
# Add message in buffer
@ -258,7 +241,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
# raise exceptions.userTournamentException()
# Make sure the recipient is not restricted or we are FokaBot
if recipientToken.restricted and fro.lower() != glob.BOT_NAME.lower():
if recipientToken.restricted == True and fro.lower() != glob.BOT_NAME.lower():
raise exceptions.userRestrictedException()
# TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend
@ -268,14 +251,14 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
sendMessage(to, fro, "\x01ACTION is away: {}\x01".format(recipientToken.awayMessage))
# Check message templates (mods/admins only)
if message in messageTemplates.templates and token.admin:
if message in messageTemplates.templates and token.admin == True:
sendMessage(fro, to, messageTemplates.templates[message])
# Everything seems fine, send packet
recipientToken.enqueue(packet)
# Send the message to IRC
if glob.irc and toIRC:
if glob.irc == True and toIRC == True:
messageSplitInLines = message.encode("latin-1").decode("utf-8").split("\n")
for line in messageSplitInLines:
if line == messageSplitInLines[:1] and line == "":
@ -287,7 +270,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
token.spamProtection()
# Fokabot message
if isChannel or to.lower() == glob.BOT_NAME.lower():
if isChannel == True or to.lower() == glob.BOT_NAME.lower():
fokaMessage = fokabot.fokabotResponse(token.username, to, message)
if fokaMessage:
sendMessage(glob.BOT_NAME, to if isChannel else fro, fokaMessage)

View File

@ -56,8 +56,8 @@ class config:
self.config.get("debug","time")
self.config.get("sentry","enable")
self.config.get("sentry","banchodsn")
self.config.get("sentry","ircdsn")
self.config.get("sentry","banchodns")
self.config.get("sentry","ircdns")
self.config.get("discord","enable")
self.config.get("discord","boturl")
@ -73,8 +73,6 @@ class config:
self.config.get("localize","enable")
self.config.get("localize","ipapiurl")
self.config.get("custom", "config")
return True
except configparser.Error:
return False
@ -120,8 +118,8 @@ class config:
self.config.add_section("sentry")
self.config.set("sentry", "enable", "0")
self.config.set("sentry", "banchodsn", "")
self.config.set("sentry", "ircdsn", "")
self.config.set("sentry", "banchodns", "")
self.config.set("sentry", "ircdns", "")
self.config.add_section("discord")
self.config.set("discord", "enable", "0")
@ -142,9 +140,6 @@ class config:
self.config.set("localize", "enable", "1")
self.config.set("localize", "ipapiurl", "http://ip.zxq.co")
self.config.add_section("custom")
self.config.set("custom", "config", "common/config.json")
# Write ini to file and close
self.config.write(f)
f.close()

View File

@ -27,11 +27,8 @@ def printServerStartHeader(asciiArt=True):
print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^{}".format(bcolors.ENDC))
printColored("> Welcome to pep.py osu!bancho server v{}".format(glob.VERSION), bcolors.GREEN)
printColored("> Common submodule v{}".format(glob.COMMON_VERSION), bcolors.GREEN)
printColored("> Made by the Ripple team", bcolors.GREEN)
printColored("> {}https://zxq.co/ripple/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN)
printColored("> Custom branch by the osufx team (just Sunpy)", bcolors.GREEN)
printColored("> {}https://github.com/osufx/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN)
printColored("> Press CTRL+C to exit\n", bcolors.GREEN)
def printNoNl(string):

View File

@ -648,7 +648,7 @@ class Server:
# Sentry
sentryClient = None
if glob.sentry:
sentryClient = raven.Client(glob.conf.config["sentry"]["ircdsn"])
sentryClient = raven.Client(glob.conf.config["sentry"]["ircdns"])
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

View File

@ -2,7 +2,6 @@
from common import generalUtils
from constants import serverPackets
from objects import glob
from common.log import logUtils as log
class banchoConfig:
@ -31,12 +30,7 @@ class banchoConfig:
"""
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"])
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["menuIcon"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'menu_icon'")["value_string"]
self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"]
@ -63,5 +57,5 @@ class banchoConfig:
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 and not value.hidden:
if value.publicRead == True and value.hidden == False:
glob.streams.broadcast("main", serverPackets.channelInfo(key))

View File

@ -1,6 +1,3 @@
import logging
from constants import exceptions
from objects import glob
class channel:
@ -23,22 +20,14 @@ class channel:
self.temp = temp
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
fokaToken = glob.tokens.getTokenFromUserID(999)
if fokaToken is not None:
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
fokaToken.joinChannel(self)

View File

@ -49,11 +49,3 @@ restarting = False
startTime = int(time.time())
streams = streamList.streamList()
# Additional modifications
COMMON_VERSION_REQ = "1.2.1"
try:
with open("common/version") as f:
COMMON_VERSION = f.read().strip()
except:
COMMON_VERSION = "Unknown"

View File

@ -66,8 +66,6 @@ class match:
self.isStarting = False
self._lock = threading.Lock()
self.createTime = int(time.time())
self.vinseID = None
self.bloodcatAlert = False
# Create all slots and reset them
self.slots = []
@ -253,7 +251,7 @@ class match:
else:
newStatus = slotStatuses.LOCKED
# Send updated settings to kicked user, so they will return to the lobby.
# Send updated settings to kicked user, so he returns to lobby
if self.slots[slotID].user is not None and self.slots[slotID].user in glob.tokens.tokens:
glob.tokens.tokens[self.slots[slotID].user].enqueue(serverPackets.updateMatch(self.matchID))
@ -435,26 +433,9 @@ class match:
# Console output
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
# saying that the match has completed.
chanName = "#multi_{}".format(self.matchID)
if self.isTourney and (chanName in glob.channels.channels):
chat.sendMessage(glob.BOT_NAME, chanName, "Match has just finished.")

View File

@ -1,8 +1,6 @@
import threading
import time
from common.sentry import sentry
from constants.exceptions import periodicLoopException
from objects import match
from objects import glob
from constants import serverPackets
@ -69,7 +67,6 @@ class matchList:
del self.matches[matchID]
log.info("MPROOM{}: Room disposed manually".format(_match.matchID))
@sentry.capture()
def cleanupLoop(self):
"""
Start match cleanup loop.
@ -79,34 +76,21 @@ class matchList:
This method starts an infinite loop, call it only once!
:return:
"""
try:
log.debug("Checking empty matches")
t = int(time.time())
emptyMatches = []
exceptions = []
log.debug("Checking empty matches")
t = int(time.time())
emptyMatches = []
# Collect all empty matches
for key, m in self.matches.items():
if [x for x in m.slots if x.user is not None]:
continue
if t - m.createTime >= 120:
log.debug("Match #{} marked for cleanup".format(m.matchID))
emptyMatches.append(m.matchID)
# Collect all empty matches
for key, m in self.matches.items():
if [x for x in m.slots if x.user is not None]:
continue
if t - m.createTime >= 120:
log.debug("Match #{} marked for cleanup".format(m.matchID))
emptyMatches.append(m.matchID)
# Dispose all empty matches
for matchID in emptyMatches:
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."
)
# Dispose all empty matches
for matchID in emptyMatches:
self.disposeMatch(matchID)
# Re-raise exception if needed
if exceptions:
raise periodicLoopException(exceptions)
finally:
# Schedule a new check (endless loop)
threading.Timer(30, self.cleanupLoop).start()
# Schedule a new check (endless loop)
threading.Timer(30, self.cleanupLoop).start()

View File

@ -141,7 +141,7 @@ class token:
"""
if channelObject.name in self.joinedChannels:
raise exceptions.userAlreadyInChannelException()
if not channelObject.publicRead and not self.admin:
if channelObject.publicRead == False and self.admin == False:
raise exceptions.channelNoPermissionsException()
self.joinedChannels.append(channelObject.name)
self.joinStream("chat/{}".format(channelObject.name))
@ -212,10 +212,10 @@ class token:
# Create and join #spectator (#spect_userid) channel
glob.channels.addTempChannel("#spect_{}".format(host.userID))
chat.joinChannel(token=self, channel="#spect_{}".format(host.userID), force=True)
chat.joinChannel(token=self, channel="#spect_{}".format(host.userID))
if len(host.spectators) == 1:
# First spectator, send #spectator join to host too
chat.joinChannel(token=host, channel="#spect_{}".format(host.userID), force=True)
chat.joinChannel(token=host, channel="#spect_{}".format(host.userID))
# Send fellow spectator join to all clients
glob.streams.broadcast(streamName, serverPackets.fellowSpectatorJoined(self.userID))
@ -265,14 +265,14 @@ class token:
# If nobody is spectating the host anymore, close #spectator channel
# and remove host from spect stream too
if len(hostToken.spectators) == 0:
chat.partChannel(token=hostToken, channel="#spect_{}".format(hostToken.userID), kick=True, force=True)
chat.partChannel(token=hostToken, channel="#spect_{}".format(hostToken.userID), kick=True)
hostToken.leaveStream(streamName)
# Console output
log.info("{} is no longer spectating {}. Current spectators: {}".format(self.username, self.spectatingUserID, hostToken.spectators))
# Part #spectator channel
chat.partChannel(token=self, channel="#spect_{}".format(self.spectatingUserID), kick=True, force=True)
chat.partChannel(token=self, channel="#spect_{}".format(self.spectatingUserID), kick=True)
# Set our spectating user to 0
self.spectating = None
@ -318,7 +318,7 @@ class token:
# Set matchID, join stream, channel and send packet
self.matchID = matchID
self.joinStream(match.streamName)
chat.joinChannel(token=self, channel="#multi_{}".format(self.matchID), force=True)
chat.joinChannel(token=self, channel="#multi_{}".format(self.matchID))
self.enqueue(serverPackets.matchJoinSuccess(matchID))
if match.isTourney:
@ -339,7 +339,7 @@ class token:
return
# Part #multiplayer channel and streams (/ and /playing)
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True, force=True)
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True)
self.leaveStream("multi/{}".format(self.matchID))
self.leaveStream("multi/{}/playing".format(self.matchID)) # optional

View File

@ -5,14 +5,11 @@ import redis
from common.ripple import userUtils
from common.log import logUtils as log
from common.sentry import sentry
from constants import serverPackets
from constants.exceptions import periodicLoopException
from events import logoutEvent
from objects import glob
from objects import osuToken
class tokenList:
def __init__(self):
self.tokens = {}
@ -174,7 +171,6 @@ class tokenList:
for _, value in self.tokens.items():
value.enqueue(packet)
@sentry.capture()
def usersTimeoutCheckLoop(self):
"""
Start timed out users disconnect loop.
@ -182,41 +178,27 @@ class tokenList:
CALL THIS FUNCTION ONLY ONCE!
:return:
"""
try:
log.debug("Checking timed out clients")
exceptions = []
timedOutTokens = [] # timed out users
timeoutLimit = int(time.time()) - 100
for key, value in self.tokens.items():
# Check timeout (fokabot is ignored)
if value.pingTime < timeoutLimit and value.userID != 999 and not value.irc and not value.tournament:
# That user has timed out, add to disconnected tokens
# We can't delete it while iterating or items() throws an error
timedOutTokens.append(key)
log.debug("Checking timed out clients")
timedOutTokens = [] # timed out users
timeoutLimit = int(time.time()) - 100
for key, value in self.tokens.items():
# Check timeout (fokabot is ignored)
if value.pingTime < timeoutLimit and value.userID != 999 and value.irc == False and value.tournament == False:
# 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
# i is token string (dictionary key)
for i in timedOutTokens:
log.debug("{} timed out!!".format(self.tokens[i].username))
self.tokens[i].enqueue(serverPackets.notification("Your connection to the server timed out."))
try:
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
# Delete timed out users from self.tokens
# i is token string (dictionary key)
for i in timedOutTokens:
log.debug("{} timed out!!".format(self.tokens[i].username))
self.tokens[i].enqueue(serverPackets.notification("Your connection to the server timed out."))
logoutEvent.handle(self.tokens[i], None)
del timedOutTokens
# Re-raise exceptions if needed
if exceptions:
raise periodicLoopException(exceptions)
finally:
# Schedule a new check (endless loop)
threading.Timer(100, self.usersTimeoutCheckLoop).start()
# Schedule a new check (endless loop)
threading.Timer(100, self.usersTimeoutCheckLoop).start()
@sentry.capture()
def spamProtectionResetLoop(self):
"""
Start spam protection reset loop.
@ -225,13 +207,12 @@ class tokenList:
:return:
"""
try:
# Reset spamRate for every token
for _, value in self.tokens.items():
value.spamRate = 0
finally:
# Schedule a new check (endless loop)
threading.Timer(10, self.spamProtectionResetLoop).start()
# Reset spamRate for every token
for _, value in self.tokens.items():
value.spamRate = 0
# Schedule a new check (endless loop)
threading.Timer(10, self.spamProtectionResetLoop).start()
def deleteBanchoSessions(self):
"""

49
pep.py
View File

@ -1,6 +1,7 @@
import os
import sys
import threading
import json
from multiprocessing.pool import ThreadPool
import tornado.gen
import tornado.httpserver
@ -9,11 +10,7 @@ import tornado.web
from raven.contrib.tornado import AsyncSentryClient
import redis
import json
import shutil
from distutils.version import LooseVersion
from common import generalUtils, agpl
from common import generalUtils
from common.constants import bcolors
from common.db import dbConnector
from common.ddog import datadogClient
@ -44,7 +41,6 @@ from pubSubHandlers import notificationHandler
from pubSubHandlers import updateSilenceHandler
from pubSubHandlers import updateStatsHandler
def make_app():
return tornado.web.Application([
(r"/", mainHandler.handler),
@ -57,15 +53,7 @@ def make_app():
(r"/stress", heavyHandler.handler)
])
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:
# Server start
consoleHelper.printServerStartHeader(True)
@ -74,6 +62,11 @@ if __name__ == "__main__":
consoleHelper.printNoNl("> Loading config file... ")
glob.conf = configHelper.config("config.ini")
# Read additional config file
consoleHelper.printNoNl("> Loading additional config file... ")
with open("config.json", "r") as f:
glob.conf.extra = json.load(f)
if glob.conf.default:
# We have generated a default config.ini, quit server
consoleHelper.printWarning()
@ -89,34 +82,6 @@ if __name__ == "__main__":
sys.exit()
else:
consoleHelper.printDone()
# Read additional config file
consoleHelper.printNoNl("> Loading additional config file... ")
try:
if not os.path.isfile(glob.conf.config["custom"]["config"]):
consoleHelper.printWarning()
consoleHelper.printColored("[!] Missing config file at {}; A default one has been generated at this location.".format(glob.conf.config["custom"]["config"]), bcolors.YELLOW)
shutil.copy("common/default_config.json", glob.conf.config["custom"]["config"])
with open(glob.conf.config["custom"]["config"], "r") as f:
glob.conf.extra = json.load(f)
consoleHelper.printDone()
except:
consoleHelper.printWarning()
consoleHelper.printColored("[!] Unable to load custom config at {}".format(glob.conf.config["custom"]["config"]), bcolors.RED)
consoleHelper.printColored("[!] Make sure you have the latest osufx common submodule!", bcolors.RED)
sys.exit()
# Check if running common module is usable
if glob.COMMON_VERSION == "Unknown":
consoleHelper.printWarning()
consoleHelper.printColored("[!] You do not seem to be using osufx's common submodule... nothing will work...", bcolors.RED)
consoleHelper.printColored("[!] You can download or fork the submodule from {}https://github.com/osufx/ripple-python-common".format(bcolors.UNDERLINE), bcolors.RED)
sys.exit()
elif LooseVersion(glob.COMMON_VERSION_REQ) > LooseVersion(glob.COMMON_VERSION):
consoleHelper.printColored("[!] Your common submodule version is below the required version number for this version of pep.py.", bcolors.RED)
consoleHelper.printColored("[!] You are highly adviced to update your common submodule as stability may vary with outdated modules.", bcolors.RED)
# Create data folder if needed
consoleHelper.printNoNl("> Checking folders... ")

View File

@ -7,7 +7,7 @@ import os
cythonExt = []
for root, dirs, files in os.walk(os.getcwd()):
for file in files:
if file.endswith(".pyx") and ".pyenv" not in root: # im sorry
if file.endswith(".pyx"):
filePath = os.path.relpath(os.path.join(root, file))
cythonExt.append(Extension(filePath.replace("/", ".")[:-4], [filePath]))

View File

@ -1 +1 @@
1.13.7
1.13.3