Moved pep.py to another repo
This commit is contained in:
commit
47305d612a
BIN
__pycache__/actions.cpython-35.pyc
Normal file
BIN
__pycache__/actions.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/banchoConfig.cpython-35.pyc
Normal file
BIN
__pycache__/banchoConfig.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/bcolors.cpython-35.pyc
Normal file
BIN
__pycache__/bcolors.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/cantSpectateEvent.cpython-35.pyc
Normal file
BIN
__pycache__/cantSpectateEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/changeActionEvent.cpython-35.pyc
Normal file
BIN
__pycache__/changeActionEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/changeMatchModsEvent.cpython-35.pyc
Normal file
BIN
__pycache__/changeMatchModsEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/changeMatchPasswordEvent.cpython-35.pyc
Normal file
BIN
__pycache__/changeMatchPasswordEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/changeMatchSettingsEvent.cpython-35.pyc
Normal file
BIN
__pycache__/changeMatchSettingsEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/changeSlotEvent.cpython-35.pyc
Normal file
BIN
__pycache__/changeSlotEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/channel.cpython-35.pyc
Normal file
BIN
__pycache__/channel.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/channelJoinEvent.cpython-35.pyc
Normal file
BIN
__pycache__/channelJoinEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/channelList.cpython-35.pyc
Normal file
BIN
__pycache__/channelList.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/channelPartEvent.cpython-35.pyc
Normal file
BIN
__pycache__/channelPartEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/clientPackets.cpython-35.pyc
Normal file
BIN
__pycache__/clientPackets.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/config.cpython-35.pyc
Normal file
BIN
__pycache__/config.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/consoleHelper.cpython-35.pyc
Normal file
BIN
__pycache__/consoleHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/countryHelper.cpython-35.pyc
Normal file
BIN
__pycache__/countryHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/createMatchEvent.cpython-35.pyc
Normal file
BIN
__pycache__/createMatchEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/crypt.cpython-35.pyc
Normal file
BIN
__pycache__/crypt.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/dataTypes.cpython-35.pyc
Normal file
BIN
__pycache__/dataTypes.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/databaseHelper.cpython-35.pyc
Normal file
BIN
__pycache__/databaseHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/exceptions.cpython-35.pyc
Normal file
BIN
__pycache__/exceptions.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/fokabot.cpython-35.pyc
Normal file
BIN
__pycache__/fokabot.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/fokabotCommands.cpython-35.pyc
Normal file
BIN
__pycache__/fokabotCommands.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/friendAddEvent.cpython-35.pyc
Normal file
BIN
__pycache__/friendAddEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/friendRemoveEvent.cpython-35.pyc
Normal file
BIN
__pycache__/friendRemoveEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/gameModes.cpython-35.pyc
Normal file
BIN
__pycache__/gameModes.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/generalFunctions.cpython-35.pyc
Normal file
BIN
__pycache__/generalFunctions.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/glob.cpython-35.pyc
Normal file
BIN
__pycache__/glob.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/joinLobbyEvent.cpython-35.pyc
Normal file
BIN
__pycache__/joinLobbyEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/joinMatchEvent.cpython-35.pyc
Normal file
BIN
__pycache__/joinMatchEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/locationHelper.cpython-35.pyc
Normal file
BIN
__pycache__/locationHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/loginEvent.cpython-35.pyc
Normal file
BIN
__pycache__/loginEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/logoutEvent.cpython-35.pyc
Normal file
BIN
__pycache__/logoutEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/match.cpython-35.pyc
Normal file
BIN
__pycache__/match.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchBeatmapEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchBeatmapEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchChangeTeamEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchChangeTeamEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchCompleteEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchCompleteEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchFailedEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchFailedEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchFramesEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchFramesEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchHasBeatmapEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchHasBeatmapEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchInviteEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchInviteEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchList.cpython-35.pyc
Normal file
BIN
__pycache__/matchList.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchLockEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchLockEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchModModes.cpython-35.pyc
Normal file
BIN
__pycache__/matchModModes.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchNoBeatmapEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchNoBeatmapEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchPlayerLoadEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchPlayerLoadEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchReadyEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchReadyEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchScoringTypes.cpython-35.pyc
Normal file
BIN
__pycache__/matchScoringTypes.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchSkipEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchSkipEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchStartEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchStartEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchTeamTypes.cpython-35.pyc
Normal file
BIN
__pycache__/matchTeamTypes.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchTeams.cpython-35.pyc
Normal file
BIN
__pycache__/matchTeams.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/matchTransferHostEvent.cpython-35.pyc
Normal file
BIN
__pycache__/matchTransferHostEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/mods.cpython-35.pyc
Normal file
BIN
__pycache__/mods.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/osuToken.cpython-35.pyc
Normal file
BIN
__pycache__/osuToken.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/packetHelper.cpython-35.pyc
Normal file
BIN
__pycache__/packetHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/packetIDs.cpython-35.pyc
Normal file
BIN
__pycache__/packetIDs.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/partLobbyEvent.cpython-35.pyc
Normal file
BIN
__pycache__/partLobbyEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/partMatchEvent.cpython-35.pyc
Normal file
BIN
__pycache__/partMatchEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/passwordHelper.cpython-35.pyc
Normal file
BIN
__pycache__/passwordHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/responseHelper.cpython-35.pyc
Normal file
BIN
__pycache__/responseHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/sendPrivateMessageEvent.cpython-35.pyc
Normal file
BIN
__pycache__/sendPrivateMessageEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/sendPublicMessageEvent.cpython-35.pyc
Normal file
BIN
__pycache__/sendPublicMessageEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/serverPackets.cpython-35.pyc
Normal file
BIN
__pycache__/serverPackets.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/setAwayMessageEvent.cpython-35.pyc
Normal file
BIN
__pycache__/setAwayMessageEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/slotStatuses.cpython-35.pyc
Normal file
BIN
__pycache__/slotStatuses.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/spectateFramesEvent.cpython-35.pyc
Normal file
BIN
__pycache__/spectateFramesEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/startSpectatingEvent.cpython-35.pyc
Normal file
BIN
__pycache__/startSpectatingEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/stopSpectatingEvent.cpython-35.pyc
Normal file
BIN
__pycache__/stopSpectatingEvent.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/systemHelper.cpython-35.pyc
Normal file
BIN
__pycache__/systemHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/tokenList.cpython-35.pyc
Normal file
BIN
__pycache__/tokenList.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/userHelper.cpython-35.pyc
Normal file
BIN
__pycache__/userHelper.cpython-35.pyc
Normal file
Binary file not shown.
BIN
__pycache__/userRanks.cpython-35.pyc
Normal file
BIN
__pycache__/userRanks.cpython-35.pyc
Normal file
Binary file not shown.
17
actions.py
Normal file
17
actions.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
"""Contains user actions"""
|
||||||
|
#TODO: Uppercase
|
||||||
|
idle = 0
|
||||||
|
afk = 1
|
||||||
|
playing = 2
|
||||||
|
editing = 3
|
||||||
|
modding = 4
|
||||||
|
multiplayer = 5
|
||||||
|
watching = 6
|
||||||
|
unknown = 7
|
||||||
|
testing = 8
|
||||||
|
submitting = 9
|
||||||
|
paused = 10
|
||||||
|
lobby = 11
|
||||||
|
multiplaying= 12
|
||||||
|
osuDirect = 13
|
||||||
|
none = 14
|
42
banchoConfig.py
Normal file
42
banchoConfig.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import glob
|
||||||
|
import generalFunctions
|
||||||
|
|
||||||
|
class banchoConfig:
|
||||||
|
"""
|
||||||
|
Class that loads settings from bancho_settings db table
|
||||||
|
"""
|
||||||
|
|
||||||
|
config = {"banchoMaintenance": False, "freeDirect": True, "menuIcon": "", "loginNotification": ""}
|
||||||
|
|
||||||
|
def __init__(self, __loadFromDB = True):
|
||||||
|
"""
|
||||||
|
Initialize a banchoConfig object (and load bancho_settings from db)
|
||||||
|
|
||||||
|
[__loadFromDB -- if True, load values from db. If False, don't load values. Default: True]
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __loadFromDB:
|
||||||
|
try:
|
||||||
|
self.loadSettings()
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def loadSettings(self):
|
||||||
|
"""
|
||||||
|
(re)load bancho_settings from DB and set values in config array
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.config["banchoMaintenance"] = generalFunctions.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"])
|
||||||
|
self.config["freeDirect"] = generalFunctions.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"]
|
||||||
|
self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"]
|
||||||
|
|
||||||
|
def setMaintenance(self, __maintenance):
|
||||||
|
"""
|
||||||
|
Turn on/off bancho maintenance mode. Write new value to db too
|
||||||
|
|
||||||
|
__maintenance -- if True, turn on maintenance mode. If false, turn it off
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.config["banchoMaintenance"] = __maintenance
|
||||||
|
glob.db.execute("UPDATE bancho_settings SET value_int = ? WHERE name = 'bancho_maintenance'", [int(__maintenance)])
|
9
bcolors.py
Normal file
9
bcolors.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"""Console colors"""
|
||||||
|
PINK = '\033[95m'
|
||||||
|
BLUE = '\033[94m'
|
||||||
|
GREEN = '\033[92m'
|
||||||
|
YELLOW = '\033[93m'
|
||||||
|
RED = '\033[91m'
|
||||||
|
ENDC = '\033[0m'
|
||||||
|
BOLD = '\033[1m'
|
||||||
|
UNDERLINE = '\033[4m'
|
21
cantSpectateEvent.py
Normal file
21
cantSpectateEvent.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import glob
|
||||||
|
import serverPackets
|
||||||
|
import consoleHelper
|
||||||
|
import bcolors
|
||||||
|
import exceptions
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# get usertoken data
|
||||||
|
userID = userToken.userID
|
||||||
|
|
||||||
|
try:
|
||||||
|
# We don't have the beatmap, we can't spectate
|
||||||
|
target = userToken.spectating
|
||||||
|
targetToken = glob.tokens.getTokenFromUserID(target)
|
||||||
|
|
||||||
|
# Send the packet to host
|
||||||
|
targetToken.enqueue(serverPackets.noSongSpectator(userID))
|
||||||
|
except exceptions.tokenNotFoundException:
|
||||||
|
# Stop spectating if token not found
|
||||||
|
consoleHelper.printColored("[!] Spectator can't spectate: token not found", bcolors.RED)
|
||||||
|
userToken.stopSpectating()
|
26
changeActionEvent.py
Normal file
26
changeActionEvent.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import glob
|
||||||
|
import clientPackets
|
||||||
|
import serverPackets
|
||||||
|
import actions
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Get usertoken data
|
||||||
|
userID = userToken.userID
|
||||||
|
username = userToken.username
|
||||||
|
|
||||||
|
# Change action packet
|
||||||
|
packetData = clientPackets.userActionChange(packetData)
|
||||||
|
|
||||||
|
# Update our action id, text and md5
|
||||||
|
userToken.actionID = packetData["actionID"]
|
||||||
|
userToken.actionText = packetData["actionText"]
|
||||||
|
userToken.actionMd5 = packetData["actionMd5"]
|
||||||
|
userToken.actionMods = packetData["actionMods"]
|
||||||
|
userToken.gameMode = packetData["gameMode"]
|
||||||
|
|
||||||
|
# Enqueue our new user panel and stats to everyone
|
||||||
|
glob.tokens.enqueueAll(serverPackets.userPanel(userID))
|
||||||
|
glob.tokens.enqueueAll(serverPackets.userStats(userID))
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
print("> {} changed action: {} [{}][{}]".format(username, str(userToken.actionID), userToken.actionText, userToken.actionMd5))
|
43
changeMatchModsEvent.py
Normal file
43
changeMatchModsEvent.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import glob
|
||||||
|
import clientPackets
|
||||||
|
import matchModModes
|
||||||
|
import mods
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Get token data
|
||||||
|
userID = userToken.userID
|
||||||
|
|
||||||
|
# Get packet data
|
||||||
|
packetData = clientPackets.changeMods(packetData)
|
||||||
|
|
||||||
|
# Make sure the match exists
|
||||||
|
matchID = userToken.matchID
|
||||||
|
if matchID not in glob.matches.matches:
|
||||||
|
return
|
||||||
|
match = glob.matches.matches[matchID]
|
||||||
|
|
||||||
|
# Set slot or match mods according to modType
|
||||||
|
if match.matchModMode == matchModModes.freeMod:
|
||||||
|
# Freemod
|
||||||
|
|
||||||
|
# Host can set global DT/HT
|
||||||
|
if userID == match.hostUserID:
|
||||||
|
# If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod
|
||||||
|
if (packetData["mods"] & mods.DoubleTime) > 0:
|
||||||
|
match.changeMatchMods(mods.DoubleTime)
|
||||||
|
# Nighcore
|
||||||
|
if (packetData["mods"] & mods.Nightcore) > 0:
|
||||||
|
match.changeMatchMods(match.mods+mods.Nightcore)
|
||||||
|
elif (packetData["mods"] & mods.HalfTime) > 0:
|
||||||
|
match.changeMatchMods(mods.HalfTime)
|
||||||
|
else:
|
||||||
|
# No DT/HT, set global mods to 0 (we are in freemod mode)
|
||||||
|
match.changeMatchMods(0)
|
||||||
|
|
||||||
|
# Set slot mods
|
||||||
|
slotID = match.getUserSlotID(userID)
|
||||||
|
if slotID != None:
|
||||||
|
match.setSlotMods(slotID, packetData["mods"])
|
||||||
|
else:
|
||||||
|
# Not freemod, set match mods
|
||||||
|
match.changeMatchMods(packetData["mods"])
|
17
changeMatchPasswordEvent.py
Normal file
17
changeMatchPasswordEvent.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import clientPackets
|
||||||
|
import glob
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Read packet data. Same structure as changeMatchSettings
|
||||||
|
packetData = clientPackets.changeMatchSettings(packetData)
|
||||||
|
|
||||||
|
# Make sure the match exists
|
||||||
|
matchID = userToken.matchID
|
||||||
|
if matchID not in glob.matches.matches:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get our match
|
||||||
|
match = glob.matches.matches[matchID]
|
||||||
|
|
||||||
|
# Update match password
|
||||||
|
match.changePassword(packetData["matchPassword"])
|
109
changeMatchSettingsEvent.py
Normal file
109
changeMatchSettingsEvent.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import glob
|
||||||
|
import clientPackets
|
||||||
|
import matchModModes
|
||||||
|
import consoleHelper
|
||||||
|
import bcolors
|
||||||
|
import random
|
||||||
|
import matchTeamTypes
|
||||||
|
import matchTeams
|
||||||
|
import slotStatuses
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Read new settings
|
||||||
|
packetData = clientPackets.changeMatchSettings(packetData)
|
||||||
|
|
||||||
|
# Get match ID
|
||||||
|
matchID = userToken.matchID
|
||||||
|
|
||||||
|
# Make sure the match exists
|
||||||
|
if matchID not in glob.matches.matches:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get match object
|
||||||
|
match = glob.matches.matches[matchID]
|
||||||
|
|
||||||
|
# Some dank memes easter egg
|
||||||
|
memeTitles = [
|
||||||
|
"RWC 2020",
|
||||||
|
"Fokabot is a duck",
|
||||||
|
"Dank memes",
|
||||||
|
"1337ms Ping",
|
||||||
|
"Iscriviti a Xenotoze",
|
||||||
|
"...e i marò?",
|
||||||
|
"Superman dies",
|
||||||
|
"The brace is on fire",
|
||||||
|
"print_foot()",
|
||||||
|
"#FREEZEBARKEZ",
|
||||||
|
"Ripple devs are actually cats",
|
||||||
|
"Thank Mr Shaural",
|
||||||
|
"NEVER GIVE UP",
|
||||||
|
"T I E D W I T H U N I T E D",
|
||||||
|
"HIGHEST HDHR LOBBY OF ALL TIME",
|
||||||
|
"This is gasoline and I set myself on fire",
|
||||||
|
"Everyone is cheating apparently",
|
||||||
|
"Kurwa mac",
|
||||||
|
"TATOE",
|
||||||
|
"This is not your drama landfill.",
|
||||||
|
"I like cheese",
|
||||||
|
"NYO IS NOT A CAT HE IS A DO(N)G",
|
||||||
|
"Datingu startuato"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Set match name
|
||||||
|
match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles)
|
||||||
|
|
||||||
|
# Update match settings
|
||||||
|
match.inProgress = packetData["inProgress"]
|
||||||
|
match.matchPassword = packetData["matchPassword"]
|
||||||
|
match.beatmapName = packetData["beatmapName"]
|
||||||
|
match.beatmapID = packetData["beatmapID"]
|
||||||
|
match.hostUserID = packetData["hostUserID"]
|
||||||
|
match.gameMode = packetData["gameMode"]
|
||||||
|
|
||||||
|
oldBeatmapMD5 = match.beatmapMD5
|
||||||
|
oldMods = match.mods
|
||||||
|
|
||||||
|
match.mods = packetData["mods"]
|
||||||
|
match.beatmapMD5 = packetData["beatmapMD5"]
|
||||||
|
match.matchScoringType = packetData["scoringType"]
|
||||||
|
match.matchTeamType = packetData["teamType"]
|
||||||
|
match.matchModMode = packetData["freeMods"]
|
||||||
|
|
||||||
|
# Reset ready if needed
|
||||||
|
if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5:
|
||||||
|
for i in range(0,16):
|
||||||
|
if match.slots[i]["status"] == slotStatuses.ready:
|
||||||
|
match.slots[i]["status"] = slotStatuses.notReady
|
||||||
|
|
||||||
|
# Reset mods if needed
|
||||||
|
if match.matchModMode == matchModModes.normal:
|
||||||
|
# Reset slot mods if not freeMods
|
||||||
|
for i in range(0,16):
|
||||||
|
match.slots[i]["mods"] = 0
|
||||||
|
else:
|
||||||
|
# Reset match mods if freemod
|
||||||
|
match.mods = 0
|
||||||
|
|
||||||
|
# Set/reset teams
|
||||||
|
if match.matchTeamType == matchTeamTypes.teamVs or match.matchTeamType == matchTeamTypes.tagTeamVs:
|
||||||
|
# Set teams
|
||||||
|
c=0
|
||||||
|
for i in range(0,16):
|
||||||
|
if match.slots[i]["team"] == matchTeams.noTeam:
|
||||||
|
match.slots[i]["team"] = matchTeams.red if c % 2 == 0 else matchTeams.blue
|
||||||
|
c+=1
|
||||||
|
else:
|
||||||
|
# Reset teams
|
||||||
|
for i in range(0,16):
|
||||||
|
match.slots[i]["team"] = matchTeams.noTeam
|
||||||
|
|
||||||
|
# Force no freemods if tag coop
|
||||||
|
if match.matchTeamType == matchTeamTypes.tagCoop or match.matchTeamType == matchTeamTypes.tagTeamVs:
|
||||||
|
match.matchModMode = matchModModes.normal
|
||||||
|
|
||||||
|
# Send updated settings
|
||||||
|
match.sendUpdate()
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
consoleHelper.printColored("> MPROOM{}: Updated room settings".format(match.matchID), bcolors.BLUE)
|
||||||
|
#consoleHelper.printColored("> MPROOM{}: DEBUG: Host is {}".format(match.matchID, match.hostUserID), bcolors.PINK)
|
18
changeSlotEvent.py
Normal file
18
changeSlotEvent.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import clientPackets
|
||||||
|
import glob
|
||||||
|
import consoleHelper
|
||||||
|
import bcolors
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Get usertoken data
|
||||||
|
userID = userToken.userID
|
||||||
|
username = userToken.username
|
||||||
|
|
||||||
|
# Read packet data
|
||||||
|
packetData = clientPackets.changeSlot(packetData)
|
||||||
|
|
||||||
|
# Get match
|
||||||
|
match = glob.matches.matches[userToken.matchID]
|
||||||
|
|
||||||
|
# Change slot
|
||||||
|
match.userChangeSlot(userID, packetData["slotID"])
|
78
channel.py
Normal file
78
channel.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
class channel:
|
||||||
|
"""
|
||||||
|
A chat channel
|
||||||
|
|
||||||
|
name -- channel name
|
||||||
|
description -- channel description
|
||||||
|
connectedUsers -- connected users IDs list
|
||||||
|
publicRead -- bool
|
||||||
|
publicWrite -- bool
|
||||||
|
moderated -- bool
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = ""
|
||||||
|
description = ""
|
||||||
|
connectedUsers = []
|
||||||
|
|
||||||
|
publicRead = False
|
||||||
|
publicWrite = False
|
||||||
|
moderated = False
|
||||||
|
|
||||||
|
def __init__(self, __name, __description, __publicRead, __publicWrite):
|
||||||
|
"""
|
||||||
|
Create a new chat channel object
|
||||||
|
|
||||||
|
__name -- channel name
|
||||||
|
__description -- channel description
|
||||||
|
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
|
||||||
|
__publicWrite -- bool, same as public read but relative to write permissions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.name = __name
|
||||||
|
self.description = __description
|
||||||
|
self.publicRead = __publicRead
|
||||||
|
self.publicWrite = __publicWrite
|
||||||
|
self.connectedUsers = []
|
||||||
|
|
||||||
|
|
||||||
|
def userJoin(self, __userID):
|
||||||
|
"""
|
||||||
|
Add a user to connected users
|
||||||
|
|
||||||
|
__userID -- user ID that joined the channel
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __userID not in self.connectedUsers:
|
||||||
|
self.connectedUsers.append(__userID)
|
||||||
|
|
||||||
|
|
||||||
|
def userPart(self, __userID):
|
||||||
|
"""
|
||||||
|
Remove a user from connected users
|
||||||
|
|
||||||
|
__userID -- user ID that left the channel
|
||||||
|
"""
|
||||||
|
|
||||||
|
connectedUsers = self.connectedUsers
|
||||||
|
if __userID in connectedUsers:
|
||||||
|
connectedUsers.remove(__userID)
|
||||||
|
|
||||||
|
|
||||||
|
def getConnectedUsers(self):
|
||||||
|
"""
|
||||||
|
Get connected user IDs list
|
||||||
|
|
||||||
|
return -- connectedUsers list
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.connectedUsers
|
||||||
|
|
||||||
|
|
||||||
|
def getConnectedUsersCount(self):
|
||||||
|
"""
|
||||||
|
Count connected users
|
||||||
|
|
||||||
|
return -- connected users number
|
||||||
|
"""
|
||||||
|
|
||||||
|
return len(self.connectedUsers)
|
56
channelJoinEvent.py
Normal file
56
channelJoinEvent.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
"""
|
||||||
|
Event called when someone joins a channel
|
||||||
|
"""
|
||||||
|
|
||||||
|
import clientPackets
|
||||||
|
import consoleHelper
|
||||||
|
import bcolors
|
||||||
|
import serverPackets
|
||||||
|
import glob
|
||||||
|
import exceptions
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Channel join packet
|
||||||
|
packetData = clientPackets.channelJoin(packetData)
|
||||||
|
joinChannel(userToken, packetData["channel"])
|
||||||
|
|
||||||
|
def joinChannel(userToken, channelName):
|
||||||
|
'''
|
||||||
|
Join a channel
|
||||||
|
|
||||||
|
userToken -- user token object of user that joins the chanlle
|
||||||
|
channelName -- name of channel
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
# Get usertoken data
|
||||||
|
username = userToken.username
|
||||||
|
userID = userToken.userID
|
||||||
|
userRank = userToken.rank
|
||||||
|
|
||||||
|
# Check spectator channel
|
||||||
|
# If it's spectator channel, skip checks and list stuff
|
||||||
|
if channelName != "#spectator" and channelName != "#multiplayer":
|
||||||
|
# Normal channel, do check stuff
|
||||||
|
# Make sure the channel exists
|
||||||
|
if channelName not in glob.channels.channels:
|
||||||
|
raise exceptions.channelUnknownException
|
||||||
|
|
||||||
|
# Check channel permissions
|
||||||
|
if glob.channels.channels[channelName].publicRead == False and userRank <= 2:
|
||||||
|
raise exceptions.channelNoPermissionsException
|
||||||
|
|
||||||
|
# Add our userID to users in that channel
|
||||||
|
glob.channels.channels[channelName].userJoin(userID)
|
||||||
|
|
||||||
|
# Add the channel to our joined channel
|
||||||
|
userToken.joinChannel(channelName)
|
||||||
|
|
||||||
|
# Send channel joined
|
||||||
|
userToken.enqueue(serverPackets.channelJoinSuccess(userID, channelName))
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
consoleHelper.printColored("> {} joined channel {}".format(username, channelName), bcolors.GREEN)
|
||||||
|
except exceptions.channelNoPermissionsException:
|
||||||
|
consoleHelper.printColored("[!] {} attempted to join channel {}, but they have no read permissions".format(username, channelName), bcolors.RED)
|
||||||
|
except exceptions.channelUnknownException:
|
||||||
|
consoleHelper.printColored("[!] {} attempted to join an unknown channel ({})".format(username, channelName), bcolors.RED)
|
40
channelList.py
Normal file
40
channelList.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import glob
|
||||||
|
import channel
|
||||||
|
|
||||||
|
class channelList:
|
||||||
|
"""
|
||||||
|
Channel list
|
||||||
|
|
||||||
|
channels -- dictionary. key: channel name, value: channel object
|
||||||
|
"""
|
||||||
|
|
||||||
|
channels = {}
|
||||||
|
|
||||||
|
|
||||||
|
def loadChannels(self):
|
||||||
|
"""
|
||||||
|
Load chat channels from db and add them to channels dictionary
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get channels from DB
|
||||||
|
channels = glob.db.fetchAll("SELECT * FROM bancho_channels")
|
||||||
|
|
||||||
|
# Add each channel if needed
|
||||||
|
for i in channels:
|
||||||
|
if i["name"] not in self.channels:
|
||||||
|
publicRead = True if i["public_read"] == 1 else False
|
||||||
|
publicWrite = True if i["public_write"] == 1 else False
|
||||||
|
self.addChannel(i["name"], i["description"], publicRead, publicWrite)
|
||||||
|
|
||||||
|
|
||||||
|
def addChannel(self, __name, __description, __publicRead, __publicWrite):
|
||||||
|
"""
|
||||||
|
Add a channel object to channels dictionary
|
||||||
|
|
||||||
|
__name -- channel name
|
||||||
|
__description -- channel description
|
||||||
|
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
|
||||||
|
__publicWrite -- bool, same as public read but relative to write permissions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.channels[__name] = channel.channel(__name, __description, __publicRead, __publicWrite)
|
36
channelPartEvent.py
Normal file
36
channelPartEvent.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
"""
|
||||||
|
Event called when someone parts a channel
|
||||||
|
"""
|
||||||
|
|
||||||
|
import consoleHelper
|
||||||
|
import bcolors
|
||||||
|
import glob
|
||||||
|
import clientPackets
|
||||||
|
import serverPackets
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Channel part packet
|
||||||
|
packetData = clientPackets.channelPart(packetData)
|
||||||
|
partChannel(userToken, packetData["channel"])
|
||||||
|
|
||||||
|
def partChannel(userToken, channelName, kick = False):
|
||||||
|
# Get usertoken data
|
||||||
|
username = userToken.username
|
||||||
|
userID = userToken.userID
|
||||||
|
|
||||||
|
# Remove us from joined users and joined channels
|
||||||
|
if channelName in glob.channels.channels:
|
||||||
|
# Check that user is in channel
|
||||||
|
if channelName in userToken.joinedChannels:
|
||||||
|
userToken.partChannel(channelName)
|
||||||
|
|
||||||
|
# Check if user is in channel
|
||||||
|
if userID in glob.channels.channels[channelName].connectedUsers:
|
||||||
|
glob.channels.channels[channelName].userPart(userID)
|
||||||
|
|
||||||
|
# Force close tab if needed
|
||||||
|
if kick == True:
|
||||||
|
userToken.enqueue(serverPackets.channelKicked(channelName))
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
consoleHelper.printColored("> {} parted channel {}".format(username, channelName), bcolors.YELLOW)
|
143
clientPackets.py
Normal file
143
clientPackets.py
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
""" Contains functions used to read specific client packets from byte stream """
|
||||||
|
import dataTypes
|
||||||
|
import packetHelper
|
||||||
|
import slotStatuses
|
||||||
|
|
||||||
|
|
||||||
|
""" General packets """
|
||||||
|
def userActionChange(stream):
|
||||||
|
return packetHelper.readPacketData(stream,
|
||||||
|
[
|
||||||
|
["actionID", dataTypes.byte],
|
||||||
|
["actionText", dataTypes.string],
|
||||||
|
["actionMd5", dataTypes.string],
|
||||||
|
["actionMods", dataTypes.uInt32],
|
||||||
|
["gameMode", dataTypes.byte]
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
""" Client chat packets """
|
||||||
|
def sendPublicMessage(stream):
|
||||||
|
return packetHelper.readPacketData(stream,
|
||||||
|
[
|
||||||
|
["unknown", dataTypes.string],
|
||||||
|
["message", dataTypes.string],
|
||||||
|
["to", dataTypes.string]
|
||||||
|
])
|
||||||
|
|
||||||
|
def sendPrivateMessage(stream):
|
||||||
|
return packetHelper.readPacketData(stream,
|
||||||
|
[
|
||||||
|
["unknown", dataTypes.string],
|
||||||
|
["message", dataTypes.string],
|
||||||
|
["to", dataTypes.string],
|
||||||
|
["unknown2", dataTypes.uInt32]
|
||||||
|
])
|
||||||
|
|
||||||
|
def setAwayMessage(stream):
|
||||||
|
return packetHelper.readPacketData(stream,
|
||||||
|
[
|
||||||
|
["unknown", dataTypes.string],
|
||||||
|
["awayMessage", dataTypes.string]
|
||||||
|
])
|
||||||
|
|
||||||
|
def channelJoin(stream):
|
||||||
|
return packetHelper.readPacketData(stream,[["channel", dataTypes.string]])
|
||||||
|
|
||||||
|
def channelPart(stream):
|
||||||
|
return packetHelper.readPacketData(stream,[["channel", dataTypes.string]])
|
||||||
|
|
||||||
|
def addRemoveFriend(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["friendID", dataTypes.sInt32]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
""" SPECTATOR PACKETS """
|
||||||
|
def startSpectating(stream):
|
||||||
|
return packetHelper.readPacketData(stream,[["userID", dataTypes.sInt32]])
|
||||||
|
|
||||||
|
|
||||||
|
""" MULTIPLAYER PACKETS """
|
||||||
|
def matchSettings(stream):
|
||||||
|
# Data to return, will be merged later
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# Some settings
|
||||||
|
struct = [
|
||||||
|
["matchID", dataTypes.uInt16],
|
||||||
|
["inProgress", dataTypes.byte],
|
||||||
|
["unknown", dataTypes.byte],
|
||||||
|
["mods", dataTypes.uInt32],
|
||||||
|
["matchName", dataTypes.string],
|
||||||
|
["matchPassword", dataTypes.string],
|
||||||
|
["beatmapName", dataTypes.string],
|
||||||
|
["beatmapID", dataTypes.uInt32],
|
||||||
|
["beatmapMD5", dataTypes.string]
|
||||||
|
]
|
||||||
|
|
||||||
|
# Slot statuses (not used)
|
||||||
|
for i in range(0,16):
|
||||||
|
struct.append(["slot{}Status".format(str(i)), dataTypes.byte])
|
||||||
|
|
||||||
|
# Slot statuses (not used)
|
||||||
|
for i in range(0,16):
|
||||||
|
struct.append(["slot{}Team".format(str(i)), dataTypes.byte])
|
||||||
|
|
||||||
|
# Read first part
|
||||||
|
data.append(packetHelper.readPacketData(stream, struct))
|
||||||
|
|
||||||
|
# Skip userIDs because fuck
|
||||||
|
start = 7+2+1+1+4+4+16+16+len(data[0]["matchName"])+len(data[0]["matchPassword"])+len(data[0]["beatmapMD5"])+len(data[0]["beatmapName"])
|
||||||
|
start += 1 if (data[0]["matchName"] == "") else 2
|
||||||
|
start += 1 if (data[0]["matchPassword"] == "") else 2
|
||||||
|
start += 2 # If beatmap name and MD5 don't change, the client sends \x0b\x00 istead of \x00 only, so always add 2. ...WHY!
|
||||||
|
start += 2
|
||||||
|
for i in range(0,16):
|
||||||
|
s = data[0]["slot{}Status".format(str(i))]
|
||||||
|
if s != slotStatuses.free and s != slotStatuses.locked:
|
||||||
|
start += 4
|
||||||
|
|
||||||
|
# Other settings
|
||||||
|
struct = [
|
||||||
|
["hostUserID", dataTypes.sInt32],
|
||||||
|
["gameMode", dataTypes.byte],
|
||||||
|
["scoringType", dataTypes.byte],
|
||||||
|
["teamType", dataTypes.byte],
|
||||||
|
["freeMods", dataTypes.byte],
|
||||||
|
]
|
||||||
|
|
||||||
|
# Read last part
|
||||||
|
data.append(packetHelper.readPacketData(stream[start:], struct, False))
|
||||||
|
|
||||||
|
# Mods if freemod (not used)
|
||||||
|
#if data[1]["freeMods"] == 1:
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for i in data:
|
||||||
|
result.update(i)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def createMatch(stream):
|
||||||
|
return matchSettings(stream)
|
||||||
|
|
||||||
|
def changeMatchSettings(stream):
|
||||||
|
return matchSettings(stream)
|
||||||
|
|
||||||
|
def changeSlot(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
|
||||||
|
|
||||||
|
def joinMatch(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["matchID", dataTypes.uInt32], ["password", dataTypes.string]])
|
||||||
|
|
||||||
|
def changeMods(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["mods", dataTypes.uInt32]])
|
||||||
|
|
||||||
|
def lockSlot(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
|
||||||
|
|
||||||
|
def transferHost(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
|
||||||
|
|
||||||
|
def matchInvite(stream):
|
||||||
|
return packetHelper.readPacketData(stream, [["userID", dataTypes.uInt32]])
|
24
config.ini
Normal file
24
config.ini
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
[db]
|
||||||
|
host = localhost
|
||||||
|
username = root
|
||||||
|
password = meme
|
||||||
|
database = heidi
|
||||||
|
pingtime = 600
|
||||||
|
|
||||||
|
[server]
|
||||||
|
server = tornado
|
||||||
|
host = 0.0.0.0
|
||||||
|
port = 5001
|
||||||
|
outputpackets = 0
|
||||||
|
outputrequesttime = 0
|
||||||
|
localizeusers = 0
|
||||||
|
timeouttime = 100
|
||||||
|
timeoutlooptime = 100
|
||||||
|
|
||||||
|
[flask]
|
||||||
|
threaded = 1
|
||||||
|
debug = 1
|
||||||
|
logger = 0
|
||||||
|
|
||||||
|
[ci]
|
||||||
|
key=rippleburgrw15gofmustard
|
107
config.py
Normal file
107
config.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import os
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
class config:
|
||||||
|
"""
|
||||||
|
config.ini object
|
||||||
|
|
||||||
|
config -- list with ini data
|
||||||
|
default -- if true, we have generated a default config.ini
|
||||||
|
"""
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
fileName = "" # config filename
|
||||||
|
default = True
|
||||||
|
|
||||||
|
# Check if config.ini exists and load/generate it
|
||||||
|
def __init__(self, __file):
|
||||||
|
"""
|
||||||
|
Initialize a config object
|
||||||
|
|
||||||
|
__file -- filename
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.fileName = __file
|
||||||
|
if os.path.isfile(self.fileName):
|
||||||
|
# config.ini found, load it
|
||||||
|
self.config.read(self.fileName)
|
||||||
|
self.default = False
|
||||||
|
else:
|
||||||
|
# config.ini not found, generate a default one
|
||||||
|
self.generateDefaultConfig()
|
||||||
|
self.default = True
|
||||||
|
|
||||||
|
|
||||||
|
# Check if config.ini has all needed the keys
|
||||||
|
def checkConfig(self):
|
||||||
|
"""
|
||||||
|
Check if this config has the required keys
|
||||||
|
|
||||||
|
return -- True if valid, False if not
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to get all the required keys
|
||||||
|
self.config.get("db","host")
|
||||||
|
self.config.get("db","username")
|
||||||
|
self.config.get("db","password")
|
||||||
|
self.config.get("db","database")
|
||||||
|
self.config.get("db","pingtime")
|
||||||
|
|
||||||
|
self.config.get("server","server")
|
||||||
|
self.config.get("server","host")
|
||||||
|
self.config.get("server","port")
|
||||||
|
self.config.get("server","localizeusers")
|
||||||
|
self.config.get("server","outputpackets")
|
||||||
|
self.config.get("server","outputrequesttime")
|
||||||
|
self.config.get("server","timeouttime")
|
||||||
|
self.config.get("server","timeoutlooptime")
|
||||||
|
|
||||||
|
if self.config["server"]["server"] == "flask":
|
||||||
|
# Flask only config
|
||||||
|
self.config.get("flask","threaded")
|
||||||
|
self.config.get("flask","debug")
|
||||||
|
self.config.get("flask","logger")
|
||||||
|
|
||||||
|
self.config.get("ci","key")
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Generate a default config.ini
|
||||||
|
def generateDefaultConfig(self):
|
||||||
|
"""Open and set default keys for that config file"""
|
||||||
|
|
||||||
|
# Open config.ini in write mode
|
||||||
|
f = open(self.fileName, "w")
|
||||||
|
|
||||||
|
# Set keys to config object
|
||||||
|
self.config.add_section("db")
|
||||||
|
self.config.set("db", "host", "localhost")
|
||||||
|
self.config.set("db", "username", "root")
|
||||||
|
self.config.set("db", "password", "")
|
||||||
|
self.config.set("db", "database", "ripple")
|
||||||
|
self.config.set("db", "pingtime", "600")
|
||||||
|
|
||||||
|
self.config.add_section("server")
|
||||||
|
self.config.set("server", "server", "tornado")
|
||||||
|
self.config.set("server", "host", "0.0.0.0")
|
||||||
|
self.config.set("server", "port", "5001")
|
||||||
|
self.config.set("server", "localizeusers", "1")
|
||||||
|
self.config.set("server", "outputpackets", "0")
|
||||||
|
self.config.set("server", "outputrequesttime", "0")
|
||||||
|
self.config.set("server", "timeoutlooptime", "100")
|
||||||
|
self.config.set("server", "timeouttime", "100")
|
||||||
|
|
||||||
|
self.config.add_section("flask")
|
||||||
|
self.config.set("flask", "threaded", "1")
|
||||||
|
self.config.set("flask", "debug", "0")
|
||||||
|
self.config.set("flask", "logger", "0")
|
||||||
|
|
||||||
|
self.config.add_section("ci")
|
||||||
|
self.config.set("ci", "key", "changeme")
|
||||||
|
|
||||||
|
# Write ini to file and close
|
||||||
|
self.config.write(f)
|
||||||
|
f.close()
|
71
consoleHelper.py
Normal file
71
consoleHelper.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
"""Some console related functions"""
|
||||||
|
|
||||||
|
import bcolors
|
||||||
|
import glob
|
||||||
|
|
||||||
|
def printServerStartHeader(asciiArt):
|
||||||
|
"""Print server start header with optional ascii art
|
||||||
|
|
||||||
|
asciiArt -- if True, will print ascii art too"""
|
||||||
|
|
||||||
|
if asciiArt == True:
|
||||||
|
print("{} _ __".format(bcolors.GREEN))
|
||||||
|
print(" (_) / /")
|
||||||
|
print(" ______ __ ____ ____ / /____")
|
||||||
|
print(" / ___/ / _ \\/ _ \\/ / _ \\")
|
||||||
|
print(" / / / / /_) / /_) / / ____/")
|
||||||
|
print("/__/ /__/ .___/ .___/__/ \\_____/")
|
||||||
|
print(" / / / /")
|
||||||
|
print(" /__/ /__/\r\n")
|
||||||
|
print(" .. o .")
|
||||||
|
print(" o.o o . o")
|
||||||
|
print(" oo...")
|
||||||
|
print(" __[]__")
|
||||||
|
print(" nyo --> _\\:D/_/o_o_o_|__ u wot m8")
|
||||||
|
print(" \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/")
|
||||||
|
print(" \\ . .. .. . /")
|
||||||
|
print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^{}".format(bcolors.ENDC))
|
||||||
|
|
||||||
|
printColored("> Welcome to pep.py osu!bancho server v{}".format(glob.VERSION), bcolors.GREEN)
|
||||||
|
printColored("> Made by the Ripple team", bcolors.GREEN)
|
||||||
|
printColored("> {}https://github.com/osuripple/ripple".format(bcolors.UNDERLINE), bcolors.GREEN)
|
||||||
|
printColored("> Press CTRL+C to exit\n",bcolors.GREEN)
|
||||||
|
|
||||||
|
|
||||||
|
def printNoNl(string):
|
||||||
|
"""
|
||||||
|
Print string without new line at the end
|
||||||
|
|
||||||
|
string -- string to print
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(string, end="")
|
||||||
|
|
||||||
|
|
||||||
|
def printColored(string, color):
|
||||||
|
"""
|
||||||
|
Print colored string
|
||||||
|
|
||||||
|
string -- string to print
|
||||||
|
color -- see bcolors.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("{}{}{}".format(color, string, bcolors.ENDC))
|
||||||
|
|
||||||
|
|
||||||
|
def printError():
|
||||||
|
"""Print error text FOR LOADING"""
|
||||||
|
|
||||||
|
printColored("Error", bcolors.RED)
|
||||||
|
|
||||||
|
|
||||||
|
def printDone():
|
||||||
|
"""Print error text FOR LOADING"""
|
||||||
|
|
||||||
|
printColored("Done", bcolors.GREEN)
|
||||||
|
|
||||||
|
|
||||||
|
def printWarning():
|
||||||
|
"""Print error text FOR LOADING"""
|
||||||
|
|
||||||
|
printColored("Warning", bcolors.YELLOW)
|
282
countryHelper.py
Normal file
282
countryHelper.py
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
"""Contains all country codes with their osu numeric code"""
|
||||||
|
|
||||||
|
countryCodes = {
|
||||||
|
"LV": 132,
|
||||||
|
"AD": 3,
|
||||||
|
"LT": 130,
|
||||||
|
"KM": 116,
|
||||||
|
"QA": 182,
|
||||||
|
"VA": 0,
|
||||||
|
"PK": 173,
|
||||||
|
"KI": 115,
|
||||||
|
"SS": 0,
|
||||||
|
"KH": 114,
|
||||||
|
"NZ": 166,
|
||||||
|
"TO": 215,
|
||||||
|
"KZ": 122,
|
||||||
|
"GA": 76,
|
||||||
|
"BW": 35,
|
||||||
|
"AX": 247,
|
||||||
|
"GE": 79,
|
||||||
|
"UA": 222,
|
||||||
|
"CR": 50,
|
||||||
|
"AE": 0,
|
||||||
|
"NE": 157,
|
||||||
|
"ZA": 240,
|
||||||
|
"SK": 196,
|
||||||
|
"BV": 34,
|
||||||
|
"SH": 0,
|
||||||
|
"PT": 179,
|
||||||
|
"SC": 189,
|
||||||
|
"CO": 49,
|
||||||
|
"GP": 86,
|
||||||
|
"GY": 93,
|
||||||
|
"CM": 47,
|
||||||
|
"TJ": 211,
|
||||||
|
"AF": 5,
|
||||||
|
"IE": 101,
|
||||||
|
"AL": 8,
|
||||||
|
"BG": 24,
|
||||||
|
"JO": 110,
|
||||||
|
"MU": 149,
|
||||||
|
"PM": 0,
|
||||||
|
"LA": 0,
|
||||||
|
"IO": 104,
|
||||||
|
"KY": 121,
|
||||||
|
"SA": 187,
|
||||||
|
"KN": 0,
|
||||||
|
"OM": 167,
|
||||||
|
"CY": 54,
|
||||||
|
"BQ": 0,
|
||||||
|
"BT": 33,
|
||||||
|
"WS": 236,
|
||||||
|
"ES": 67,
|
||||||
|
"LR": 128,
|
||||||
|
"RW": 186,
|
||||||
|
"AQ": 12,
|
||||||
|
"PW": 180,
|
||||||
|
"JE": 250,
|
||||||
|
"TN": 214,
|
||||||
|
"ZW": 243,
|
||||||
|
"JP": 111,
|
||||||
|
"BB": 20,
|
||||||
|
"VN": 233,
|
||||||
|
"HN": 96,
|
||||||
|
"KP": 0,
|
||||||
|
"WF": 235,
|
||||||
|
"EC": 62,
|
||||||
|
"HU": 99,
|
||||||
|
"GF": 80,
|
||||||
|
"GQ": 87,
|
||||||
|
"TW": 220,
|
||||||
|
"MC": 135,
|
||||||
|
"BE": 22,
|
||||||
|
"PN": 176,
|
||||||
|
"SZ": 205,
|
||||||
|
"CZ": 55,
|
||||||
|
"LY": 0,
|
||||||
|
"IN": 103,
|
||||||
|
"FM": 0,
|
||||||
|
"PY": 181,
|
||||||
|
"PH": 172,
|
||||||
|
"MN": 142,
|
||||||
|
"GG": 248,
|
||||||
|
"CC": 39,
|
||||||
|
"ME": 242,
|
||||||
|
"DO": 60,
|
||||||
|
"KR": 0,
|
||||||
|
"PL": 174,
|
||||||
|
"MT": 148,
|
||||||
|
"MM": 141,
|
||||||
|
"AW": 17,
|
||||||
|
"MV": 150,
|
||||||
|
"BD": 21,
|
||||||
|
"NR": 164,
|
||||||
|
"AT": 15,
|
||||||
|
"GW": 92,
|
||||||
|
"FR": 74,
|
||||||
|
"LI": 126,
|
||||||
|
"CF": 41,
|
||||||
|
"DZ": 61,
|
||||||
|
"MA": 134,
|
||||||
|
"VG": 0,
|
||||||
|
"NC": 156,
|
||||||
|
"IQ": 105,
|
||||||
|
"BN": 0,
|
||||||
|
"BF": 23,
|
||||||
|
"BO": 30,
|
||||||
|
"GB": 77,
|
||||||
|
"CU": 51,
|
||||||
|
"LU": 131,
|
||||||
|
"YT": 238,
|
||||||
|
"NO": 162,
|
||||||
|
"SM": 198,
|
||||||
|
"GL": 83,
|
||||||
|
"IS": 107,
|
||||||
|
"AO": 11,
|
||||||
|
"MH": 138,
|
||||||
|
"SE": 191,
|
||||||
|
"ZM": 241,
|
||||||
|
"FJ": 70,
|
||||||
|
"SL": 197,
|
||||||
|
"CH": 43,
|
||||||
|
"RU": 0,
|
||||||
|
"CW": 0,
|
||||||
|
"CX": 53,
|
||||||
|
"TF": 208,
|
||||||
|
"NL": 161,
|
||||||
|
"AU": 16,
|
||||||
|
"FI": 69,
|
||||||
|
"MS": 147,
|
||||||
|
"GH": 81,
|
||||||
|
"BY": 36,
|
||||||
|
"IL": 102,
|
||||||
|
"VC": 0,
|
||||||
|
"NG": 159,
|
||||||
|
"HT": 98,
|
||||||
|
"LS": 129,
|
||||||
|
"MR": 146,
|
||||||
|
"YE": 237,
|
||||||
|
"MP": 144,
|
||||||
|
"SX": 0,
|
||||||
|
"RE": 183,
|
||||||
|
"RO": 184,
|
||||||
|
"NP": 163,
|
||||||
|
"CG": 0,
|
||||||
|
"FO": 73,
|
||||||
|
"CI": 0,
|
||||||
|
"TH": 210,
|
||||||
|
"HK": 94,
|
||||||
|
"TK": 212,
|
||||||
|
"XK": 0,
|
||||||
|
"DM": 59,
|
||||||
|
"LC": 0,
|
||||||
|
"ID": 100,
|
||||||
|
"MG": 137,
|
||||||
|
"JM": 109,
|
||||||
|
"IT": 108,
|
||||||
|
"CA": 38,
|
||||||
|
"TZ": 221,
|
||||||
|
"GI": 82,
|
||||||
|
"KG": 113,
|
||||||
|
"NU": 165,
|
||||||
|
"TV": 219,
|
||||||
|
"LB": 124,
|
||||||
|
"SY": 0,
|
||||||
|
"PR": 177,
|
||||||
|
"NI": 160,
|
||||||
|
"KE": 112,
|
||||||
|
"MO": 0,
|
||||||
|
"SR": 201,
|
||||||
|
"VI": 0,
|
||||||
|
"SV": 203,
|
||||||
|
"HM": 0,
|
||||||
|
"CD": 0,
|
||||||
|
"BI": 26,
|
||||||
|
"BM": 28,
|
||||||
|
"MW": 151,
|
||||||
|
"TM": 213,
|
||||||
|
"GT": 90,
|
||||||
|
"AG": 0,
|
||||||
|
"UM": 0,
|
||||||
|
"US": 225,
|
||||||
|
"AR": 13,
|
||||||
|
"DJ": 57,
|
||||||
|
"KW": 120,
|
||||||
|
"MY": 153,
|
||||||
|
"FK": 71,
|
||||||
|
"EG": 64,
|
||||||
|
"BA": 0,
|
||||||
|
"CN": 48,
|
||||||
|
"GN": 85,
|
||||||
|
"PS": 178,
|
||||||
|
"SO": 200,
|
||||||
|
"IM": 249,
|
||||||
|
"GS": 0,
|
||||||
|
"BR": 31,
|
||||||
|
"GM": 84,
|
||||||
|
"PF": 170,
|
||||||
|
"PA": 168,
|
||||||
|
"PG": 171,
|
||||||
|
"BH": 25,
|
||||||
|
"TG": 209,
|
||||||
|
"GU": 91,
|
||||||
|
"CK": 45,
|
||||||
|
"MF": 252,
|
||||||
|
"VE": 230,
|
||||||
|
"CL": 46,
|
||||||
|
"TR": 217,
|
||||||
|
"UG": 223,
|
||||||
|
"GD": 78,
|
||||||
|
"TT": 218,
|
||||||
|
"TL": 0,
|
||||||
|
"MD": 0,
|
||||||
|
"MK": 0,
|
||||||
|
"ST": 202,
|
||||||
|
"CV": 52,
|
||||||
|
"MQ": 145,
|
||||||
|
"GR": 88,
|
||||||
|
"HR": 97,
|
||||||
|
"BZ": 37,
|
||||||
|
"UZ": 227,
|
||||||
|
"DK": 58,
|
||||||
|
"SN": 199,
|
||||||
|
"ET": 68,
|
||||||
|
"VU": 234,
|
||||||
|
"ER": 66,
|
||||||
|
"BJ": 27,
|
||||||
|
"LK": 127,
|
||||||
|
"NA": 155,
|
||||||
|
"AS": 14,
|
||||||
|
"SG": 192,
|
||||||
|
"PE": 169,
|
||||||
|
"IR": 0,
|
||||||
|
"MX": 152,
|
||||||
|
"TD": 207,
|
||||||
|
"AZ": 18,
|
||||||
|
"AM": 9,
|
||||||
|
"BL": 0,
|
||||||
|
"SJ": 195,
|
||||||
|
"SB": 188,
|
||||||
|
"NF": 158,
|
||||||
|
"RS": 239,
|
||||||
|
"DE": 56,
|
||||||
|
"EH": 65,
|
||||||
|
"EE": 63,
|
||||||
|
"SD": 190,
|
||||||
|
"ML": 140,
|
||||||
|
"TC": 206,
|
||||||
|
"MZ": 154,
|
||||||
|
"BS": 32,
|
||||||
|
"UY": 226,
|
||||||
|
"SI": 194,
|
||||||
|
"AI": 7
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def getCountryID(code):
|
||||||
|
"""
|
||||||
|
Get country ID for osu client
|
||||||
|
|
||||||
|
code -- country name abbreviation (eg: US)
|
||||||
|
return -- country code int
|
||||||
|
"""
|
||||||
|
|
||||||
|
if code in countryCodes:
|
||||||
|
return countryCodes[code]
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def getCountryLetters(code):
|
||||||
|
"""
|
||||||
|
Get country letters from osu country ID
|
||||||
|
|
||||||
|
code -- country code int
|
||||||
|
return -- country name (2 letters) (XX if code not found)
|
||||||
|
"""
|
||||||
|
|
||||||
|
for key, value in countryCodes.items():
|
||||||
|
if value == code:
|
||||||
|
return key
|
||||||
|
|
||||||
|
return "XX"
|
44
createMatchEvent.py
Normal file
44
createMatchEvent.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import serverPackets
|
||||||
|
import clientPackets
|
||||||
|
import glob
|
||||||
|
import consoleHelper
|
||||||
|
import bcolors
|
||||||
|
import joinMatchEvent
|
||||||
|
import exceptions
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
try:
|
||||||
|
# get usertoken data
|
||||||
|
userID = userToken.userID
|
||||||
|
|
||||||
|
# Read packet data
|
||||||
|
packetData = clientPackets.createMatch(packetData)
|
||||||
|
|
||||||
|
# Create a match object
|
||||||
|
# TODO: Player number check
|
||||||
|
matchID = glob.matches.createMatch(packetData["matchName"], packetData["matchPassword"], packetData["beatmapID"], packetData["beatmapName"], packetData["beatmapMD5"], packetData["gameMode"], userID)
|
||||||
|
|
||||||
|
# Make sure the match has been created
|
||||||
|
if matchID not in glob.matches.matches:
|
||||||
|
raise exceptions.matchCreateError
|
||||||
|
|
||||||
|
# Get match object
|
||||||
|
match = glob.matches.matches[matchID]
|
||||||
|
|
||||||
|
# Join that match
|
||||||
|
joinMatchEvent.joinMatch(userToken, matchID, packetData["matchPassword"])
|
||||||
|
|
||||||
|
# Give host to match creator
|
||||||
|
match.setHost(userID)
|
||||||
|
|
||||||
|
# Send match create packet to everyone in lobby
|
||||||
|
for i in glob.matches.usersInLobby:
|
||||||
|
# Make sure this user is still connected
|
||||||
|
token = glob.tokens.getTokenFromUserID(i)
|
||||||
|
if token != None:
|
||||||
|
token.enqueue(serverPackets.createMatch(matchID))
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
consoleHelper.printColored("> MPROOM{}: Room created!".format(matchID), bcolors.BLUE)
|
||||||
|
except exceptions.matchCreateError:
|
||||||
|
consoleHelper.printColored("[!] Error while creating match!", bcolors.RED)
|
302
crypt.py
Normal file
302
crypt.py
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
# Huge thanks to Cairnarvon
|
||||||
|
# https://gist.github.com/Cairnarvon/5075687
|
||||||
|
|
||||||
|
# Initial permutation
|
||||||
|
IP = (
|
||||||
|
58, 50, 42, 34, 26, 18, 10, 2,
|
||||||
|
60, 52, 44, 36, 28, 20, 12, 4,
|
||||||
|
62, 54, 46, 38, 30, 22, 14, 6,
|
||||||
|
64, 56, 48, 40, 32, 24, 16, 8,
|
||||||
|
57, 49, 41, 33, 25, 17, 9, 1,
|
||||||
|
59, 51, 43, 35, 27, 19, 11, 3,
|
||||||
|
61, 53, 45, 37, 29, 21, 13, 5,
|
||||||
|
63, 55, 47, 39, 31, 23, 15, 7,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Final permutation, FP = IP^(-1)
|
||||||
|
FP = (
|
||||||
|
40, 8, 48, 16, 56, 24, 64, 32,
|
||||||
|
39, 7, 47, 15, 55, 23, 63, 31,
|
||||||
|
38, 6, 46, 14, 54, 22, 62, 30,
|
||||||
|
37, 5, 45, 13, 53, 21, 61, 29,
|
||||||
|
36, 4, 44, 12, 52, 20, 60, 28,
|
||||||
|
35, 3, 43, 11, 51, 19, 59, 27,
|
||||||
|
34, 2, 42, 10, 50, 18, 58, 26,
|
||||||
|
33, 1, 41, 9, 49, 17, 57, 25,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Permuted-choice 1 from the key bits to yield C and D.
|
||||||
|
# Note that bits 8,16... are left out: They are intended for a parity check.
|
||||||
|
PC1_C = (
|
||||||
|
57, 49, 41, 33, 25, 17, 9,
|
||||||
|
1, 58, 50, 42, 34, 26, 18,
|
||||||
|
10, 2, 59, 51, 43, 35, 27,
|
||||||
|
19, 11, 3, 60, 52, 44, 36,
|
||||||
|
)
|
||||||
|
PC1_D = (
|
||||||
|
63, 55, 47, 39, 31, 23, 15,
|
||||||
|
7, 62, 54, 46, 38, 30, 22,
|
||||||
|
14, 6, 61, 53, 45, 37, 29,
|
||||||
|
21, 13, 5, 28, 20, 12, 4,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Permuted-choice 2, to pick out the bits from the CD array that generate the
|
||||||
|
# key schedule.
|
||||||
|
PC2_C = (
|
||||||
|
14, 17, 11, 24, 1, 5,
|
||||||
|
3, 28, 15, 6, 21, 10,
|
||||||
|
23, 19, 12, 4, 26, 8,
|
||||||
|
16, 7, 27, 20, 13, 2,
|
||||||
|
)
|
||||||
|
PC2_D = (
|
||||||
|
41, 52, 31, 37, 47, 55,
|
||||||
|
30, 40, 51, 45, 33, 48,
|
||||||
|
44, 49, 39, 56, 34, 53,
|
||||||
|
46, 42, 50, 36, 29, 32,
|
||||||
|
)
|
||||||
|
|
||||||
|
# The C and D arrays are used to calculate the key schedule.
|
||||||
|
C = [0] * 28
|
||||||
|
D = [0] * 28
|
||||||
|
|
||||||
|
# The key schedule. Generated from the key.
|
||||||
|
KS = [[0] * 48 for _ in range(16)]
|
||||||
|
|
||||||
|
# The E bit-selection table.
|
||||||
|
E = [0] * 48
|
||||||
|
e2 = (
|
||||||
|
32, 1, 2, 3, 4, 5,
|
||||||
|
4, 5, 6, 7, 8, 9,
|
||||||
|
8, 9, 10, 11, 12, 13,
|
||||||
|
12, 13, 14, 15, 16, 17,
|
||||||
|
16, 17, 18, 19, 20, 21,
|
||||||
|
20, 21, 22, 23, 24, 25,
|
||||||
|
24, 25, 26, 27, 28, 29,
|
||||||
|
28, 29, 30, 31, 32, 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
# S-boxes.
|
||||||
|
S = (
|
||||||
|
(
|
||||||
|
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
|
||||||
|
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
|
||||||
|
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
|
||||||
|
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
|
||||||
|
),
|
||||||
|
(
|
||||||
|
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
|
||||||
|
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
|
||||||
|
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
|
||||||
|
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
|
||||||
|
),
|
||||||
|
(
|
||||||
|
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
|
||||||
|
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
|
||||||
|
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
|
||||||
|
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
|
||||||
|
),
|
||||||
|
(
|
||||||
|
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
|
||||||
|
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
|
||||||
|
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
|
||||||
|
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
|
||||||
|
),
|
||||||
|
(
|
||||||
|
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
|
||||||
|
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
|
||||||
|
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
|
||||||
|
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
|
||||||
|
),
|
||||||
|
(
|
||||||
|
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
|
||||||
|
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
|
||||||
|
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
|
||||||
|
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
|
||||||
|
),
|
||||||
|
(
|
||||||
|
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
|
||||||
|
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
|
||||||
|
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
|
||||||
|
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
|
||||||
|
),
|
||||||
|
(
|
||||||
|
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
|
||||||
|
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
|
||||||
|
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
|
||||||
|
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# P is a permutation on the selected combination of the current L and key.
|
||||||
|
P = (
|
||||||
|
16, 7, 20, 21,
|
||||||
|
29, 12, 28, 17,
|
||||||
|
1, 15, 23, 26,
|
||||||
|
5, 18, 31, 10,
|
||||||
|
2, 8, 24, 14,
|
||||||
|
32, 27, 3, 9,
|
||||||
|
19, 13, 30, 6,
|
||||||
|
22, 11, 4, 25,
|
||||||
|
)
|
||||||
|
|
||||||
|
# The combination of the key and the input, before selection.
|
||||||
|
preS = [0] * 48
|
||||||
|
|
||||||
|
|
||||||
|
def __setkey(key):
|
||||||
|
"""
|
||||||
|
Set up the key schedule from the encryption key.
|
||||||
|
"""
|
||||||
|
global C, D, KS, E
|
||||||
|
|
||||||
|
shifts = (1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1)
|
||||||
|
|
||||||
|
# First, generate C and D by permuting the key. The lower order bit of each
|
||||||
|
# 8-bit char is not used, so C and D are only 28 bits apiece.
|
||||||
|
for i in range(28):
|
||||||
|
C[i] = key[PC1_C[i] - 1]
|
||||||
|
D[i] = key[PC1_D[i] - 1]
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
# rotate
|
||||||
|
for k in range(shifts[i]):
|
||||||
|
temp = C[0]
|
||||||
|
|
||||||
|
for j in range(27):
|
||||||
|
C[j] = C[j + 1]
|
||||||
|
|
||||||
|
C[27] = temp
|
||||||
|
temp = D[0]
|
||||||
|
for j in range(27):
|
||||||
|
D[j] = D[j + 1]
|
||||||
|
|
||||||
|
D[27] = temp
|
||||||
|
|
||||||
|
# get Ki. Note C and D are concatenated
|
||||||
|
for j in range(24):
|
||||||
|
KS[i][j] = C[PC2_C[j] - 1]
|
||||||
|
KS[i][j + 24] = D[PC2_D[j] - 28 - 1]
|
||||||
|
|
||||||
|
# load E with the initial E bit selections
|
||||||
|
for i in range(48):
|
||||||
|
E[i] = e2[i]
|
||||||
|
|
||||||
|
def __encrypt(block):
|
||||||
|
global preS
|
||||||
|
|
||||||
|
left, right = [], [] # block in two halves
|
||||||
|
f = [0] * 32
|
||||||
|
|
||||||
|
# First, permute the bits in the input
|
||||||
|
for j in range(32):
|
||||||
|
left.append(block[IP[j] - 1])
|
||||||
|
|
||||||
|
for j in range(32, 64):
|
||||||
|
right.append(block[IP[j] - 1])
|
||||||
|
|
||||||
|
# Perform an encryption operation 16 times.
|
||||||
|
for i in range(16):
|
||||||
|
# Save the right array, which will be the new left.
|
||||||
|
old = right[:]
|
||||||
|
|
||||||
|
# Expand right to 48 bits using the E selector and exclusive-or with
|
||||||
|
# the current key bits.
|
||||||
|
for j in range(48):
|
||||||
|
preS[j] = right[E[j] - 1] ^ KS[i][j]
|
||||||
|
|
||||||
|
# The pre-select bits are now considered in 8 groups of 6 bits each.
|
||||||
|
# The 8 selection functions map these 6-bit quantities into 4-bit
|
||||||
|
# quantities and the results are permuted to make an f(R, K).
|
||||||
|
# The indexing into the selection functions is peculiar; it could be
|
||||||
|
# simplified by rewriting the tables.
|
||||||
|
for j in range(8):
|
||||||
|
temp = 6 * j
|
||||||
|
k = S[j][(preS[temp + 0] << 5) +
|
||||||
|
(preS[temp + 1] << 3) +
|
||||||
|
(preS[temp + 2] << 2) +
|
||||||
|
(preS[temp + 3] << 1) +
|
||||||
|
(preS[temp + 4] << 0) +
|
||||||
|
(preS[temp + 5] << 4)]
|
||||||
|
|
||||||
|
temp = 4 * j
|
||||||
|
f[temp + 0] = (k >> 3) & 1
|
||||||
|
f[temp + 1] = (k >> 2) & 1
|
||||||
|
f[temp + 2] = (k >> 1) & 1
|
||||||
|
f[temp + 3] = (k >> 0) & 1
|
||||||
|
|
||||||
|
# The new right is left ^ f(R, K).
|
||||||
|
# The f here has to be permuted first, though.
|
||||||
|
for j in range(32):
|
||||||
|
right[j] = left[j] ^ f[P[j] - 1]
|
||||||
|
|
||||||
|
# Finally the new left (the original right) is copied back.
|
||||||
|
left = old
|
||||||
|
|
||||||
|
# The output left and right are reversed.
|
||||||
|
left, right = right, left
|
||||||
|
|
||||||
|
# The final output gets the inverse permutation of the very original
|
||||||
|
for j in range(64):
|
||||||
|
i = FP[j]
|
||||||
|
if i < 33:
|
||||||
|
block[j] = left[i - 1]
|
||||||
|
else:
|
||||||
|
block[j] = right[i - 33]
|
||||||
|
|
||||||
|
return block
|
||||||
|
|
||||||
|
def crypt(pw, salt):
|
||||||
|
iobuf = []
|
||||||
|
|
||||||
|
# break pw into 64 bits
|
||||||
|
block = []
|
||||||
|
for c in pw:
|
||||||
|
c = ord(c)
|
||||||
|
for j in range(7):
|
||||||
|
block.append((c >> (6 - j)) & 1)
|
||||||
|
block.append(0)
|
||||||
|
block += [0] * (64 - len(block))
|
||||||
|
|
||||||
|
# set key based on pw
|
||||||
|
__setkey(block)
|
||||||
|
|
||||||
|
for i in range(2):
|
||||||
|
# store salt at beginning of results
|
||||||
|
iobuf.append(salt[i])
|
||||||
|
c = ord(salt[i])
|
||||||
|
|
||||||
|
if c > ord('Z'):
|
||||||
|
c -= 6
|
||||||
|
|
||||||
|
if c > ord('9'):
|
||||||
|
c -= 7
|
||||||
|
|
||||||
|
c -= ord('.')
|
||||||
|
|
||||||
|
# use salt to effect the E-bit selection
|
||||||
|
for j in range(6):
|
||||||
|
if (c >> j) & 1:
|
||||||
|
E[6 * i + j], E[6 * i + j + 24] = E[6 * i + j + 24], E[6 * i + j]
|
||||||
|
|
||||||
|
# call DES encryption 25 times using pw as key and initial data = 0
|
||||||
|
block = [0] * 66
|
||||||
|
for i in range(25):
|
||||||
|
block = __encrypt(block)
|
||||||
|
|
||||||
|
# format encrypted block for standard crypt(3) output
|
||||||
|
for i in range(11):
|
||||||
|
c = 0
|
||||||
|
for j in range(6):
|
||||||
|
c <<= 1
|
||||||
|
c |= block[6 * i + j]
|
||||||
|
|
||||||
|
c += ord('.')
|
||||||
|
if c > ord('9'):
|
||||||
|
c += 7
|
||||||
|
|
||||||
|
if c > ord('Z'):
|
||||||
|
c += 6
|
||||||
|
|
||||||
|
iobuf.append(chr(c))
|
||||||
|
|
||||||
|
return ''.join(iobuf)
|
12
dataTypes.py
Normal file
12
dataTypes.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
"""Bancho packets data types"""
|
||||||
|
#TODO: Uppercase, maybe?
|
||||||
|
byte = 0
|
||||||
|
uInt16 = 1
|
||||||
|
sInt16 = 2
|
||||||
|
uInt32 = 3
|
||||||
|
sInt32 = 4
|
||||||
|
uInt64 = 5
|
||||||
|
sInt64 = 6
|
||||||
|
string = 7
|
||||||
|
ffloat = 8 # because float is a keyword
|
||||||
|
bbytes = 9
|
137
databaseHelper.py
Normal file
137
databaseHelper.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import pymysql
|
||||||
|
import bcolors
|
||||||
|
import consoleHelper
|
||||||
|
import threading
|
||||||
|
|
||||||
|
class db:
|
||||||
|
"""A MySQL database connection"""
|
||||||
|
|
||||||
|
connection = None
|
||||||
|
disconnected = False
|
||||||
|
pingTime = 600
|
||||||
|
|
||||||
|
def __init__(self, __host, __username, __password, __database, __pingTime = 600):
|
||||||
|
"""
|
||||||
|
Connect to MySQL database
|
||||||
|
|
||||||
|
__host -- MySQL host name
|
||||||
|
__username -- MySQL username
|
||||||
|
__password -- MySQL password
|
||||||
|
__database -- MySQL database name
|
||||||
|
__pingTime -- MySQL database ping time (default: 600)
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.connection = pymysql.connect(host=__host, user=__username, password=__password, db=__database, cursorclass=pymysql.cursors.DictCursor, autocommit=True)
|
||||||
|
self.pingTime = __pingTime
|
||||||
|
self.pingLoop()
|
||||||
|
|
||||||
|
|
||||||
|
def bindParams(self, __query, __params):
|
||||||
|
"""
|
||||||
|
Replace every ? with the respective **escaped** parameter in array
|
||||||
|
|
||||||
|
__query -- query with ?s
|
||||||
|
__params -- array with params
|
||||||
|
|
||||||
|
return -- new query
|
||||||
|
"""
|
||||||
|
|
||||||
|
for i in __params:
|
||||||
|
escaped = self.connection.escape(i)
|
||||||
|
__query = __query.replace("?", str(escaped), 1)
|
||||||
|
|
||||||
|
return __query
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, __query, __params = None):
|
||||||
|
"""
|
||||||
|
Execute a SQL query
|
||||||
|
|
||||||
|
__query -- query, can contain ?s
|
||||||
|
__params -- array with params. Optional
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
with self.connection.cursor() as cursor:
|
||||||
|
try:
|
||||||
|
# Bind params if needed
|
||||||
|
if __params != None:
|
||||||
|
__query = self.bindParams(__query, __params)
|
||||||
|
|
||||||
|
# Execute the query
|
||||||
|
cursor.execute(__query)
|
||||||
|
finally:
|
||||||
|
# Close this connection
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def fetch(self, __query, __params = None, __all = False):
|
||||||
|
"""
|
||||||
|
Fetch the first (or all) element(s) of SQL query result
|
||||||
|
|
||||||
|
__query -- query, can contain ?s
|
||||||
|
__params -- array with params. Optional
|
||||||
|
__all -- if true, will fetch all values. Same as fetchAll
|
||||||
|
|
||||||
|
return -- dictionary with result data or False if failed
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
with self.connection.cursor() as cursor:
|
||||||
|
try:
|
||||||
|
# Bind params if needed
|
||||||
|
if __params != None:
|
||||||
|
__query = self.bindParams(__query, __params)
|
||||||
|
|
||||||
|
# Execute the query with binded params
|
||||||
|
cursor.execute(__query)
|
||||||
|
|
||||||
|
# Get first result and return it
|
||||||
|
if __all == False:
|
||||||
|
return cursor.fetchone()
|
||||||
|
else:
|
||||||
|
return cursor.fetchall()
|
||||||
|
finally:
|
||||||
|
# Close this connection
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def fetchAll(self, __query, __params = None):
|
||||||
|
"""
|
||||||
|
Fetch the all elements of SQL query result
|
||||||
|
|
||||||
|
__query -- query, can contain ?s
|
||||||
|
__params -- array with params. Optional
|
||||||
|
|
||||||
|
return -- dictionary with result data
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.fetch(__query, __params, True)
|
||||||
|
|
||||||
|
def pingLoop(self):
|
||||||
|
"""
|
||||||
|
Pings MySQL server. We need to ping/execute a query at least once every 8 hours
|
||||||
|
or the connection will die.
|
||||||
|
If called once, will recall after 30 minutes and so on, forever
|
||||||
|
CALL THIS FUNCTION ONLY ONCE!
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Default loop time
|
||||||
|
time = self.pingTime
|
||||||
|
|
||||||
|
# Make sure the connection is alive
|
||||||
|
try:
|
||||||
|
# Try to ping and reconnect if not connected
|
||||||
|
self.connection.ping()
|
||||||
|
if self.disconnected == True:
|
||||||
|
# If we were disconnected, set disconnected to false and print message
|
||||||
|
self.disconnected = False
|
||||||
|
consoleHelper.printColored("> Reconnected to MySQL server!", bcolors.GREEN)
|
||||||
|
except:
|
||||||
|
# Can't ping MySQL server. Show error and call loop in 5 seconds
|
||||||
|
consoleHelper.printColored("[!] CRITICAL!! MySQL connection died! Make sure your MySQL server is running! Checking again in 5 seconds...", bcolors.RED)
|
||||||
|
self.disconnected = True
|
||||||
|
time = 5
|
||||||
|
|
||||||
|
# Schedule a new check (endless loop)
|
||||||
|
threading.Timer(time, self.pingLoop).start()
|
58
exceptions.py
Normal file
58
exceptions.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
"""Bancho exceptions"""
|
||||||
|
# TODO: Prints in exceptions
|
||||||
|
class loginFailedException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class loginBannedException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class tokenNotFoundException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class channelNoPermissionsException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class channelUnknownException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class channelModeratedException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class noAdminException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class commandSyntaxException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class banchoConfigErrorException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class banchoMaintenanceException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class moderatedPMException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class userNotFoundException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class alreadyConnectedException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class stopSpectating(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class matchWrongPasswordException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class matchNotFoundException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class matchJoinErrorException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class matchCreateError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class banchoRestartingException(Exception):
|
||||||
|
pass
|
55
fokabot.py
Normal file
55
fokabot.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
"""FokaBot related functions"""
|
||||||
|
import userHelper
|
||||||
|
import glob
|
||||||
|
import actions
|
||||||
|
import serverPackets
|
||||||
|
import fokabotCommands
|
||||||
|
|
||||||
|
def connect():
|
||||||
|
"""Add FokaBot to connected users and send userpanel/stats packet to everyone"""
|
||||||
|
|
||||||
|
token = glob.tokens.addToken(999)
|
||||||
|
token.actionID = actions.idle
|
||||||
|
glob.tokens.enqueueAll(serverPackets.userPanel(999))
|
||||||
|
glob.tokens.enqueueAll(serverPackets.userStats(999))
|
||||||
|
|
||||||
|
def disconnect():
|
||||||
|
"""Remove FokaBot from connected users"""
|
||||||
|
|
||||||
|
glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(999))
|
||||||
|
|
||||||
|
def fokabotResponse(fro, chan, message):
|
||||||
|
"""
|
||||||
|
Check if a message has triggered fokabot (and return its response)
|
||||||
|
|
||||||
|
fro -- sender username (for permissions stuff with admin commands)
|
||||||
|
chan -- channel name
|
||||||
|
message -- message
|
||||||
|
|
||||||
|
return -- fokabot's response string or False
|
||||||
|
"""
|
||||||
|
|
||||||
|
for i in fokabotCommands.commands:
|
||||||
|
# Loop though all commands
|
||||||
|
if i["trigger"] in message:
|
||||||
|
# message has triggered a command
|
||||||
|
|
||||||
|
# Make sure the user has right permissions
|
||||||
|
if i["minRank"] > 1:
|
||||||
|
# Get rank from db only if minrank > 1, so we save some CPU
|
||||||
|
if userHelper.getRankPrivileges(userHelper.getID(fro)) < i["minRank"]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check argument number
|
||||||
|
message = message.split(" ")
|
||||||
|
if i["syntax"] != "" and len(message) <= len(i["syntax"].split(" ")):
|
||||||
|
return "Wrong syntax: {} {}".format(i["trigger"], i["syntax"])
|
||||||
|
|
||||||
|
# Return response or execute callback
|
||||||
|
if i["callback"] == None:
|
||||||
|
return i["response"]
|
||||||
|
else:
|
||||||
|
return i["callback"](fro, chan, message[1:])
|
||||||
|
|
||||||
|
# No commands triggered
|
||||||
|
return False
|
355
fokabotCommands.py
Normal file
355
fokabotCommands.py
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
import fokabot
|
||||||
|
import random
|
||||||
|
import glob
|
||||||
|
import serverPackets
|
||||||
|
import exceptions
|
||||||
|
import userHelper
|
||||||
|
import time
|
||||||
|
import systemHelper
|
||||||
|
|
||||||
|
"""
|
||||||
|
Commands callbacks
|
||||||
|
|
||||||
|
Must have fro, chan and messages as arguments
|
||||||
|
fro -- name of who triggered the command
|
||||||
|
chan -- channel where the message was sent
|
||||||
|
message -- list containing arguments passed from the message
|
||||||
|
[0] = first argument
|
||||||
|
[1] = second argument
|
||||||
|
. . .
|
||||||
|
|
||||||
|
return the message or **False** if there's no response by the bot
|
||||||
|
"""
|
||||||
|
|
||||||
|
def faq(fro, chan, message):
|
||||||
|
if message[0] == "rules":
|
||||||
|
return "Please make sure to check (Ripple's rules)[http://ripple.moe/?p=23]."
|
||||||
|
elif message[0] == "rules":
|
||||||
|
return "Please make sure to check (Ripple's rules)[http://ripple.moe/?p=23]."
|
||||||
|
elif message[0] == "swearing":
|
||||||
|
return "Please don't abuse swearing"
|
||||||
|
elif message[0] == "spam":
|
||||||
|
return "Please don't spam"
|
||||||
|
elif message[0] == "offend":
|
||||||
|
return "Please don't offend other players"
|
||||||
|
elif message[0] == "github":
|
||||||
|
return "(Ripple's Github page!)[https://github.com/osuripple/ripple]"
|
||||||
|
elif message[0] == "discord":
|
||||||
|
return "(Join Ripple's Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]"
|
||||||
|
elif message[0] == "blog":
|
||||||
|
return "You can find the latest Ripple news on the (blog)[https://ripple.moe/blog/]!"
|
||||||
|
elif message[0] == "changelog":
|
||||||
|
return "Check the (changelog)[https://ripple.moe/index.php?p=17] !"
|
||||||
|
elif message[0] == "status":
|
||||||
|
return "Check the server status (here!)[https://ripple.moe/index.php?p=27]"
|
||||||
|
|
||||||
|
def roll(fro, chan, message):
|
||||||
|
maxPoints = 100
|
||||||
|
if len(message) >= 1:
|
||||||
|
if message[0].isdigit() == True and int(message[0]) > 0:
|
||||||
|
maxPoints = int(message[0])
|
||||||
|
|
||||||
|
points = random.randrange(0,maxPoints)
|
||||||
|
return "{} rolls {} points!".format(fro, str(points))
|
||||||
|
|
||||||
|
def ask(fro, chan, message):
|
||||||
|
return random.choice(["yes", "no", "maybe"])
|
||||||
|
|
||||||
|
def alert(fro, chan, message):
|
||||||
|
glob.tokens.enqueueAll(serverPackets.notification(' '.join(message[:])))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def moderated(fro, chan, message):
|
||||||
|
try:
|
||||||
|
# Make sure we are in a channel and not PM
|
||||||
|
if chan.startswith("#") == False:
|
||||||
|
raise exceptions.moderatedPMException
|
||||||
|
|
||||||
|
# Get on/off
|
||||||
|
enable = True
|
||||||
|
if len(message) >= 1:
|
||||||
|
if message[0] == "off":
|
||||||
|
enable = False
|
||||||
|
|
||||||
|
# Turn on/off moderated mode
|
||||||
|
glob.channels.channels[chan].moderated = enable
|
||||||
|
return "This channel is {} in moderated mode!".format("now" if enable else "no longer")
|
||||||
|
except exceptions.moderatedPMException:
|
||||||
|
return "You are trying to put a private chat in moderated mode. Are you serious?!? You're fired."
|
||||||
|
|
||||||
|
def kickAll(fro, chan, message):
|
||||||
|
# Kick everyone but mods/admins
|
||||||
|
toKick = []
|
||||||
|
for key, value in glob.tokens.tokens.items():
|
||||||
|
if value.rank < 3:
|
||||||
|
toKick.append(key)
|
||||||
|
|
||||||
|
# Loop though users to kick (we can't change dictionary size while iterating)
|
||||||
|
for i in toKick:
|
||||||
|
if i in glob.tokens.tokens:
|
||||||
|
glob.tokens.tokens[i].kick()
|
||||||
|
|
||||||
|
return "Whoops! Rip everyone."
|
||||||
|
|
||||||
|
def kick(fro, chan, message):
|
||||||
|
# Get parameters
|
||||||
|
target = message[0].replace("_", " ")
|
||||||
|
|
||||||
|
# Get target token and make sure is connected
|
||||||
|
targetToken = glob.tokens.getTokenFromUsername(target)
|
||||||
|
if targetToken == None:
|
||||||
|
return "{} is not online".format(target)
|
||||||
|
|
||||||
|
# Kick user
|
||||||
|
targetToken.kick()
|
||||||
|
|
||||||
|
# Bot response
|
||||||
|
return "{} has been kicked from the server.".format(target)
|
||||||
|
|
||||||
|
def fokabotReconnect(fro, chan, message):
|
||||||
|
# Check if fokabot is already connected
|
||||||
|
if glob.tokens.getTokenFromUserID(999) != None:
|
||||||
|
return"Fokabot is already connected to Bancho"
|
||||||
|
|
||||||
|
# Fokabot is not connected, connect it
|
||||||
|
fokabot.connect()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def silence(fro, chan, message):
|
||||||
|
for i in message:
|
||||||
|
i = i.lower()
|
||||||
|
target = message[0].replace("_", " ")
|
||||||
|
amount = message[1]
|
||||||
|
unit = message[2]
|
||||||
|
reason = ' '.join(message[3:])
|
||||||
|
|
||||||
|
# Get target user ID
|
||||||
|
targetUserID = userHelper.getID(target)
|
||||||
|
|
||||||
|
# Make sure the user exists
|
||||||
|
if targetUserID == False:
|
||||||
|
return "{}: user not found".format(target)
|
||||||
|
|
||||||
|
# Calculate silence seconds
|
||||||
|
if unit == 's':
|
||||||
|
silenceTime = int(amount)
|
||||||
|
elif unit == 'm':
|
||||||
|
silenceTime = int(amount)*60
|
||||||
|
elif unit == 'h':
|
||||||
|
silenceTime = int(amount)*3600
|
||||||
|
elif unit == 'd':
|
||||||
|
silenceTime = int(amount)*86400
|
||||||
|
else:
|
||||||
|
return "Invalid time unit (s/m/h/d)."
|
||||||
|
|
||||||
|
# Max silence time is 7 days
|
||||||
|
if silenceTime > 604800:
|
||||||
|
return "Invalid silence time. Max silence time is 7 days."
|
||||||
|
|
||||||
|
# Calculate silence end time
|
||||||
|
endTime = int(time.time())+silenceTime
|
||||||
|
|
||||||
|
# Update silence end in db
|
||||||
|
userHelper.silence(targetUserID, endTime, reason)
|
||||||
|
|
||||||
|
# Send silence packet to target if he's connected
|
||||||
|
targetToken = glob.tokens.getTokenFromUsername(target)
|
||||||
|
if targetToken != None:
|
||||||
|
targetToken.enqueue(serverPackets.silenceEndTime(silenceTime))
|
||||||
|
|
||||||
|
return "{} has been silenced for the following reason: {}".format(target, reason)
|
||||||
|
|
||||||
|
def removeSilence(fro, chan, message):
|
||||||
|
# Get parameters
|
||||||
|
for i in message:
|
||||||
|
i = i.lower()
|
||||||
|
target = message[0].replace("_", " ")
|
||||||
|
|
||||||
|
# Make sure the user exists
|
||||||
|
targetUserID = userHelper.getID(target)
|
||||||
|
if targetUserID == False:
|
||||||
|
return "{}: user not found".format(target)
|
||||||
|
|
||||||
|
# Reset user silence time and reason in db
|
||||||
|
userHelper.silence(targetUserID, 0, "")
|
||||||
|
|
||||||
|
# Send new silence end packet to user if he's online
|
||||||
|
targetToken = glob.tokens.getTokenFromUsername(target)
|
||||||
|
if targetToken != None:
|
||||||
|
targetToken.enqueue(serverPackets.silenceEndTime(0))
|
||||||
|
|
||||||
|
return "{}'s silence reset".format(target)
|
||||||
|
|
||||||
|
def restartShutdown(restart):
|
||||||
|
"""Restart (if restart = True) or shutdown (if restart = False) pep.py safely"""
|
||||||
|
msg = "We are performing some maintenance. Bancho will {} in 5 seconds. Thank you for your patience.".format("restart" if restart else "shutdown")
|
||||||
|
systemHelper.scheduleShutdown(5, restart, msg)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def systemRestart(fro, chan, message):
|
||||||
|
return restartShutdown(True)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
# Send new channels and new bottom icon to everyone
|
||||||
|
glob.tokens.enqueueAll(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
|
||||||
|
glob.tokens.enqueueAll(serverPackets.channelInfoEnd())
|
||||||
|
for key, _ in glob.channels.channels.items():
|
||||||
|
glob.tokens.enqueueAll(serverPackets.channelInfo(key))
|
||||||
|
|
||||||
|
return "Bancho settings reloaded!"
|
||||||
|
|
||||||
|
def systemMaintenance(fro, chan, message):
|
||||||
|
# Turn on/off bancho maintenance
|
||||||
|
maintenance = True
|
||||||
|
|
||||||
|
# Get on/off
|
||||||
|
if len(message) >= 2:
|
||||||
|
if message[1] == "off":
|
||||||
|
maintenance = False
|
||||||
|
|
||||||
|
# Set new maintenance value in bancho_settings table
|
||||||
|
glob.banchoConf.setMaintenance(maintenance)
|
||||||
|
|
||||||
|
if maintenance == True:
|
||||||
|
# We have turned on maintenance mode
|
||||||
|
# Users that will be disconnected
|
||||||
|
who = []
|
||||||
|
|
||||||
|
# Disconnect everyone but mod/admins
|
||||||
|
for _, value in glob.tokens.tokens.items():
|
||||||
|
if value.rank < 3:
|
||||||
|
who.append(value.userID)
|
||||||
|
|
||||||
|
glob.tokens.enqueueAll(serverPackets.notification("Our bancho server is in maintenance mode. Please try to login again later."))
|
||||||
|
glob.tokens.multipleEnqueue(serverPackets.loginError(), who)
|
||||||
|
msg = "The server is now in maintenance mode!"
|
||||||
|
else:
|
||||||
|
# We have turned off maintenance mode
|
||||||
|
# Send message if we have turned off maintenance mode
|
||||||
|
msg = "The server is no longer in maintenance mode!"
|
||||||
|
|
||||||
|
# Chat output
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def systemStatus(fro, chan, message):
|
||||||
|
# Print some server info
|
||||||
|
data = systemHelper.getSystemInfo()
|
||||||
|
|
||||||
|
# Final message
|
||||||
|
msg = "=== PEP.PY STATS ===\n"
|
||||||
|
msg += "Running pep.py server\n"
|
||||||
|
msg += "Webserver: {}\n".format(data["webServer"])
|
||||||
|
msg += "\n"
|
||||||
|
msg += "=== BANCHO STATS ===\n"
|
||||||
|
msg += "Connected users: {}\n".format(str(data["connectedUsers"]))
|
||||||
|
msg += "\n"
|
||||||
|
msg += "=== SYSTEM STATS ===\n"
|
||||||
|
msg += "CPU: {}%\n".format(str(data["cpuUsage"]))
|
||||||
|
msg += "RAM: {}GB/{}GB\n".format(str(data["usedMemory"]), str(data["totalMemory"]))
|
||||||
|
if data["unix"] == True:
|
||||||
|
msg += "Load average: {}/{}/{}\n".format(str(data["loadAverage"][0]), str(data["loadAverage"][1]), str(data["loadAverage"][2]))
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
"""
|
||||||
|
Commands list
|
||||||
|
|
||||||
|
trigger: message that triggers the command
|
||||||
|
callback: function to call when the command is triggered. Optional.
|
||||||
|
response: text to return when the command is triggered. Optional.
|
||||||
|
syntax: command syntax. Arguments must be separated by spaces (eg: <arg1> <arg2>)
|
||||||
|
minRank: minimum rank to execute that command. Optional (default = 1)
|
||||||
|
|
||||||
|
You MUST set trigger and callback/response, or the command won't work.
|
||||||
|
"""
|
||||||
|
commands = [
|
||||||
|
{
|
||||||
|
"trigger": "!roll",
|
||||||
|
"callback": roll
|
||||||
|
}, {
|
||||||
|
"trigger": "!faq",
|
||||||
|
"syntax": "<name>",
|
||||||
|
"callback": faq
|
||||||
|
}, {
|
||||||
|
"trigger": "!report",
|
||||||
|
"response": "Report command isn't here yet :c"
|
||||||
|
}, {
|
||||||
|
"trigger": "!help",
|
||||||
|
"response": "Click (here)[https://ripple.moe/index.php?p=16&id=4] for FokaBot's full command list"
|
||||||
|
}, {
|
||||||
|
"trigger": "!ask",
|
||||||
|
"syntax": "<question>",
|
||||||
|
"callback": ask
|
||||||
|
}, {
|
||||||
|
"trigger": "!mm00",
|
||||||
|
"response": random.choice(["meme", "MA MAURO ESISTE?"])
|
||||||
|
}, {
|
||||||
|
"trigger": "!alert",
|
||||||
|
"syntax": "<message>",
|
||||||
|
"minRank": 4,
|
||||||
|
"callback": alert
|
||||||
|
}, {
|
||||||
|
"trigger": "!moderated",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": moderated
|
||||||
|
}, {
|
||||||
|
"trigger": "!kickall",
|
||||||
|
"minRank": 4,
|
||||||
|
"callback": kickAll
|
||||||
|
}, {
|
||||||
|
"trigger": "!kick",
|
||||||
|
"syntax": "<target>",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": kick
|
||||||
|
}, {
|
||||||
|
"trigger": "!fokabot reconnect",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": fokabotReconnect
|
||||||
|
}, {
|
||||||
|
"trigger": "!silence",
|
||||||
|
"syntax": "<target> <amount> <unit(s/m/h/d)> <reason>",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": silence
|
||||||
|
}, {
|
||||||
|
"trigger": "!removesilence",
|
||||||
|
"syntax": "<target>",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": removeSilence
|
||||||
|
}, {
|
||||||
|
"trigger": "!system restart",
|
||||||
|
"minRank": 4,
|
||||||
|
"callback": systemRestart
|
||||||
|
}, {
|
||||||
|
"trigger": "!system shutdown",
|
||||||
|
"minRank": 4,
|
||||||
|
"callback": systemShutdown
|
||||||
|
}, {
|
||||||
|
"trigger": "!system reload",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": systemReload
|
||||||
|
}, {
|
||||||
|
"trigger": "!system maintenance",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": systemMaintenance
|
||||||
|
}, {
|
||||||
|
"trigger": "!system status",
|
||||||
|
"minRank": 3,
|
||||||
|
"callback": systemStatus
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# Commands list default values
|
||||||
|
for cmd in commands:
|
||||||
|
cmd.setdefault("syntax", "")
|
||||||
|
cmd.setdefault("minRank", 1)
|
||||||
|
cmd.setdefault("callback", None)
|
||||||
|
cmd.setdefault("response", "u w0t m8?")
|
10
friendAddEvent.py
Normal file
10
friendAddEvent.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import userHelper
|
||||||
|
import clientPackets
|
||||||
|
|
||||||
|
def handle(userToken, packetData):
|
||||||
|
# Friend add packet
|
||||||
|
packetData = clientPackets.addRemoveFriend(packetData)
|
||||||
|
userHelper.addFriend(userToken.userID, packetData["friendID"])
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
print("> {} have added {} to their friends".format(userToken.username, str(packetData["friendID"])))
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user