master #28

Closed
opened 2020-11-26 16:53:57 +00:00 by Elnzburn · 0 comments
Elnzburn commented 2020-11-26 16:53:57 +00:00 (Migrated from zxq.co)

Original pull request patch:

From 38adda759baac3a3773d4de5c421d64a742790b4 Mon Sep 17 00:00:00 2001
From: osu!thailand <43190206+osuthailand@users.noreply.github.com>
Date: Wed, 6 Mar 2019 18:06:57 +0700
Subject: [PATCH 01/88] Bot name patch + Match room fix

---
 constants/fokabotCommands.py         |  47 ++-
 constants/serverPackets.py           |   6 +-
 events/changeMatchSettingsEvent.py   |   8 +-
 events/createMatchEvent.py           |   8 +-
 events/setAwayMessageEvent.py        |   6 +-
 handlers/apiFokabotMessageHandler.py |   2 +-
 handlers/chatHelper.py               | 433 +++++++++++++++++++++++++++
 helpers/chatHelper.py                |  16 +-
 objects/channel.py                   |   2 +-
 objects/fokabot.py                   |   1 +
 objects/glob.py                      |   2 +
 objects/match.py                     |  20 +-
 objects/osuToken.py                  |   8 +-
 13 files changed, 502 insertions(+), 57 deletions(-)
 create mode 100644 handlers/chatHelper.py

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index d4b6b72..ff97fae 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -52,15 +52,14 @@ def instantRestart(fro, chan, message):
 def faq(fro, chan, message):
 	# TODO: Unhardcode this
 	messages = {
-		"rules": "Please make sure to check (Ripple's rules)[https://ripple.moe/doc/rules].",
+		"rules": "Please make sure to check (osu!thailand's rules)[https://bigtu.vip/doc/rules].",
 		"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://blog.ripple.moe]!",
-		"changelog": "Check the (changelog)[https://ripple.moe/changelog] !",
-		"status": "Check the server status (here!)[https://status.ripple.moe]",
+		"github": "(osu!Ainu's Github page!)[https://github.com/osuripple/ripple]",
+		"discord": "(Join Ainu Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]",
+		"changelog": "Check the (changelog)[https://bigtu.vip/changelog] !",
+		"status": "Check the server status (here!)[https://status.bigtu.vip]",
 		"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."
@@ -137,7 +136,7 @@ def kickAll(fro, chan, message):
 def kick(fro, chan, message):
 	# Get parameters
 	target = message[0].lower()
-	if target == "fokabot":
+	if target == glob.BOT_NAME.lower():
 		return "Nope."
 
 	# Get target token and make sure is connected
@@ -153,11 +152,11 @@ def kick(fro, chan, message):
 	return "{} has been kicked from the server.".format(target)
 
 def fokabotReconnect(fro, chan, message):
-	# Check if fokabot is already connected
+	# Check if the bot is already connected
 	if glob.tokens.getTokenFromUserID(999) is not None:
-		return "Fokabot is already connected to Bancho"
+		return "{} is already connected to Bancho".format(glob.BOT_NAME)
 
-	# Fokabot is not connected, connect it
+	# Bot is not connected, connect it
 	fokabot.connect()
 	return False
 
@@ -371,12 +370,12 @@ def systemStatus(fro, chan, message):
 	# Final message
 	letsVersion = glob.redis.get("lets:version")
 	if letsVersion is None:
-		letsVersion = "\_(xd)_/"
+		letsVersion = "\_(-w-)_/"
 	else:
 		letsVersion = letsVersion.decode("utf-8")
 	msg = "pep.py bancho server v{}\n".format(glob.VERSION)
 	msg += "LETS scores server v{}\n".format(letsVersion)
-	msg += "made by the Ripple team\n"
+	msg += "made by the osu!thailand\n"
 	msg += "\n"
 	msg += "=== BANCHO STATS ===\n"
 	msg += "Connected users: {}\n".format(data["connectedUsers"])
@@ -620,7 +619,7 @@ def tillerinoLast(fro, chan, message):
 		rank = generalUtils.getRank(data["play_mode"], data["mods"], data["accuracy"],
 									data["300_count"], data["100_count"], data["50_count"], data["misses_count"])
 
-		ifPlayer = "{0} | ".format(fro) if chan != "FokaBot" else ""
+		ifPlayer = "{0} | ".format(fro) if chan != glob.BOT_NAME else ""
 		ifFc = " (FC)" if data["max_combo"] == data["fc"] else " {0}x/{1}x".format(data["max_combo"], data["fc"])
 		beatmapLink = "[http://osu.ppy.sh/b/{1} {0}]".format(data["sn"], data["bid"])
 
@@ -738,7 +737,7 @@ def report(fro, chan, message):
 		target = chat.fixUsernameForBancho(target)
 
 		# Make sure the target is not foka
-		if target.lower() == "fokabot":
+		if target.lower() == glob.BOT_NAME.lower():
 			raise exceptions.invalidUserException()
 
 		# Make sure the user exists
@@ -762,10 +761,10 @@ def report(fro, chan, message):
 		adminMsg = "{user} has reported {target} for {reason} ({info})".format(user=fro, target=target, reason=reason, info=additionalInfo)
 
 		# Log report in #admin and on discord
-		chat.sendMessage("FokaBot", "#admin", adminMsg)
+		chat.sendMessage(glob.BOT_NAME, "#admin", adminMsg)
 		log.warning(adminMsg, discord="cm")
 	except exceptions.invalidUserException:
-		msg = "Hello, FokaBot here! You can't report me. I won't forget what you've tried to do. Watch out."
+		msg = "Hello, {} here! You can't report me. I won't forget what you've tried to do. Watch out."
 	except exceptions.invalidArgumentsException:
 		msg = "Invalid report command syntax. To report an user, click on it and select 'Report user'."
 	except exceptions.userNotFoundException:
@@ -779,7 +778,7 @@ def report(fro, chan, message):
 			token = glob.tokens.getTokenFromUsername(fro)
 			if token is not None:
 				if token.irc:
-					chat.sendMessage("FokaBot", fro, msg)
+					chat.sendMessage(glob.BOT_NAME, fro, msg)
 				else:
 					token.enqueue(serverPackets.notification(msg))
 	return False
@@ -891,10 +890,10 @@ def multiplayer(fro, chan, message):
 			matchID = getMatchIDFromChannel(chan)
 			success = glob.matches.matches[matchID].start()
 			if not success:
-				chat.sendMessage("FokaBot", chan, "Couldn't start match. Make sure there are enough players and "
+				chat.sendMessage(glob.BOT_NAME, chan, "Couldn't start match. Make sure there are enough players and "
 												  "teams are valid. The match has been unlocked.")
 			else:
-				chat.sendMessage("FokaBot", chan, "Have fun!")
+				chat.sendMessage(glob.BOT_NAME, chan, "Have fun!")
 
 
 		def _decreaseTimer(t):
@@ -902,7 +901,7 @@ def multiplayer(fro, chan, message):
 				_start()
 			else:
 				if t % 10 == 0 or t <= 5:
-					chat.sendMessage("FokaBot", chan, "Match starts in {} seconds.".format(t))
+					chat.sendMessage(glob.BOT_NAME, chan, "Match starts in {} seconds.".format(t))
 				threading.Timer(1.00, _decreaseTimer, [t - 1]).start()
 
 		if len(message) < 2 or not message[1].isdigit():
@@ -949,8 +948,8 @@ def multiplayer(fro, chan, message):
 			raise exceptions.invalidUserException("That user is not connected to bancho right now.")
 		_match = glob.matches.matches[getMatchIDFromChannel(chan)]
 		_match.invite(999, userID)
-		token.enqueue(serverPackets.notification("Please accept the invite you've just received from FokaBot to "
-												 "enter your tourney match."))
+		token.enqueue(serverPackets.notification("Please accept the invite you've just received from {} to "
+												 "enter your tourney match.".format(glob.BOT_NAME)))
 		return "An invite to this match has been sent to {}".format(username)
 
 	def mpMap():
@@ -1243,7 +1242,7 @@ commands = [
 		"callback": report
 	}, {
 		"trigger": "!help",
-		"response": "Click (here)[https://ripple.moe/index.php?p=16&id=4] for FokaBot's full command list"
+		"response": "Click (here)[https://bigtu.vip/index.php?p=16&id=4] for full command list"
 	}, #{
 		#"trigger": "!ask",
 		#"syntax": "<question>",
@@ -1276,7 +1275,7 @@ commands = [
 		"privileges": privileges.ADMIN_KICK_USERS,
 		"callback": kick
 	}, {
-		"trigger": "!fokabot reconnect",
+		"trigger": "!bot reconnect",
 		"privileges": privileges.ADMIN_MANAGE_SERVERS,
 		"callback": fokabotReconnect
 	}, {
diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 299d0b1..c386274 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -94,8 +94,12 @@ def userPanel(userID, force = False):
 	# Get username color according to rank
 	# Only admins and normal users are currently supported
 	userRank = 0
-	if username == "FokaBot":
+	if username == glob.BOT_NAME:
 		userRank |= userRanks.MOD
+	elif username == "Aoba":
+		userRank |= userRanks.PEPPY
+	elif username == "Natsue":
+		userRank |= userRanks.PEPPY
 	elif userUtils.isInPrivilegeGroup(userID, "developer"):
 		userRank |= userRanks.ADMIN
 	elif userUtils.isInPrivilegeGroup(userID, "chat mod"):
diff --git a/events/changeMatchSettingsEvent.py b/events/changeMatchSettingsEvent.py
index ac8d241..047b0ad 100644
--- a/events/changeMatchSettingsEvent.py
+++ b/events/changeMatchSettingsEvent.py
@@ -28,8 +28,8 @@ def handle(userToken, packetData):
 
 		# Some dank memes easter egg
 		memeTitles = [
-			"RWC 2020",
-			"Fokabot is a duck",
+			"OWC 2020",
+			"AC is a duck",
 			"Dank memes",
 			"1337ms Ping",
 			"Iscriviti a Xenotoze",
@@ -38,7 +38,7 @@ def handle(userToken, packetData):
 			"The brace is on fire",
 			"print_foot()",
 			"#FREEZEBARKEZ",
-			"Ripple devs are actually cats",
+			"osu!thailand devs are actually cats",
 			"Thank Mr Shaural",
 			"NEVER GIVE UP",
 			"T I E D  W I T H  U N I T E D",
@@ -49,7 +49,7 @@ def handle(userToken, packetData):
 			"TATOE",
 			"This is not your drama landfill.",
 			"I like cheese",
-			"NYO IS NOT A CAT HE IS A DO(N)G",
+			"AOBA IS NOT A CAT HE IS A DO(N)G",
 			"Datingu startuato"
 		]
 
diff --git a/events/createMatchEvent.py b/events/createMatchEvent.py
index 22d5aa9..66911aa 100644
--- a/events/createMatchEvent.py
+++ b/events/createMatchEvent.py
@@ -20,7 +20,7 @@ def handle(userToken, packetData):
 		# Create a match object
 		# 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
 		if matchID not in glob.matches.matches:
 			raise exceptions.matchCreateError()
@@ -29,6 +29,12 @@ def handle(userToken, packetData):
 			# Join that match
 			userToken.joinMatch(matchID)
 
+			# Multiplayer Room Patch
+			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()
diff --git a/events/setAwayMessageEvent.py b/events/setAwayMessageEvent.py
index f69a6e6..6b43e93 100644
--- a/events/setAwayMessageEvent.py
+++ b/events/setAwayMessageEvent.py
@@ -1,7 +1,7 @@
 from common.log import logUtils as log
 from constants import clientPackets
 from constants import serverPackets
-
+from objects import glob
 
 def handle(userToken, packetData):
 	# get token data
@@ -13,10 +13,10 @@ def handle(userToken, packetData):
 	# Set token away message
 	userToken.awayMessage = packetData["awayMessage"]
 
-	# Send private message from fokabot
+	# Send private message from the bot
 	if packetData["awayMessage"] == "":
 		fokaMessage = "Your away message has been reset"
 	else:
 		fokaMessage = "Your away message is now: {}".format(packetData["awayMessage"])
-	userToken.enqueue(serverPackets.sendMessage("FokaBot", username, fokaMessage))
+	userToken.enqueue(serverPackets.sendMessage(glob.BOT_NAME, username, fokaMessage))
 	log.info("{} has changed their away message to: {}".format(username, packetData["awayMessage"]))
diff --git a/handlers/apiFokabotMessageHandler.py b/handlers/apiFokabotMessageHandler.py
index 85e2729..eed14ef 100644
--- a/handlers/apiFokabotMessageHandler.py
+++ b/handlers/apiFokabotMessageHandler.py
@@ -28,7 +28,7 @@ class handler(requestsManager.asyncRequestHandler):
 				raise exceptions.invalidArgumentsException()
 
 			chatHelper.sendMessage(
-				"FokaBot",
+				glob.BOT_NAME,
 				self.get_argument("to").encode().decode("ASCII", "ignore"),
 				self.get_argument("msg").encode().decode("ASCII", "ignore")
 			)
diff --git a/handlers/chatHelper.py b/handlers/chatHelper.py
new file mode 100644
index 0000000..347755c
--- /dev/null
+++ b/handlers/chatHelper.py
@@ -0,0 +1,433 @@
+from common.log import logUtils as log
+from common.ripple import userUtils
+from constants import exceptions
+from constants import messageTemplates
+from constants import serverPackets
+from events import logoutEvent
+from objects import fokabot
+from objects import glob
+
+
+def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=False):
+	"""
+	Join a channel
+
+	:param userID: user ID of the user that joins the channel. Optional. token can be used instead.
+	:param token: user token object of user that joins the channel. Optional. userID can be used instead.
+	:param channel: channel name
+	:param 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:
+		# Get token if not defined
+		if token is None:
+			token = glob.tokens.getTokenFromUserID(userID)
+			# Make sure the token exists
+			if token is None:
+				raise exceptions.userNotFoundException
+		else:
+			token = token
+
+		# Normal channel, do check stuff
+		# Make sure the channel exists
+		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)
+
+		# Send channel joined (IRC)
+		if glob.irc and not toIRC:
+			glob.ircServer.banchoJoinChannel(token.username, channel)
+
+		# Console output
+		log.info("{} joined channel {}".format(token.username, channel))
+
+		# IRC code return
+		return 0
+	except exceptions.channelNoPermissionsException:
+		log.warning("{} attempted to join channel {}, but they have no read permissions".format(token.username, channel))
+		return 403
+	except exceptions.channelUnknownException:
+		log.warning("{} attempted to join an unknown channel ({})".format(token.username, channel))
+		return 403
+	except exceptions.userAlreadyInChannelException:
+		log.warning("User {} already in channel {}".format(token.username, channel))
+		return 403
+	except exceptions.userNotFoundException:
+		log.warning("User not connected to IRC/Bancho")
+		return 403	# idk
+
+def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False, force=False):
+	"""
+	Part a channel
+
+	:param userID: user ID of the user that parts the channel. Optional. token can be used instead.
+	:param token: user token object of user that parts 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. 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:
+		# Make sure the client is not drunk and sends partChannel when closing a PM tab
+		if not channel.startswith("#"):
+			return
+
+		# Get token if not defined
+		if token is None:
+			token = glob.tokens.getTokenFromUserID(userID)
+			# Make sure the token exists
+			if token is None:
+				raise exceptions.userNotFoundException()
+		else:
+			token = token
+
+		# Determine internal/client name if needed
+		# (toclient is used clientwise for #multiplayer and #spectator channels)
+		channelClient = channel
+		if channel == "#spectator":
+			if token.spectating is None:
+				s = userID
+			else:
+				s = token.spectatingUserID
+			channel = "#spect_{}".format(s)
+		elif channel == "#multiplayer":
+			channel = "#multi_{}".format(token.matchID)
+		elif channel.startswith("#spect_"):
+			channelClient = "#spectator"
+		elif channel.startswith("#multi_"):
+			channelClient = "#multiplayer"
+
+		# Make sure the channel exists
+		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)
+		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:
+				glob.channels.removeChannel(channelObject.name)
+
+		# Force close tab if needed
+		# NOTE: Maybe always needed, will check later
+		if kick:
+			token.enqueue(serverPackets.channelKicked(channelClient))
+
+		# IRC part
+		if glob.irc and toIRC:
+			glob.ircServer.banchoPartChannel(token.username, channel)
+
+		# Console output
+		log.info("{} parted channel {} ({})".format(token.username, channel, channelClient))
+
+		# Return IRC code
+		return 0
+	except exceptions.channelUnknownException:
+		log.warning("{} attempted to part an unknown channel ({})".format(token.username, channel))
+		return 403
+	except exceptions.userNotInChannelException:
+		log.warning("{} attempted to part {}, but he's not in that channel".format(token.username, channel))
+		return 442
+	except exceptions.userNotFoundException:
+		log.warning("User not connected to IRC/Bancho")
+		return 442	# idk
+
+def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
+	"""
+	Send a message to osu!bancho and IRC server
+
+	:param fro: sender username. Optional. token can be used instead
+	:param to: receiver channel (if starts with #) or username
+	:param message: text of the message
+	:param token: sender token object. Optional. fro can be used instead
+	:param toIRC: if True, send the message to IRC. If False, send it to Bancho only. Default: True
+	:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
+	"""
+	try:
+		#tokenString = ""
+		# Get token object if not passed
+		if token is None:
+			token = glob.tokens.getTokenFromUsername(fro)
+			if token is None:
+				raise exceptions.userNotFoundException()
+		else:
+			# token object alredy passed, get its string and its username (fro)
+			fro = token.username
+			#tokenString = token.token
+
+		# Make sure this is not a tournament client
+		# if token.tournament:
+		# 	raise exceptions.userTournamentException()
+
+		# Make sure the user is not in restricted mode
+		if token.restricted:
+			raise exceptions.userRestrictedException()
+
+		# Make sure the user is not silenced
+		if token.isSilenced():
+			raise exceptions.userSilencedException()
+
+		# Redirect !report to the bot
+		if message.startswith("!report"):
+			to = glob.BOT_NAME
+
+		# Determine internal name if needed
+		# (toclient is used clientwise for #multiplayer and #spectator channels)
+		toClient = to
+		if to == "#spectator":
+			if token.spectating is None:
+				s = token.userID
+			else:
+				s = token.spectatingUserID
+			to = "#spect_{}".format(s)
+		elif to == "#multiplayer":
+			to = "#multi_{}".format(token.matchID)
+		elif to.startswith("#spect_"):
+			toClient = "#spectator"
+		elif to.startswith("#multi_"):
+			toClient = "#multiplayer"
+
+		# Make sure the message is valid
+		if not message.strip():
+			raise exceptions.invalidArgumentsException()
+
+		# Truncate message if > 2048 characters
+		message = message[:2048]+"..." if len(message) > 2048 else message
+
+		# Check for word filters
+		message = glob.chatFilters.filterMessage(message)
+
+		# Build packet bytes
+		packet = serverPackets.sendMessage(token.username, toClient, message)
+
+		# Send the message
+		isChannel = to.startswith("#")
+		if isChannel:
+			# CHANNEL
+			# Make sure the channel exists
+			if to not in glob.channels.channels:
+				raise exceptions.channelUnknownException()
+
+			# Make sure the channel is not in moderated mode
+			if glob.channels.channels[to].moderated and not token.admin:
+				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:
+				raise exceptions.channelNoPermissionsException()
+
+			# Add message in buffer
+			token.addMessageInBuffer(to, message)
+
+			# Everything seems fine, build recipients list and send packet
+			glob.streams.broadcast("chat/{}".format(to), packet, but=[token.token])
+		else:
+			# USER
+			# Make sure recipient user is connected
+			recipientToken = glob.tokens.getTokenFromUsername(to)
+			if recipientToken is None:
+				raise exceptions.userNotFoundException()
+
+			# Make sure the recipient is not a tournament client
+			#if recipientToken.tournament:
+			#	raise exceptions.userTournamentException()
+
+			# Make sure the recipient is not restricted or we are your bot
+			if recipientToken.restricted 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
+
+			# Away check
+			if recipientToken.awayCheck(token.userID):
+				sendMessage(to, fro, "\x01ACTION is away: {}\x01".format(recipientToken.awayMessage))
+
+			# Check message templates (mods/admins only)
+			if message in messageTemplates.templates and token.admin:
+				sendMessage(fro, to, messageTemplates.templates[message])
+
+			# Everything seems fine, send packet
+			recipientToken.enqueue(packet)
+
+		# Send the message to IRC
+		if glob.irc and toIRC:
+			messageSplitInLines = message.encode("latin-1").decode("utf-8").split("\n")
+			for line in messageSplitInLines:
+				if line == messageSplitInLines[:1] and line == "":
+					continue
+				glob.ircServer.banchoMessage(fro, to, line)
+
+		# Spam protection (ignore your bot)
+		if token.userID > 999:
+			token.spamProtection()
+
+		# bot message
+		if isChannel 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)
+
+		# File and discord logs (public chat only)
+		if to.startswith("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")):
+			log.chat("{fro} @ {to}: {message}".format(fro=token.username, to=to, message=message.encode("latin-1").decode("utf-8")))
+			glob.schiavo.sendChatlog("**{fro} @ {to}:** {message}".format(fro=token.username, to=to, message=message.encode("latin-1").decode("utf-8")))
+		return 0
+	except exceptions.userSilencedException:
+		token.enqueue(serverPackets.silenceEndTime(token.getSilenceSecondsLeft()))
+		log.warning("{} tried to send a message during silence".format(token.username))
+		return 404
+	except exceptions.channelModeratedException:
+		log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(token.username, to))
+		return 404
+	except exceptions.channelUnknownException:
+		log.warning("{} tried to send a message to an unknown channel ({})".format(token.username, to))
+		return 403
+	except exceptions.channelNoPermissionsException:
+		log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(token.username, to))
+		return 404
+	except exceptions.userRestrictedException:
+		log.warning("{} tried to send a message {}, but the recipient is in restricted mode".format(token.username, to))
+		return 404
+	except exceptions.userTournamentException:
+		log.warning("{} tried to send a message {}, but the recipient is a tournament client".format(token.username, to))
+		return 404
+	except exceptions.userNotFoundException:
+		log.warning("User not connected to IRC/Bancho")
+		return 401
+	except exceptions.invalidArgumentsException:
+		log.warning("{} tried to send an invalid message to {}".format(token.username, to))
+		return 404
+
+
+""" IRC-Bancho Connect/Disconnect/Join/Part interfaces"""
+def fixUsernameForBancho(username):
+	"""
+	Convert username from IRC format (without spaces) to Bancho format (with spaces)
+
+	:param username: username to convert
+	:return: converted username
+	"""
+	# If there are no spaces or underscores in the name
+	# return it
+	if " " not in username and "_" not in username:
+		return username
+
+	# Exact match first
+	result = glob.db.fetch("SELECT id FROM users WHERE username = %s LIMIT 1", [username])
+	if result is not None:
+		return username
+
+	# Username not found, replace _ with space
+	return username.replace("_", " ")
+
+def fixUsernameForIRC(username):
+	"""
+	Convert an username from Bancho format to IRC format (underscores instead of spaces)
+
+	:param username: username to convert
+	:return: converted username
+	"""
+	return username.replace(" ", "_")
+
+def IRCConnect(username):
+	"""
+	Handle IRC login bancho-side.
+	Add token and broadcast login packet.
+
+	:param username: username
+	:return:
+	"""
+	userID = userUtils.getID(username)
+	if not userID:
+		log.warning("{} doesn't exist".format(username))
+		return
+	glob.tokens.deleteOldTokens(userID)
+	glob.tokens.addToken(userID, irc=True)
+	glob.streams.broadcast("main", serverPackets.userPanel(userID))
+	log.info("{} logged in from IRC".format(username))
+
+def IRCDisconnect(username):
+	"""
+	Handle IRC logout bancho-side.
+	Remove token and broadcast logout packet.
+
+	:param username: username
+	:return:
+	"""
+	token = glob.tokens.getTokenFromUsername(username)
+	if token is None:
+		log.warning("{} doesn't exist".format(username))
+		return
+	logoutEvent.handle(token)
+	log.info("{} disconnected from IRC".format(username))
+
+def IRCJoinChannel(username, channel):
+	"""
+	Handle IRC channel join bancho-side.
+
+	:param username: username
+	:param channel: channel name
+	:return: IRC return code
+	"""
+	userID = userUtils.getID(username)
+	if not userID:
+		log.warning("{} doesn't exist".format(username))
+		return
+	# NOTE: This should have also `toIRC` = False` tho,
+	# since we send JOIN message later on ircserver.py.
+	# Will test this later
+	return joinChannel(userID, channel)
+
+def IRCPartChannel(username, channel):
+	"""
+	Handle IRC channel part bancho-side.
+
+	:param username: username
+	:param channel: channel name
+	:return: IRC return code
+	"""
+	userID = userUtils.getID(username)
+	if not userID:
+		log.warning("{} doesn't exist".format(username))
+		return
+	return partChannel(userID, channel)
+
+def IRCAway(username, message):
+	"""
+	Handle IRC away command bancho-side.
+
+	:param username:
+	:param message: away message
+	:return: IRC return code
+	"""
+	userID = userUtils.getID(username)
+	if not userID:
+		log.warning("{} doesn't exist".format(username))
+		return
+	glob.tokens.getTokenFromUserID(userID).awayMessage = message
+	return 305 if message == "" else 306
\ No newline at end of file
diff --git a/helpers/chatHelper.py b/helpers/chatHelper.py
index 229eb33..c50ad0e 100644
--- a/helpers/chatHelper.py
+++ b/helpers/chatHelper.py
@@ -186,9 +186,9 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
 		if token.isSilenced():
 			raise exceptions.userSilencedException()
 
-		# Redirect !report to FokaBot
+		# Redirect !report to the bot
 		if message.startswith("!report"):
-			to = "FokaBot"
+			to = glob.BOT_NAME
 
 		# Determine internal name if needed
 		# (toclient is used clientwise for #multiplayer and #spectator channels)
@@ -257,8 +257,8 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
 			#if recipientToken.tournament:
 			#	raise exceptions.userTournamentException()
 
-			# Make sure the recipient is not restricted or we are FokaBot
-			if recipientToken.restricted and fro.lower() != "fokabot":
+			# Make sure the recipient is not restricted or we are the bot
+			if recipientToken.restricted 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
@@ -282,15 +282,15 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
 					continue
 				glob.ircServer.banchoMessage(fro, to, line)
 
-		# Spam protection (ignore FokaBot)
+		# Spam protection (ignore the bot)
 		if token.userID > 999:
 			token.spamProtection()
 
-		# Fokabot message
-		if isChannel or to.lower() == "fokabot":
+		# Some bot message
+		if isChannel or to.lower() == glob.BOT_NAME.lower():
 			fokaMessage = fokabot.fokabotResponse(token.username, to, message)
 			if fokaMessage:
-				sendMessage("FokaBot", to if isChannel else fro, fokaMessage)
+				sendMessage(glob.BOT_NAME, to if isChannel else fro, fokaMessage)
 
 		# File and discord logs (public chat only)
 		if to.startswith("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")):
diff --git a/objects/channel.py b/objects/channel.py
index 9d84142..91740cd 100644
--- a/objects/channel.py
+++ b/objects/channel.py
@@ -29,7 +29,7 @@ class channel:
 			try:
 				fokaToken.joinChannel(self)
 			except exceptions.userAlreadyInChannelException:
-				logging.warning("FokaBot has already joined channel {}".format(self.name))
+				logging.warning("Bot has already joined channel {}".format(self.name))
 
 	@property
 	def isSpecial(self):
diff --git a/objects/fokabot.py b/objects/fokabot.py
index d45a43c..24afa43 100644
--- a/objects/fokabot.py
+++ b/objects/fokabot.py
@@ -17,6 +17,7 @@ def connect():
 
 	:return:
 	"""
+	glob.BOT_NAME = userUtils.getUsername(999)
 	token = glob.tokens.addToken(999)
 	token.actionID = actions.IDLE
 	glob.streams.broadcast("main", serverPackets.userPanel(999))
diff --git a/objects/glob.py b/objects/glob.py
index c47a78e..afe5259 100644
--- a/objects/glob.py
+++ b/objects/glob.py
@@ -18,6 +18,8 @@ except:
 	VERSION = "Unknown"
 
 DATADOG_PREFIX = "peppy"
+BOT_NAME = "AC"
+# YOU CAN CHANGE TO YOUR BOT NAME! #
 application = None
 db = None
 redis = None
diff --git a/objects/match.py b/objects/match.py
index de33379..d8a8e50 100644
--- a/objects/match.py
+++ b/objects/match.py
@@ -253,7 +253,7 @@ class match:
 		else:
 			newStatus = slotStatuses.LOCKED
 
-		# Send updated settings to kicked user, so he returns to lobby
+		# Send updated settings to kicked user, so THEY!!! (WTF RIPPLE??) 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))
 
@@ -439,24 +439,24 @@ class match:
 		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)
+			chat.sendMessage(glob.BOT_NAME, chanName, "Match history available [{} here]".format(
+				"https://multi.bigtu.vip/match/{}".format(self.vinseID)
 			))
 		if not self.bloodcatAlert:
 			chat.sendMessage(
-				"FokaBot",
+				glob.BOT_NAME,
 				chanName,
-				"Oh by the way, in case you're playing unranked or broken maps "
+				"and uh... 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!"
+				"currently selected map from Bloodcat! or just use osu!direct !"
 			)
 			self.bloodcatAlert = True
 
 		# If this is a tournament match, then we send a notification in the chat
 		# saying that the match has completed.
 		if self.isTourney and (chanName in glob.channels.channels):
-			chat.sendMessage("FokaBot", chanName, "Match has just finished.")
+			chat.sendMessage(glob.BOT_NAME, chanName, "Match has just finished.")
 
 	def resetSlots(self):
 		for i in range(0,16):
@@ -686,9 +686,9 @@ class match:
 		if froToken is None or toToken is None:
 			return
 
-		# FokaBot is too busy
+		# BOT IS BUSY!!!
 		if to == 999:
-			chat.sendMessage("FokaBot", froToken.username, "I would love to join your match, but I'm busy keeping ripple up and running. Sorry. Beep Boop.")
+			chat.sendMessage(glob.BOT_NAME, froToken.username, "I would love to join your match, but I'm busy keeping the server up and running. Sorry. Beep Boop.")
 
 		# Send message
 		message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName)
@@ -878,7 +878,7 @@ class match:
 		if totalUsers == 0:
 			message = "The match is now empty."
 
-		chat.sendMessage("FokaBot", chanName, message)
+		chat.sendMessage(glob.BOT_NAME, chanName, message)
 
 	def __enter__(self):
 		# 🌚🌚🌚🌚🌚
diff --git a/objects/osuToken.py b/objects/osuToken.py
index bdfe44a..18211c6 100644
--- a/objects/osuToken.py
+++ b/objects/osuToken.py
@@ -110,7 +110,7 @@ class token:
 			# Acquire the buffer lock
 			self._bufferLock.acquire()
 
-			# Never enqueue for IRC clients or Foka
+			# Never enqueue for IRC clients or Bot
 			if self.irc or self.userID < 999:
 				return
 
@@ -386,7 +386,7 @@ class token:
 
 		:param seconds: silence length in seconds. If None, get it from db. Default: None
 		:param reason: silence reason. Default: empty string
-		:param author: userID of who has silenced the user. Default: 999 (FokaBot)
+		:param author: userID of who has silenced the user. Default: 999 (Your Bot Name lol)
 		:return:
 		"""
 		if seconds is None:
@@ -487,7 +487,7 @@ class token:
 		:return:
 		"""
 		self.restricted = True
-		chat.sendMessage("FokaBot", self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
+		chat.sendMessage(glob.BOT_NAME, self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
 
 	def resetRestricted(self):
 		"""
@@ -496,7 +496,7 @@ class token:
 
 		:return:
 		"""
-		chat.sendMessage("FokaBot", self.username, "Your account has been unrestricted! Please log in again.")
+		chat.sendMessage(glob.BOT_NAME, self.username, "Your account has been unrestricted! Please log in again.")
 
 	def joinStream(self, name):
 		"""
-- 
2.20.1


From a5373bd3f498889e5eafb3f344be93458fb012a2 Mon Sep 17 00:00:00 2001
From: osu!thailand <43190206+osuthailand@users.noreply.github.com>
Date: Wed, 6 Mar 2019 18:11:11 +0700
Subject: [PATCH 02/88] Edit little README.md

---
 README.md | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 2c51f67..35bffa4 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,15 @@
-## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master)
+## pep.py
 
-- Origin: https://git.zxq.co/ripple/pep.py
-- Mirror: https://github.com/osuripple/pep.py
-
-This is Ripple's bancho server. It handles:
+This is Ripple's bancho server but osu!thailand forked edition. It handles:
 - Client login
 - Online users listing and statuses
 - Public and private chat
 - Spectator
 - Multiplayer
-- Fokabot
+- The Bot
 
 ## Requirements
-- Python 3.5
+- Python 3.6
 - Cython
 - C compiler
 - MySQLdb (`mysqlclient`)
-- 
2.20.1


From 7bb45966c5f476395622a9966dfb2a8d9d920ad3 Mon Sep 17 00:00:00 2001
From: osu!thailand <43190206+osuthailand@users.noreply.github.com>
Date: Wed, 6 Mar 2019 18:12:09 +0700
Subject: [PATCH 03/88] Use official common (but GitHub instead)

---
 .gitmodules | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitmodules b/.gitmodules
index d934dd7..f3fe6f6 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "common"]
 	path = common
-	url = https://zxq.co/ripple/ripple-python-common.git
+	url = https://github.com/osuripple/ripple-python-common.git
-- 
2.20.1


From c5c6ed0727a13fb4b9186f8357492ef19001726f Mon Sep 17 00:00:00 2001
From: osu!thailand <43190206+osuthailand@users.noreply.github.com>
Date: Wed, 6 Mar 2019 20:10:50 +0700
Subject: [PATCH 04/88] Ok, I guess... I'll go back to mine instead.

---
 .gitmodules | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitmodules b/.gitmodules
index f3fe6f6..a4ead52 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "common"]
 	path = common
-	url = https://github.com/osuripple/ripple-python-common.git
+	url = https://github.com/osuthailand/common.git
-- 
2.20.1


From 622ba118137ee2fff974d0a78ed788bbb16a8587 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <minecraft172zzz@gmail.com>
Date: Mon, 21 Oct 2019 17:39:27 +0700
Subject: [PATCH 05/88] Add !beatconnect command

---
 constants/fokabotCommands.py | 65 ++++++++++++++++++------------------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 14a1151..0ea3862 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -30,6 +30,15 @@ def bloodcatMessage(beatmapID):
 		beatmap["song_name"],
 	)
 
+def beatconnectMessage(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://beatconnect.io/b/{} {}] from Beatconnect".format(
+		beatmap["beatmapset_id"],
+		beatmap["song_name"],
+	)
+	
 """
 Commands callbacks
 
@@ -56,10 +65,9 @@ def faq(fro, chan, message):
 		"swearing": "Please don't abuse swearing",
 		"spam": "Please don't spam",
 		"offend": "Please don't offend other players",
-		"github": "(osu!Ainu's Github page!)[https://github.com/osuripple/ripple]",
-		"discord": "(Join Ainu Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]",
+		"github": "(osu!Ainu's Github page!)[https://github.com/osuthailand]",
+		"discord": "(Join Ainu Discord!)[https://discord.gg/cnaDpVY]",
 		"changelog": "Check the (changelog)[https://bigtu.vip/changelog] !",
-		"status": "Check the server status (here!)[https://status.bigtu.vip]",
 		"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."
@@ -1186,29 +1194,6 @@ def switchServer(fro, chan, message):
 	# userToken.kick()
 	return "{} has been connected to {}".format(target, newServer)
 
-def reloadConfig(fro, chan, message):
-	if chan.startswith("#"):
-		return
-	try:
-		if not glob.conf.reload():
-			return "Invalid configuration file structure. The new configuration file was not reloaded."
-	except Exception as e:
-		return "Unhandled exception while reloading the configuration file: {}".format(str(e))
-	return "Configuration file reloaded successfully"
-
-def delta(fro, chan, message):
-	if chan.startswith("#"):
-		return
-	if not glob.conf.config["server"]["deltaurl"].strip():
-		return "Delta is disabled."
-	userToken = glob.tokens.getTokenFromUserID(userUtils.getID(fro), ignoreIRC=True, _all=False)
-	if userToken is None:
-		return "You must be connected from a game client to switch to delta"
-	if not generalUtils.stringToBool(glob.conf.config["server"]["publicdelta"]) and not userToken.admin:
-		return "You can't use delta yet. Try again later."
-	userToken.enqueue(serverPackets.switchServer(glob.conf.config["server"]["deltaurl"]))
-	return "Connecting to delta..."
-
 def rtx(fro, chan, message):
 	target = message[0]
 	message = " ".join(message[1:]).strip()
@@ -1242,7 +1227,27 @@ def bloodcat(fro, chan, message):
 		beatmapID = spectatorHostToken.beatmapID
 	return bloodcatMessage(beatmapID)
 
+def beatconnect(fro, chan, message):
+	try:
+		matchID = getMatchIDFromChannel(chan)
+	except exceptions.wrongChannelException:
+		matchID = None
+	try:
+		spectatorHostUserID = getSpectatorHostUserIDFromChannel(chan)
+	except exceptions.wrongChannelException:
+		spectatorHostUserID = None
 
+	if matchID is not None:
+		if matchID not in glob.matches.matches:
+			return "This match doesn't seem to exist... Or does it...?"
+		beatmapID = glob.matches.matches[matchID].beatmapID
+	else:
+		spectatorHostToken = glob.tokens.getTokenFromUserID(spectatorHostUserID, ignoreIRC=True)
+		if spectatorHostToken is None:
+			return "The spectator host is offline."
+		beatmapID = spectatorHostToken.beatmapID
+	return beatconnectMessage(beatmapID)
+	
 """
 Commands list
 
@@ -1396,12 +1401,8 @@ commands = [
 		"trigger": "!bloodcat",
 		"callback": bloodcat
 	}, {
-		"trigger": "!delta",
-		"callback": delta
-	}, {
-		"trigger": "!reloadconfig",
-		"privileges": privileges.ADMIN_MANAGE_SERVERS,
-		"callback": reloadConfig
+		"trigger": "!beatconnect",
+		"callback": beatconnect
 	}
 	#
 	#	"trigger": "!acc",
-- 
2.20.1


From f6e45f58bbf25e8e0371ae56671b70a17889135b Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <minecraft172zzz@gmail.com>
Date: Tue, 22 Oct 2019 18:46:35 +0700
Subject: [PATCH 06/88] Add more ways to download the beatmap while spectating

---
 constants/fokabotCommands.py | 38 +++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 0ea3862..c169396 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -39,6 +39,17 @@ def beatconnectMessage(beatmapID):
 		beatmap["song_name"],
 	)
 	
+def mirrorMessage(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 {} from [https://beatconnect.io/b/{} Beatconnect], [https://bloodcat.com/osu/s/{} Bloodcat] or [osu://dl/{} osu!direct].".format(
+		beatmap["song_name"],
+		beatmap["beatmapset_id"],
+		beatmap["beatmapset_id"],
+		beatmap["beatmapset_id"],
+	)
+	
 """
 Commands callbacks
 
@@ -470,13 +481,13 @@ def getPPMessage(userID, just_data = False):
 
 def tillerinoNp(fro, chan, message):
 	try:
-		# Bloodcat trigger for #spect_
+		# Mirror list 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)
+			return mirrorMessage(spectatorHostToken.beatmapID)
 
 		# Run the command in PM only
 		if chan.startswith("#"):
@@ -772,7 +783,7 @@ def report(fro, chan, message):
 		chat.sendMessage(glob.BOT_NAME, "#admin", adminMsg)
 		log.warning(adminMsg, discord="cm")
 	except exceptions.invalidUserException:
-		msg = "Hello, {} here! You can't report me. I won't forget what you've tried to do. Watch out."
+		msg = "Hello, {} here! You can't report me. I won't forget what you've tried to do. Watch out.".format(glob.BOT_NAME)
 	except exceptions.invalidArgumentsException:
 		msg = "Invalid report command syntax. To report an user, click on it and select 'Report user'."
 	except exceptions.userNotFoundException:
@@ -1247,6 +1258,27 @@ def beatconnect(fro, chan, message):
 			return "The spectator host is offline."
 		beatmapID = spectatorHostToken.beatmapID
 	return beatconnectMessage(beatmapID)
+
+def mirror(fro, chan, message):
+	try:
+		matchID = getMatchIDFromChannel(chan)
+	except exceptions.wrongChannelException:
+		matchID = None
+	try:
+		spectatorHostUserID = getSpectatorHostUserIDFromChannel(chan)
+	except exceptions.wrongChannelException:
+		spectatorHostUserID = None
+
+	if matchID is not None:
+		if matchID not in glob.matches.matches:
+			return "This match doesn't seem to exist... Or does it...?"
+		beatmapID = glob.matches.matches[matchID].beatmapID
+	else:
+		spectatorHostToken = glob.tokens.getTokenFromUserID(spectatorHostUserID, ignoreIRC=True)
+		if spectatorHostToken is None:
+			return "The spectator host is offline."
+		beatmapID = spectatorHostToken.beatmapID
+	return mirrorMessage(beatmapID)
 	
 """
 Commands list
-- 
2.20.1


From 43755b9fbfa7228b49d5c28a58b37286edabbf70 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 12:05:15 +0700
Subject: [PATCH 07/88] Custom stuff for user ;p

---
 constants/serverPackets.py | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index c386274..7d61e37 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -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 support@ripple.moe from the email address you've used to sign up.")
+	packets += notification("You are banned! I don't know what did you do but you can appeal after one month since your ban by contacting Discord! (You can join by going to the website!)")
 	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 support@ripple.moe from the email address you've used to sign up.")
+	packets += notification("Well... Your account is locked but everything still in the website ya know? and uh... You can appeal us at Discord! (You can go to our website for the link!)")
 	return packets
 
 def loginError():
@@ -85,8 +85,20 @@ def userPanel(userID, force = False):
 
 	# Get user data
 	username = userToken.username
-	timezone = 24+userToken.timeOffset
-	country = userToken.country
+	# Custom Timezone
+	if userID in (1000, 1055, 1114):
+		timezone = 24+9
+	else:
+		timezone = 24+userToken.timeOffset
+	# Custom Countries for Users
+	# 111 = Japan
+	# 36 = Belarus
+	if userID in (1000, 1055, 1114):
+		country = 111
+	elif userID == 1209:
+		country = 36
+	else:
+		country = userToken.country
 	gameRank = userToken.gameRank
 	latitude = userToken.getLatitude()
 	longitude = userToken.getLongitude()
@@ -96,10 +108,14 @@ def userPanel(userID, force = False):
 	userRank = 0
 	if username == glob.BOT_NAME:
 		userRank |= userRanks.MOD
-	elif username == "Aoba":
+	# 1000 = Aoba's User ID
+	elif userID == 1000:
 		userRank |= userRanks.PEPPY
-	elif username == "Natsue":
+	# 1000 = peppy's User ID
+	elif userID == 1114:
 		userRank |= userRanks.PEPPY
+	elif userID == 1055:
+		userRank |= userRanks.NORMAL
 	elif userUtils.isInPrivilegeGroup(userID, "developer"):
 		userRank |= userRanks.ADMIN
 	elif userUtils.isInPrivilegeGroup(userID, "chat mod"):
-- 
2.20.1


From 104e87de3343d060b6af030d87d161b071110816 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 12:06:29 +0700
Subject: [PATCH 08/88] If you're restricted, then no you're not

---
 events/loginEvent.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/events/loginEvent.py b/events/loginEvent.py
index 611ada1..99efcd7 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -130,7 +130,10 @@ def handle(tornadoRequest):
 
 		# Get supporter/GMT
 		userGMT = False
-		userSupporter = True
+		if not userUtils.isRestricted(userID):
+			userSupporter = True
+		else:
+			userSupporter = False
 		userTournament = False
 		if responseToken.admin:
 			userGMT = True
-- 
2.20.1


From e62d5c33c1a68bf6e10ecc1d0dce1daabdd70249 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 12:07:09 +0700
Subject: [PATCH 09/88] AC is from Japan :+1:

---
 objects/fokabot.py | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/objects/fokabot.py b/objects/fokabot.py
index 24afa43..42c52db 100644
--- a/objects/fokabot.py
+++ b/objects/fokabot.py
@@ -11,7 +11,7 @@ from objects import glob
 # Tillerino np regex, compiled only once to increase performance
 npRegex = re.compile("^https?:\\/\\/osu\\.ppy\\.sh\\/b\\/(\\d*)")
 
-def connect():
+def connect(timeOffset = 9):
 	"""
 	Connect FokaBot to Bancho
 
@@ -20,6 +20,14 @@ def connect():
 	glob.BOT_NAME = userUtils.getUsername(999)
 	token = glob.tokens.addToken(999)
 	token.actionID = actions.IDLE
+	token.actionText = "\n-- Welcome to Ainu --"
+	token.pp = 727
+	token.accuracy = 0.9885
+	token.playcount = 26956
+	token.totalScore = 237228316533
+	token.timeOffset = timeOffset
+	token.timezone = 24+token.timeOffset
+	token.country = 111
 	glob.streams.broadcast("main", serverPackets.userPanel(999))
 	glob.streams.broadcast("main", serverPackets.userStats(999))
 
-- 
2.20.1


From bd04506e6412c6c2ec77a8a11b792788bcdfab37 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 12:07:40 +0700
Subject: [PATCH 10/88] Add !beatconnect when playing #multi

---
 objects/match.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/objects/match.py b/objects/match.py
index d8a8e50..c0bed58 100644
--- a/objects/match.py
+++ b/objects/match.py
@@ -449,7 +449,8 @@ class match:
 				"and uh... 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! or just use osu!direct !"
+				"currently selected map from Bloodcat! If osu!direct is not working, "
+				"You can still use '!beatconnect' as a mirror too! "
 			)
 			self.bloodcatAlert = True
 
-- 
2.20.1


From 656a99319dec60589ddccb0246f7c968435f1949 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 12:08:25 +0700
Subject: [PATCH 11/88] Man, this stuff looks nice.

---
 objects/osuToken.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/objects/osuToken.py b/objects/osuToken.py
index 18211c6..dc2a5dd 100644
--- a/objects/osuToken.py
+++ b/objects/osuToken.py
@@ -66,8 +66,14 @@ class token:
 		self.spamRate = 0
 
 		# Stats cache
-		self.actionID = actions.IDLE
-		self.actionText = ""
+		if userID == 1000:
+			self.actionID = actions.WATCHING
+		else:
+			self.actionID = actions.IDLE
+		if userID == 1000:
+			self.actionText = "HentaiHaven"
+		else:
+			self.actionText = ""
 		self.actionMd5 = ""
 		self.actionMods = 0
 		self.gameMode = gameModes.STD
-- 
2.20.1


From b82fc9c2829b8c69f843625878cb814029b8a7ce Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 12:09:07 +0700
Subject: [PATCH 12/88] AC is not a player, so... no.

---
 handlers/apiOnlineUsersHandler.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py
index 4960c8b..5d6b380 100644
--- a/handlers/apiOnlineUsersHandler.py
+++ b/handlers/apiOnlineUsersHandler.py
@@ -14,10 +14,11 @@ class handler(requestsManager.asyncRequestHandler):
 	@sentry.captureTornado
 	def asyncGet(self):
 		statusCode = 400
+		ass = 1
 		data = {"message": "unknown error"}
 		try:
 			# Get online users count
-			data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8"))
+			data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8")) - int(ass)
 
 			# Status code and message
 			statusCode = 200
-- 
2.20.1


From 6709c3bc7fa89edfca3cbda7782fe2fb5f570748 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 24 Oct 2019 13:10:38 +0700
Subject: [PATCH 13/88] big MEME

---
 handlers/mainHandler.pyx | 29 +++++++----------------------
 1 file changed, 7 insertions(+), 22 deletions(-)

diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx
index 1341581..ade78d1 100644
--- a/handlers/mainHandler.pyx
+++ b/handlers/mainHandler.pyx
@@ -197,7 +197,7 @@ class handler(requestsManager.asyncRequestHandler):
 			except exceptions.tokenNotFoundException:
 				# Token not found. Disconnect that user
 				responseData = serverPackets.loginError()
-				responseData += serverPackets.notification("Whoops! Something went wrong, please login again.")
+				responseData += serverPackets.notification("Oh no! Ainu have something wrong at the moment... Maybe try login again?")
 				log.warning("Received packet from unknown token ({}).".format(requestTokenString))
 				log.info("{} has been disconnected (invalid token)".format(requestTokenString))
 			finally:
@@ -243,24 +243,9 @@ 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%}</style></head><body><pre>"
-		html += "           _                 __<br>"
-		html += "          (_)              /  /<br>"
-		html += "   ______ __ ____   ____  /  /____<br>"
-		html += "  /  ___/  /  _  \\/  _  \\/  /  _  \\<br>"
-		html += " /  /  /  /  /_) /  /_) /  /  ____/<br>"
-		html += "/__/  /__/  .___/  .___/__/ \\_____/<br>"
-		html += "        /  /   /  /<br>"
-		html += "       /__/   /__/<br>"
-		html += "<b>PYTHON > ALL VERSION</b><br><br>"
-		html += "<marquee style='white-space:pre;'><br>"
-		html += "                          .. o  .<br>"
-		html += "                         o.o o . o<br>"
-		html += "                        oo...<br>"
-		html += "                    __[]__<br>"
-		html += "    phwr-->  _\\:D/_/o_o_o_|__     <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
-		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><i>&copy; Ripple team, 2016</i></pre></body></html>"
-		self.write(html)
\ No newline at end of file
+		html = 	"<html><head><title>Aoba's a cutie?</title><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fontello.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fonts.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/inter-ui.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/master.less' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2-bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-ansi.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-pygments.css' type='text/css'></head><body><div class='code' id='code'>"
+		html += "<script>NekoType='spooky'</script>"
+		html += "<h1 id=nl><script src='https://webneko.net/n20171213.js'></script></h1>"
+		html += "<iframe src='https://ghostbin.co/paste/8j2ft' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>"
+		#Yes. I just wrote the credit... in it.
+		self.write(html)
-- 
2.20.1


From ae973dc20801e64157d7f9283310cfd931217dd3 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <minecraft172zzz@gmail.com>
Date: Thu, 24 Oct 2019 13:21:00 +0700
Subject: [PATCH 14/88] Oh well... I still using this...

---
 README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/README.md b/README.md
index 4ed9153..7e72ba3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
-# ⚠️ As of 26th June 2019, this repository has been deprecated and is not used in Ripple's stack anymore. It's left here just for historical reasons.
 ## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master)
 
 This is Ripple's bancho server but osu!thailand forked edition. It handles:
-- 
2.20.1


From 4eb990b202e8fea7a4dec07b977b2716cabf860e Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <minecraft172zzz@gmail.com>
Date: Fri, 25 Oct 2019 00:15:57 +0700
Subject: [PATCH 15/88] He wants Mod color :p

---
 constants/serverPackets.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 7d61e37..91649f4 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -115,7 +115,7 @@ def userPanel(userID, force = False):
 	elif userID == 1114:
 		userRank |= userRanks.PEPPY
 	elif userID == 1055:
-		userRank |= userRanks.NORMAL
+		userRank |= userRanks.MOD
 	elif userUtils.isInPrivilegeGroup(userID, "developer"):
 		userRank |= userRanks.ADMIN
 	elif userUtils.isInPrivilegeGroup(userID, "chat mod"):
@@ -298,4 +298,4 @@ def banchoRestart(msUntilReconnection):
 	return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.UINT32]])
 
 def rtx(message):
-	return packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]])
\ No newline at end of file
+	return packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]])
-- 
2.20.1


From 8fdb7ed3071ff338441ce1ab0050fa5e2bf8490f Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 25 Oct 2019 19:14:45 +0700
Subject: [PATCH 16/88] You can't ban or restrict AC! Remember it Simon.

---
 constants/fokabotCommands.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index c169396..dd19268 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -261,7 +261,8 @@ def ban(fro, chan, message):
 	userID = userUtils.getID(fro)
 	if not targetUserID:
 		return "{}: user not found".format(target)
-
+	if targetUserID in (999, 1000):
+		return "NO!"
 	# Set allowed to 0
 	userUtils.ban(targetUserID)
 
@@ -302,7 +303,9 @@ def restrict(fro, chan, message):
 	userID = userUtils.getID(fro)
 	if not targetUserID:
 		return "{}: user not found".format(target)
-
+	if targetUserID in (999, 1000):
+		return "NO!"
+		
 	# Put this user in restricted mode
 	userUtils.restrict(targetUserID)
 
-- 
2.20.1


From 9df9ffef460bb91219be9105d17d5d8daf6f1ef6 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 25 Oct 2019 19:20:25 +0700
Subject: [PATCH 17/88] Alert you when you're playing Relax or disabled it

---
 events/changeActionEvent.py | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py
index 5e541fd..dc9b9b9 100644
--- a/events/changeActionEvent.py
+++ b/events/changeActionEvent.py
@@ -2,6 +2,7 @@ from common.log import logUtils as log
 from constants import clientPackets
 from constants import serverPackets
 from objects import glob
+from common.constants import mods
 
 def handle(userToken, packetData):
 	# Get usertoken data
@@ -40,11 +41,30 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us
 
 	# Always update action id, text, md5 and beatmapID
 	userToken.actionID = packetData["actionID"]
-	userToken.actionText = packetData["actionText"]
+	#userToken.actionID = packetData["actionText"]
 	userToken.actionMd5 = packetData["actionMd5"]
 	userToken.actionMods = packetData["actionMods"]
 	userToken.beatmapID = packetData["beatmapID"]
 
+	
+	if bool(packetData["actionMods"] & 128) == True:
+		userToken.relaxing = True
+		UserText = packetData["actionText"] + " on Relax"
+		userToken.actionText = UserText
+		userToken.updateCachedStats()
+		if userToken.relaxAnnounce == False:
+			userToken.relaxAnnounce = True
+			userToken.enqueue(serverPackets.notification("You're playing with Relax, we've changed the leaderboard to Relax."))
+	else:
+		UserText = packetData["actionText"]
+		userToken.actionText = UserText
+		userToken.relaxing = False
+		userToken.autobotting = False
+		userToken.updateCachedStats()
+		if userToken.relaxAnnounce == True:
+			userToken.relaxAnnounce = False
+			userToken.enqueue(serverPackets.notification("You've disabled relax. We've changed back to the Regular leaderboard."))
+	glob.db.execute("UPDATE users_stats SET current_status = %s WHERE id = %s", [UserText, userID])
 	# Enqueue our new user panel and stats to us and our spectators
 	recipients = [userToken]
 	if len(userToken.spectators) > 0:
-- 
2.20.1


From 5ed0649f385df74c3362ba9e184056d2cf80ea41 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 25 Oct 2019 19:21:49 +0700
Subject: [PATCH 18/88] Where is this came from?!

---
 events/changeActionEvent.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py
index dc9b9b9..5c2f79e 100644
--- a/events/changeActionEvent.py
+++ b/events/changeActionEvent.py
@@ -59,7 +59,6 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us
 		UserText = packetData["actionText"]
 		userToken.actionText = UserText
 		userToken.relaxing = False
-		userToken.autobotting = False
 		userToken.updateCachedStats()
 		if userToken.relaxAnnounce == True:
 			userToken.relaxAnnounce = False
-- 
2.20.1


From e0b3828e55f65584a29dc22358d81d246e8d08d4 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 25 Oct 2019 19:27:01 +0700
Subject: [PATCH 19/88] They're offline, and then set it to offline.

---
 events/logoutEvent.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/events/logoutEvent.py b/events/logoutEvent.py
index 3a8d55e..f9b5a39 100644
--- a/events/logoutEvent.py
+++ b/events/logoutEvent.py
@@ -44,6 +44,7 @@ def handle(userToken, _=None, deleteToken=True):
 		else:
 			userToken.kicked = True
 
+		glob.db.execute("UPDATE users_stats SET current_status = 'Offline' WHERE id = %s", [userID])
 		# Change username if needed
 		newUsername = glob.redis.get("ripple:change_username_pending:{}".format(userID))
 		if newUsername is not None:
-- 
2.20.1


From 4c7d72c476678305c663291b6ddbac746ec44e49 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 25 Oct 2019 19:36:57 +0700
Subject: [PATCH 20/88] Add Relax stats

---
 objects/osuToken.py | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/objects/osuToken.py b/objects/osuToken.py
index dc2a5dd..771992e 100644
--- a/objects/osuToken.py
+++ b/objects/osuToken.py
@@ -84,6 +84,10 @@ class token:
 		self.totalScore = 0
 		self.gameRank = 0
 		self.pp = 0
+		
+		# Relax
+		self.relaxing = False
+		self.relaxAnnounce = False
 
 		# Generate/set token
 		if token_ is not None:
@@ -450,16 +454,26 @@ class token:
 		:return:
 		"""
 		stats = userUtils.getUserStats(self.userID, self.gameMode)
+		stats_relax = userUtils.getUserStatsRx(self.userID, self.gameMode)
 		log.debug(str(stats))
+		
 		if stats is None:
 			log.warning("Stats query returned None")
 			return
-		self.rankedScore = stats["rankedScore"]
-		self.accuracy = stats["accuracy"]/100
-		self.playcount = stats["playcount"]
-		self.totalScore = stats["totalScore"]
-		self.gameRank = stats["gameRank"]
-		self.pp = stats["pp"]
+		if self.relaxing == True:
+			self.gameRank = stats_relax["gameRank"]
+			self.pp = stats_relax["pp"]
+			self.rankedScore = stats_relax["rankedScore"]
+			self.accuracy = stats_relax["accuracy"]/100
+			self.playcount = stats_relax["playcount"]
+			self.totalScore = stats_relax["totalScore"]
+		else:
+			self.rankedScore = stats["rankedScore"]
+			self.accuracy = stats["accuracy"]/100
+			self.playcount = stats["playcount"]
+			self.totalScore = stats["totalScore"]
+			self.gameRank = stats["gameRank"]
+			self.pp = stats["pp"]
 
 	def checkRestricted(self):
 		"""
@@ -493,7 +507,7 @@ class token:
 		:return:
 		"""
 		self.restricted = True
-		chat.sendMessage(glob.BOT_NAME, self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
+		chat.sendMessage(glob.BOT_NAME, self.username, "Your account is currently in restricted mode. Please visit Ainu's website for more information.")
 
 	def resetRestricted(self):
 		"""
-- 
2.20.1


From deb3362a62ee5c9d6a0b7e743bfeb8aa51b1de6b Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 25 Oct 2019 19:37:16 +0700
Subject: [PATCH 21/88] No one is on the boat :(

---
 helpers/consoleHelper.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/helpers/consoleHelper.py b/helpers/consoleHelper.py
index 6f9a17f..352c45a 100644
--- a/helpers/consoleHelper.py
+++ b/helpers/consoleHelper.py
@@ -21,13 +21,13 @@ def printServerStartHeader(asciiArt=True):
 		print("                         o.o o . o")
 		print("                        oo...")
 		print("                    __[]__")
-		print("    nyo -->  _\\:D/_/o_o_o_|__     u wot m8")
+		print("             ______/o_o_o_|__  everybody is gone :(")
 		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("> Made by the Ripple team, custom fork by osu!thailand", bcolors.GREEN)
 	printColored("> {}https://zxq.co/ripple/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN)
 	printColored("> Press CTRL+C to exit\n", bcolors.GREEN)
 
-- 
2.20.1


From e7cf92e6e60d6ff0429bb9a00c79c1197692a082 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <minecraft172zzz@gmail.com>
Date: Sat, 26 Oct 2019 11:26:45 +0700
Subject: [PATCH 22/88] Fix space stuff in online panel

---
 events/changeActionEvent.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py
index 5c2f79e..6ca1d94 100644
--- a/events/changeActionEvent.py
+++ b/events/changeActionEvent.py
@@ -49,7 +49,10 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us
 	
 	if bool(packetData["actionMods"] & 128) == True:
 		userToken.relaxing = True
-		UserText = packetData["actionText"] + " on Relax"
+		if userToken.actionID in (0, 1, 14):
+			UserText = packetData["actionText"] + "on Relax"
+		else:
+			UserText = packetData["actionText"] + " on Relax"
 		userToken.actionText = UserText
 		userToken.updateCachedStats()
 		if userToken.relaxAnnounce == False:
-- 
2.20.1


From 654daee37e899fac549c5c55d181c5c867307d71 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <minecraft172zzz@gmail.com>
Date: Fri, 1 Nov 2019 19:01:21 +0700
Subject: [PATCH 23/88] R.I.P pep.py

I will miss you :')
---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 7e72ba3..d7b9adc 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master)
-
+# ⚠️ As of 1st November 2019, this repository has been deprecated and is not used in Ainu's stack anymore. It's left here just for historical reasons.
+## pep.py
 This is Ripple's bancho server but osu!thailand forked edition. It handles:
 - Client login
 - Online users listing and statuses
-- 
2.20.1


From 7d62cc18394360b8cc592a0892426c73ede3c331 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 2 Nov 2019 23:17:47 +0700
Subject: [PATCH 24/88] pep.py's last message before death

---
 common              | 2 +-
 objects/osuToken.py | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/common b/common
index 1b6fbdd..4a270cd 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 1b6fbdd0d5e3c128a0850145dbe2c84a40a478a0
+Subproject commit 4a270cdc8d18bc929a6da54d0c8ea88464792f91
diff --git a/objects/osuToken.py b/objects/osuToken.py
index 771992e..504314e 100644
--- a/objects/osuToken.py
+++ b/objects/osuToken.py
@@ -460,7 +460,7 @@ class token:
 		if stats is None:
 			log.warning("Stats query returned None")
 			return
-		if self.relaxing == True:
+		if self.relaxing:
 			self.gameRank = stats_relax["gameRank"]
 			self.pp = stats_relax["pp"]
 			self.rankedScore = stats_relax["rankedScore"]
@@ -468,12 +468,12 @@ class token:
 			self.playcount = stats_relax["playcount"]
 			self.totalScore = stats_relax["totalScore"]
 		else:
+			self.gameRank = stats["gameRank"]
+			self.pp = stats["pp"]
 			self.rankedScore = stats["rankedScore"]
 			self.accuracy = stats["accuracy"]/100
 			self.playcount = stats["playcount"]
 			self.totalScore = stats["totalScore"]
-			self.gameRank = stats["gameRank"]
-			self.pp = stats["pp"]
 
 	def checkRestricted(self):
 		"""
-- 
2.20.1


From 92abeab94ae0c2e100ca6097f0bf6df712e86faf Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Thu, 14 Nov 2019 22:14:29 +0700
Subject: [PATCH 25/88] Hello again, Ripple.

---
 README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/README.md b/README.md
index d7b9adc..9a03d58 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
-# ⚠️ As of 1st November 2019, this repository has been deprecated and is not used in Ainu's stack anymore. It's left here just for historical reasons.
 ## pep.py
 This is Ripple's bancho server but osu!thailand forked edition. It handles:
 - Client login
-- 
2.20.1


From c25287ccae24906623bba4241d6f6f1e9e1568e8 Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Thu, 14 Nov 2019 23:42:23 +0700
Subject: [PATCH 26/88] Kurikku is my idol

---
 constants/clientPackets.py | 25 ++++++++-----------------
 1 file changed, 8 insertions(+), 17 deletions(-)

diff --git a/constants/clientPackets.py b/constants/clientPackets.py
index c5e42b6..60059a1 100644
--- a/constants/clientPackets.py
+++ b/constants/clientPackets.py
@@ -89,34 +89,25 @@ def matchSettings(stream):
 		struct.append(["slot{}Team".format(str(i)), dataTypes.BYTE])
 
 	# Read first part
-	data.append(packetHelper.readPacketData(stream, struct))
+	slotData = 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
+		s = slotData["slot{}Status".format(str(i))]
+		if s & (4 | 8 | 16 | 32 | 64) > 0:
+			struct.append(["slot{}UserId".format(str(i)), dataTypes.SINT32])
 
 	# Other settings
-	struct = [
+	struct.extend([
 		["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))
+	])
 
-	result = {}
-	for i in data:
-		result.update(i)
+	# Results goes here
+	result = packetHelper.readPacketData(stream, struct)
 	return result
 
 def createMatch(stream):
-- 
2.20.1


From 7c6b3511a284a9b0e06ab2ad7fd96567475bfdc8 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 15 Nov 2019 00:38:22 +0700
Subject: [PATCH 27/88] Updated submodules

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index 4a270cd..1be36af 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 4a270cdc8d18bc929a6da54d0c8ea88464792f91
+Subproject commit 1be36afaa4ee104158b9e1f4dc32a535021e123a
-- 
2.20.1


From d7420cdbc083a4528cb71f7c11bb78ebfb1d0fc8 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 15 Nov 2019 20:09:49 +0700
Subject: [PATCH 28/88] Added !map command to rank maps in game

cuz you don't have time to rank it in old panel :+1:
---
 constants/fokabotCommands.py | 49 ++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index dd19268..7d48b7a 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1219,6 +1219,49 @@ def rtx(fro, chan, message):
 	userToken = glob.tokens.getTokenFromUserID(targetUserID, ignoreIRC=True, _all=False)
 	userToken.enqueue(serverPackets.rtx(message))
 	return ":ok_hand:"
+	
+def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by cmyui and edited by Aoba
+	messages = [m.lower() for m in message]
+	rankType = message[0]
+	mapType = message[1]
+	mapID = message[2]
+	
+	# Get persons username & ID
+	userID = userUtils.getID(fro)
+	name = userUtils.getUsername(userID)
+	
+	# What do I do here?
+	if rankType == 'rank':
+		rankTypeID = 2
+		freezeStatus = 1
+	elif rankType == 'unrank':
+		rankTypeID = 0
+		freezeStatus = 0
+		
+	# Grab beatmapData from db
+	beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
+	
+	if mapType == 'set':
+		glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format(rankTypeID, freezeStatus, beatmapData["beatmapset_id"]))
+		if freezeStatus == 1:
+			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps 
+							WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmapset_id"]))
+	elif mapType == 'map':
+		glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, mapID))
+		if freezeStatus == 1:
+			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps 
+							WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmap_id"]))
+	else:
+		return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'"
+	
+	log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), True)
+	if mapType == 'set':
+		msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"])
+	else:
+		msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, mapID, beatmapData["song_name"])
+		
+	chat.sendMessage(glob.BOT_NAME, "#ranked", msg)
+	return msg
 
 def bloodcat(fro, chan, message):
 	try:
@@ -1311,6 +1354,12 @@ commands = [
 		#"syntax": "<question>",
 		#"callback": ask
 	#}, {
+	{
+		"trigger": "!map",
+		"syntax": "<rank/unrank> <set/map> <ID>",
+		"privileges": privileges.ADMIN_MANAGE_BEATMAPS,
+		"callback": editMap
+	},
 	{
 		"trigger": "!mm00",
 		"callback": mm00
-- 
2.20.1


From ffa642b5f74aad9d6be2fd7183c95344da76cc6e Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 15 Nov 2019 20:17:27 +0700
Subject: [PATCH 29/88] *internal screaming*

---
 constants/fokabotCommands.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 7d48b7a..a4f4da2 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1258,7 +1258,7 @@ def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by
 	if mapType == 'set':
 		msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"])
 	else:
-		msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, mapID, beatmapData["song_name"])
+		msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, beatmapData["song_name"])
 		
 	chat.sendMessage(glob.BOT_NAME, "#ranked", msg)
 	return msg
-- 
2.20.1


From f9dc2a000c4cd514bc0f1cf6df576dfb2d70d4dd Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Fri, 15 Nov 2019 20:44:46 +0700
Subject: [PATCH 30/88] Added !announce command

---
 constants/fokabotCommands.py | 71 +++++++++++++++++++++++++++++-------
 1 file changed, 57 insertions(+), 14 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index a4f4da2..0bacc0d 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1239,30 +1239,69 @@ def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by
 		freezeStatus = 0
 		
 	# Grab beatmapData from db
-	beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
+	try:
+		beatmapData = glob.db.fetch("SELECT beatmapset_id, song_name, ranked, blacklisted FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
+	except:
+		return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly."
 	
 	if mapType == 'set':
-		glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format(rankTypeID, freezeStatus, beatmapData["beatmapset_id"]))
+		glob.db.execute(
+			"UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format(
+				rankTypeID, freezeStatus, beatmapData["beatmapset_id"]))
 		if freezeStatus == 1:
-			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps 
-							WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmapset_id"]))
+			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps
+					WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(
+				beatmapData["beatmapset_id"]))
+		typeBM = 'set'
 	elif mapType == 'map':
-		glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, mapID))
+		glob.db.execute(
+			"UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format(
+				rankTypeID, freezeStatus, mapID))
 		if freezeStatus == 1:
-			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps 
-							WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmap_id"]))
+			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps
+					WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(
+				beatmapData["beatmap_id"]))
+		typeBM = 'beatmap'
 	else:
 		return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'"
 	
-	log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), True)
-	if mapType == 'set':
-		msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"])
+	# Announce that YOOOOOOO THIS MAP IS RANKED!!!
+	if rankType == "rank":
+		log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID),
+				True)
+		if mapType == 'set':
+			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType,
+																				beatmapData["beatmapset_id"],
+																				beatmapData["song_name"])
+		else:
+			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID,
+																			beatmapData["song_name"])
+		glob.db.execute(
+			"UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format(
+				beatmapData["beatmap_id"]))
 	else:
-		msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, beatmapData["song_name"])
+		log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID),
+				True)
+		if mapType == 'set':
+			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType,
+																				beatmapData["beatmapset_id"],
+																				beatmapData["song_name"])
+		else:
+			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID,
+																			beatmapData["song_name"])
+
+			glob.db.execute(
+				"UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format(
+					beatmapData["beatmap_id"]))
 		
 	chat.sendMessage(glob.BOT_NAME, "#ranked", msg)
 	return msg
 
+def postAnnouncement(fro, chan, message): # Post to #announce ingame
+	announcement = ' '.join(message[0:])
+	chat.sendMessage(glob.BOT_NAME, "#announce", announcement)
+	return "Announcement successfully sent."
+	
 def bloodcat(fro, chan, message):
 	try:
 		matchID = getMatchIDFromChannel(chan)
@@ -1349,7 +1388,12 @@ commands = [
 	}, {
 		"trigger": "!help",
 		"response": "Click (here)[https://bigtu.vip/index.php?p=16&id=4] for full command list"
-	}, #{
+	}, {
+		"trigger": "!announce",
+		"syntax": "<announcement>",
+		"privileges": privileges.ADMIN_SEND_ALERTS,
+		"callback": postAnnouncement
+	},	#{
 		#"trigger": "!ask",
 		#"syntax": "<question>",
 		#"callback": ask
@@ -1359,8 +1403,7 @@ commands = [
 		"syntax": "<rank/unrank> <set/map> <ID>",
 		"privileges": privileges.ADMIN_MANAGE_BEATMAPS,
 		"callback": editMap
-	},
-	{
+	}, {
 		"trigger": "!mm00",
 		"callback": mm00
 	}, {
-- 
2.20.1


From 91a26d01eeb808bd1b95908043c1396cdcaa7d8b Mon Sep 17 00:00:00 2001
From: _Cherry <42537021+xxCherry@users.noreply.github.com>
Date: Sat, 16 Nov 2019 18:37:43 +0300
Subject: [PATCH 31/88] send bancho restart packet instead login erro

---
 handlers/mainHandler.pyx | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx
index ade78d1..29eb508 100644
--- a/handlers/mainHandler.pyx
+++ b/handlers/mainHandler.pyx
@@ -195,11 +195,9 @@ class handler(requestsManager.asyncRequestHandler):
 				responseData = userToken.queue
 				userToken.resetQueue()
 			except exceptions.tokenNotFoundException:
-				# Token not found. Disconnect that user
-				responseData = serverPackets.loginError()
-				responseData += serverPackets.notification("Oh no! Ainu have something wrong at the moment... Maybe try login again?")
-				log.warning("Received packet from unknown token ({}).".format(requestTokenString))
-				log.info("{} has been disconnected (invalid token)".format(requestTokenString))
+				# Token not found. Send bancho restart packet to user
+				responseData = serverPackets.banchoRestart(1)
+
 			finally:
 				# Unlock token
 				if userToken is not None:
-- 
2.20.1


From 6724274c3a607d6fd15760b00d04d52f9526b132 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 16 Nov 2019 23:15:28 +0700
Subject: [PATCH 32/88] Updated submodules

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index 1be36af..dfe5762 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 1be36afaa4ee104158b9e1f4dc32a535021e123a
+Subproject commit dfe576268bc6cb8fd4c1c1aa624a0c31f4a716d8
-- 
2.20.1


From c0ea3d9b5d10bbadb099edff085c912276159dfa Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 16 Nov 2019 23:49:40 +0700
Subject: [PATCH 33/88] Revert "send bancho restart packet instead login erro"

This reverts commit 91a26d01eeb808bd1b95908043c1396cdcaa7d8b.
---
 handlers/mainHandler.pyx | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx
index 29eb508..ade78d1 100644
--- a/handlers/mainHandler.pyx
+++ b/handlers/mainHandler.pyx
@@ -195,9 +195,11 @@ class handler(requestsManager.asyncRequestHandler):
 				responseData = userToken.queue
 				userToken.resetQueue()
 			except exceptions.tokenNotFoundException:
-				# Token not found. Send bancho restart packet to user
-				responseData = serverPackets.banchoRestart(1)
-
+				# Token not found. Disconnect that user
+				responseData = serverPackets.loginError()
+				responseData += serverPackets.notification("Oh no! Ainu have something wrong at the moment... Maybe try login again?")
+				log.warning("Received packet from unknown token ({}).".format(requestTokenString))
+				log.info("{} has been disconnected (invalid token)".format(requestTokenString))
 			finally:
 				# Unlock token
 				if userToken is not None:
-- 
2.20.1


From 6e8afb046edb6cfc256aaffbea7c90205c1902b4 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 16 Nov 2019 23:51:20 +0700
Subject: [PATCH 34/88] No, Cherry... Let's just keep it is.

---
 handlers/mainHandler.pyx | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx
index ade78d1..de6cc62 100644
--- a/handlers/mainHandler.pyx
+++ b/handlers/mainHandler.pyx
@@ -243,9 +243,7 @@ class handler(requestsManager.asyncRequestHandler):
 	@tornado.web.asynchronous
 	@tornado.gen.engine
 	def asyncGet(self):
-		html = 	"<html><head><title>Aoba's a cutie?</title><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fontello.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fonts.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/inter-ui.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/master.less' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2-bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-ansi.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-pygments.css' type='text/css'></head><body><div class='code' id='code'>"
-		html += "<script>NekoType='spooky'</script>"
-		html += "<h1 id=nl><script src='https://webneko.net/n20171213.js'></script></h1>"
+		html = 	"<html><head><title>Aoba's a cutie?</title>"
 		html += "<iframe src='https://ghostbin.co/paste/8j2ft' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>"
 		#Yes. I just wrote the credit... in it.
 		self.write(html)
-- 
2.20.1


From f38273ba36635995e034a6e397940249a4c1de41 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 17 Nov 2019 01:44:21 +0700
Subject: [PATCH 35/88] Updated submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 mode change 160000 => 120000 common

diff --git a/common b/common
deleted file mode 160000
index dfe5762..0000000
--- a/common
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit dfe576268bc6cb8fd4c1c1aa624a0c31f4a716d8
diff --git a/common b/common
new file mode 120000
index 0000000..8047583
--- /dev/null
+++ b/common
@@ -0,0 +1 @@
+E:/GitHub/common
\ No newline at end of file
-- 
2.20.1


From 52e0f7dc1b6c67d8ee5715e7e7e664a7690d91af Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 17 Nov 2019 01:48:02 +0700
Subject: [PATCH 36/88] Wait... Where did the submodule go!?

---
 common | 1 -
 1 file changed, 1 deletion(-)
 delete mode 120000 common

diff --git a/common b/common
deleted file mode 120000
index 8047583..0000000
--- a/common
+++ /dev/null
@@ -1 +0,0 @@
-E:/GitHub/common
\ No newline at end of file
-- 
2.20.1


From da543f5c8332d02a5e0ff960c81e06fd22304b33 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 17 Nov 2019 01:57:04 +0700
Subject: [PATCH 37/88] Hello... Is there anybody... there

---
 .gitmodules | 2 +-
 common      | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)
 create mode 160000 common

diff --git a/.gitmodules b/.gitmodules
index a4ead52..c04cfe0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "common"]
 	path = common
-	url = https://github.com/osuthailand/common.git
+	url = https://github.com/osuthailand/common
diff --git a/common b/common
new file mode 160000
index 0000000..f098b7b
--- /dev/null
+++ b/common
@@ -0,0 +1 @@
+Subproject commit f098b7b89ced5648c3b12bf51933796dfba58243
-- 
2.20.1


From 8f527abc205a80acbd974fb9c4769bff804c2eb7 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 17 Nov 2019 02:05:32 +0700
Subject: [PATCH 38/88] Updated submodule

---
 .gitmodules | 2 +-
 common      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitmodules b/.gitmodules
index c04cfe0..a4ead52 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "common"]
 	path = common
-	url = https://github.com/osuthailand/common
+	url = https://github.com/osuthailand/common.git
diff --git a/common b/common
index f098b7b..8bacaa8 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit f098b7b89ced5648c3b12bf51933796dfba58243
+Subproject commit 8bacaa80fd95e7f06c2d20f1fb58642875dfe95d
-- 
2.20.1


From ec63d4d264178372cc4f62d229173ba75744e39d Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 17 Nov 2019 02:24:47 +0700
Subject: [PATCH 39/88] Updated submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index 8bacaa8..6055fc1 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 8bacaa80fd95e7f06c2d20f1fb58642875dfe95d
+Subproject commit 6055fc11b8ae196ee3636522dbc41be578301c54
-- 
2.20.1


From 686babd223e3b88119f87027e03a2e986f655dbe Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Fri, 22 Nov 2019 01:27:55 +0700
Subject: [PATCH 40/88] Fix beatmap not found in map command

---
 constants/fokabotCommands.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 0bacc0d..1cc0160 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1240,7 +1240,7 @@ def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by
 		
 	# Grab beatmapData from db
 	try:
-		beatmapData = glob.db.fetch("SELECT beatmapset_id, song_name, ranked, blacklisted FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
+		beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
 	except:
 		return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly."
 	
-- 
2.20.1


From 4ac1bf8d9380088f4fecb1f05dfc2bc0096e375d Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Thu, 5 Dec 2019 20:29:37 +0700
Subject: [PATCH 41/88] bigtu.vip > ainu.pw

---
 constants/fokabotCommands.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 1cc0160..9f9e706 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -72,13 +72,13 @@ def instantRestart(fro, chan, message):
 def faq(fro, chan, message):
 	# TODO: Unhardcode this
 	messages = {
-		"rules": "Please make sure to check (osu!thailand's rules)[https://bigtu.vip/doc/rules].",
+		"rules": "Please make sure to check (osu!thailand's rules)[https://ainu.pw/doc/rules].",
 		"swearing": "Please don't abuse swearing",
 		"spam": "Please don't spam",
 		"offend": "Please don't offend other players",
 		"github": "(osu!Ainu's Github page!)[https://github.com/osuthailand]",
-		"discord": "(Join Ainu Discord!)[https://discord.gg/cnaDpVY]",
-		"changelog": "Check the (changelog)[https://bigtu.vip/changelog] !",
+		"discord": "(Join Ainu Discord!)[https://discord.gg/Qp3WQU8]",
+		"changelog": "Check the (changelog)[https://ainu.pw/changelog] !",
 		"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."
@@ -1387,7 +1387,7 @@ commands = [
 		"callback": report
 	}, {
 		"trigger": "!help",
-		"response": "Click (here)[https://bigtu.vip/index.php?p=16&id=4] for full command list"
+		"response": "Click (here)[https://ainu.pw/index.php?p=16&id=4] for full command list"
 	}, {
 		"trigger": "!announce",
 		"syntax": "<announcement>",
-- 
2.20.1


From a3388efd908f4050a27841cdbdababd00177eb6f Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Fri, 6 Dec 2019 07:03:20 +0700
Subject: [PATCH 42/88] Updated submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index 6055fc1..d114fc1 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 6055fc11b8ae196ee3636522dbc41be578301c54
+Subproject commit d114fc1a86dccf67a8fb918de802d7b2ced36aab
-- 
2.20.1


From c76be19c80614eee475d7d0f9c65fadcc44736da Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Sat, 7 Dec 2019 19:33:28 +0700
Subject: [PATCH 43/88] Updated submodule

---
 common             | 2 +-
 objects/fokabot.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/common b/common
index d114fc1..d6f0f9b 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit d114fc1a86dccf67a8fb918de802d7b2ced36aab
+Subproject commit d6f0f9bd9e2b6a8fc6f75ee5621814f63e55027d
diff --git a/objects/fokabot.py b/objects/fokabot.py
index 42c52db..3ab179d 100644
--- a/objects/fokabot.py
+++ b/objects/fokabot.py
@@ -71,4 +71,4 @@ def fokabotResponse(fro, chan, message):
 				return i["callback"](fro, chan, message[1:])
 
 	# No commands triggered
-	return False
\ No newline at end of file
+	return False
-- 
2.20.1


From 3506289371fc5cc369e9ba15ff835a843848ab8e Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Sat, 7 Dec 2019 20:54:13 +0700
Subject: [PATCH 44/88] Update submoudle v999

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index d6f0f9b..2e011c9 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit d6f0f9bd9e2b6a8fc6f75ee5621814f63e55027d
+Subproject commit 2e011c942c9ab6904dded6fecc1cc2cf3a967416
-- 
2.20.1


From c74ac5e794091bafa192ca2db9ee5f4268db4f3f Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 8 Dec 2019 17:09:47 +0700
Subject: [PATCH 45/88] I know it's annoying.

---
 events/changeActionEvent.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py
index 6ca1d94..ca3db49 100644
--- a/events/changeActionEvent.py
+++ b/events/changeActionEvent.py
@@ -55,17 +55,21 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us
 			UserText = packetData["actionText"] + " on Relax"
 		userToken.actionText = UserText
 		userToken.updateCachedStats()
+		"""
 		if userToken.relaxAnnounce == False:
 			userToken.relaxAnnounce = True
 			userToken.enqueue(serverPackets.notification("You're playing with Relax, we've changed the leaderboard to Relax."))
+		"""
 	else:
 		UserText = packetData["actionText"]
 		userToken.actionText = UserText
 		userToken.relaxing = False
 		userToken.updateCachedStats()
+		"""
 		if userToken.relaxAnnounce == True:
 			userToken.relaxAnnounce = False
 			userToken.enqueue(serverPackets.notification("You've disabled relax. We've changed back to the Regular leaderboard."))
+		"""
 	glob.db.execute("UPDATE users_stats SET current_status = %s WHERE id = %s", [UserText, userID])
 	# Enqueue our new user panel and stats to us and our spectators
 	recipients = [userToken]
-- 
2.20.1


From d831ab7bb8af59ced039b4d5921cb3aa9c097f12 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Tue, 10 Dec 2019 19:39:41 +0700
Subject: [PATCH 46/88] Revert "AC is not a player, so... no."

This reverts commit b82fc9c2829b8c69f843625878cb814029b8a7ce.
---
 handlers/apiOnlineUsersHandler.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py
index 5d6b380..4960c8b 100644
--- a/handlers/apiOnlineUsersHandler.py
+++ b/handlers/apiOnlineUsersHandler.py
@@ -14,11 +14,10 @@ class handler(requestsManager.asyncRequestHandler):
 	@sentry.captureTornado
 	def asyncGet(self):
 		statusCode = 400
-		ass = 1
 		data = {"message": "unknown error"}
 		try:
 			# Get online users count
-			data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8")) - int(ass)
+			data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8"))
 
 			# Status code and message
 			statusCode = 200
-- 
2.20.1


From 914cd3e95b0b4a5d4f49e150b642466302debc11 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 14 Dec 2019 11:14:40 +0700
Subject: [PATCH 47/88] Updated submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index 2e011c9..037e453 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 2e011c942c9ab6904dded6fecc1cc2cf3a967416
+Subproject commit 037e453812e69fbc2a0eb8f2172ffa7bc1e15bd2
-- 
2.20.1


From 404845ee24a419c228d3bee91bd04cb0447995ac Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 14 Dec 2019 11:17:37 +0700
Subject: [PATCH 48/88] Add whitelist from autorestrictions

---
 constants/fokabotCommands.py | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 9f9e706..9fd2f5d 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1301,6 +1301,24 @@ def postAnnouncement(fro, chan, message): # Post to #announce ingame
 	announcement = ' '.join(message[0:])
 	chat.sendMessage(glob.BOT_NAME, "#announce", announcement)
 	return "Announcement successfully sent."
+
+def whitelistUserPPLimit(fro, chan, message):
+	messages = [m.lower() for m in message]
+	target = message[0]
+	relax = message[1]
+
+	userID = userUtils.getID(target)
+
+	if userID == 0:
+		return "That user does not exist."
+
+	if 'x' in relax:
+		rx = True
+	else:
+		rx = False
+
+	userUtils.whitelistUserPPLimit(userID, rx)
+	return "{user} has been whitelisted from autorestrictions on {rx}.".format(user=target, rx='relax' if rx else 'vanilla')
 	
 def bloodcat(fro, chan, message):
 	try:
-- 
2.20.1


From 43a1b082343b01d55f64885b5d16753a16b32970 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 14 Dec 2019 11:19:48 +0700
Subject: [PATCH 49/88] Aoba forgot everything v2

---
 constants/fokabotCommands.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 9fd2f5d..249c38b 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1406,6 +1406,11 @@ commands = [
 	}, {
 		"trigger": "!help",
 		"response": "Click (here)[https://ainu.pw/index.php?p=16&id=4] for full command list"
+	}, {
+		"trigger": "!whitelist",
+		"privileges": privileges.ADMIN_BAN_USERS,
+		"syntax": "<target> <relax/vanilla>",
+		"callback": whitelistUserPPLimit
 	}, {
 		"trigger": "!announce",
 		"syntax": "<announcement>",
-- 
2.20.1


From c5c3265cf3c298e8e2fde4606822208916138fe1 Mon Sep 17 00:00:00 2001
From: Mike Shenin <i@kotrik.ru>
Date: Wed, 27 Nov 2019 22:13:03 +0300
Subject: [PATCH 50/88] feat: logging user osuVer

---
 events/loginEvent.py                             |  4 ++++
 helpers/kotrikhelper.py                          |  4 ++++
 ...elper.py~64f4109... feat: logging user osuVer | 16 ++++++++++++++++
 3 files changed, 24 insertions(+)
 create mode 100644 helpers/kotrikhelper.py
 create mode 100644 helpers/kotrikhelper.py~64f4109... feat: logging user osuVer

diff --git a/events/loginEvent.py b/events/loginEvent.py
index 99efcd7..bcf08fb 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -10,6 +10,7 @@ from constants import serverPackets
 from helpers import chatHelper as chat
 from helpers import countryHelper
 from helpers import locationHelper
+from helpers import kotrikhelper
 from objects import glob
 
 
@@ -100,6 +101,9 @@ def handle(tornadoRequest):
 		# Log user IP
 		userUtils.logIP(userID, requestIP)
 
+		# Log user osuver
+		kotrikhelper.setUserLastOsuVer(userID, osuVersion)
+
 		# Delete old tokens for that user and generate a new one
 		isTournament = "tourney" in osuVersion
 		if not isTournament:
diff --git a/helpers/kotrikhelper.py b/helpers/kotrikhelper.py
new file mode 100644
index 0000000..7c45ac7
--- /dev/null
+++ b/helpers/kotrikhelper.py
@@ -0,0 +1,4 @@
+import math
+
+def secondsToFormatted(length):
+	return f"{math.floor(length / 60)}:{str(length % 60)}"
\ No newline at end of file
diff --git a/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer b/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer
new file mode 100644
index 0000000..2e72edc
--- /dev/null
+++ b/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer	
@@ -0,0 +1,16 @@
+import math
+from objects import glob
+
+def secondsToFormatted(length):
+  return f"{math.floor(length / 60)}:{str(length % 60)}"
+
+
+def setUserLastOsuVer(userID: int, osuVer: str):
+	"""
+	Set userID's osu ver
+
+	:param userID int: user id
+	:param osuVer str: osu ver
+	:return:
+	"""
+	glob.db.execute("UPDATE users SET osuver = %s WHERE id = %s LIMIT 1", [osuVer, userID])
\ No newline at end of file
-- 
2.20.1


From e83a1b5bf6494d4ed565f9d054a0d433b4f3ac26 Mon Sep 17 00:00:00 2001
From: Aoba's Phone <neko.aoba.suzukaze@gmail.com>
Date: Fri, 20 Dec 2019 08:56:23 +0700
Subject: [PATCH 51/88] ???

---
 helpers/kotrikhelper.py                          | 15 ++++++++++++++-
 ...elper.py~64f4109... feat: logging user osuVer | 16 ----------------
 2 files changed, 14 insertions(+), 17 deletions(-)
 delete mode 100644 helpers/kotrikhelper.py~64f4109... feat: logging user osuVer

diff --git a/helpers/kotrikhelper.py b/helpers/kotrikhelper.py
index 7c45ac7..07d2f20 100644
--- a/helpers/kotrikhelper.py
+++ b/helpers/kotrikhelper.py
@@ -1,4 +1,17 @@
 import math
+from objects import glob
 
 def secondsToFormatted(length):
-	return f"{math.floor(length / 60)}:{str(length % 60)}"
\ No newline at end of file
+	return f"{math.floor(length / 60)}:{str(length % 60)}"
+	
+def setUserLastOsuVer(userID: int, osuVer: str):
+	"""
+	Set userID's osu ver
+	Thanks to osu!Kurikku's pep.py
+	Commit ID: 64f4109b6507ee218fdaa7c3e45433df0a02a8e5
+	
+	:param userID int: user id
+	:param osuVer str: osu ver
+	:return:
+	"""
+	glob.db.execute("UPDATE users SET osuver = %s WHERE id = %s LIMIT 1", [osuVer, userID])
\ No newline at end of file
diff --git a/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer b/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer
deleted file mode 100644
index 2e72edc..0000000
--- a/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer	
+++ /dev/null
@@ -1,16 +0,0 @@
-import math
-from objects import glob
-
-def secondsToFormatted(length):
-  return f"{math.floor(length / 60)}:{str(length % 60)}"
-
-
-def setUserLastOsuVer(userID: int, osuVer: str):
-	"""
-	Set userID's osu ver
-
-	:param userID int: user id
-	:param osuVer str: osu ver
-	:return:
-	"""
-	glob.db.execute("UPDATE users SET osuver = %s WHERE id = %s LIMIT 1", [osuVer, userID])
\ No newline at end of file
-- 
2.20.1


From bc72d12e671ae6b8ab0599767abe8a464dcb12ff Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 21 Dec 2019 07:08:23 +0700
Subject: [PATCH 52/88] Update submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index 037e453..bea974a 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 037e453812e69fbc2a0eb8f2172ffa7bc1e15bd2
+Subproject commit bea974a4db6dcf08480b410b8b0866b2f4a04c4e
-- 
2.20.1


From e61d187897b18332b6942e641a7acc27dbd42f25 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 21 Dec 2019 07:54:42 +0700
Subject: [PATCH 53/88] Add RX to Tillerino

---
 constants/fokabotCommands.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 249c38b..e7ea136 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -559,8 +559,8 @@ def tillerinoMods(fro, chan, message):
 		modsList = [message[0][i:i+2].upper() for i in range(0, len(message[0]), 2)]
 		modsEnum = 0
 		for i in modsList:
-			if i not in ["NO", "NF", "EZ", "HD", "HR", "DT", "HT", "NC", "FL", "SO"]:
-				return "Invalid mods. Allowed mods: NO, NF, EZ, HD, HR, DT, HT, NC, FL, SO. Do not use spaces for multiple mods."
+			if i not in ["NO", "NF", "EZ", "HD", "HR", "DT", "HT", "NC", "FL", "SO", "RX"]:
+				return "Invalid mods. Allowed mods: NO, NF, EZ, HD, HR, DT, HT, NC, FL, SO, RX. Do not use spaces for multiple mods."
 			if i == "NO":
 				modsEnum = 0
 				break
@@ -582,6 +582,8 @@ def tillerinoMods(fro, chan, message):
 				modsEnum += mods.FLASHLIGHT
 			elif i == "SO":
 				modsEnum += mods.SPUNOUT
+			elif i == "RX":
+				modsEnum += mods.RELAX
 
 		# Set mods
 		token.tillerino[1] = modsEnum
-- 
2.20.1


From 879624e9b5ce1341c6460c1462c5c14b1dc34477 Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 22 Dec 2019 15:34:43 +0700
Subject: [PATCH 54/88] ppboard and scoreboard command

---
 common                       |  2 +-
 constants/fokabotCommands.py | 38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/common b/common
index bea974a..e551e08 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit bea974a4db6dcf08480b410b8b0866b2f4a04c4e
+Subproject commit e551e086d757bf5d1aecc4f0d5f15a8aa1654e2e
diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index e7ea136..6eda3f1 100644
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1304,6 +1304,36 @@ def postAnnouncement(fro, chan, message): # Post to #announce ingame
 	chat.sendMessage(glob.BOT_NAME, "#announce", announcement)
 	return "Announcement successfully sent."
 
+def usePPBoard(fro, chan, message):
+	messages = [m.lower() for m in message]
+	relax = message[0]
+
+	userID = userUtils.getID(fro)
+
+	if 'x' in relax:
+		rx = True
+	else:
+		rx = False
+
+	# Set PPBoard value in user_stats table
+	userUtils.setPPBoard(userID, rx)
+	return "You're using PPBoard in {rx}.".format(rx='relax' if rx else 'vanilla')
+
+def useScoreBoard(fro, chan, message):
+	messages = [m.lower() for m in message]
+	relax = message[0]
+
+	userID = userUtils.getID(fro)
+	
+	if 'x' in relax:
+		rx = True
+	else:
+		rx = False
+
+	# Set PPBoard value in user_stats table
+	userUtils.setScoreBoard(userID, rx)
+	return "You're using Scoreboard in {rx}.".format(rx='relax' if rx else 'vanilla')
+
 def whitelistUserPPLimit(fro, chan, message):
 	messages = [m.lower() for m in message]
 	target = message[0]
@@ -1408,6 +1438,14 @@ commands = [
 	}, {
 		"trigger": "!help",
 		"response": "Click (here)[https://ainu.pw/index.php?p=16&id=4] for full command list"
+	}, {
+		"trigger": "!ppboard",
+		"syntax": "<relax/vanilla>",
+		"callback": usePPBoard
+	}, {
+		"trigger": "!scoreboard",
+		"syntax": "<relax/vanilla>",
+		"callback": useScoreBoard
 	}, {
 		"trigger": "!whitelist",
 		"privileges": privileges.ADMIN_BAN_USERS,
-- 
2.20.1


From faefe70822d130e49b7cb243bddb833a7beacc4a Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sun, 22 Dec 2019 15:52:56 +0700
Subject: [PATCH 55/88] Updated submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index e551e08..480fbe1 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit e551e086d757bf5d1aecc4f0d5f15a8aa1654e2e
+Subproject commit 480fbe11994876ff1f717d7a32a53e20308d1fcf
-- 
2.20.1


From a7c9d355d222ae4fae45b47d8040594733596b5f Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 11:54:16 +0700
Subject: [PATCH 56/88] Anti Ainu Client

---
 events/loginEvent.py  |  6 ++++++
 helpers/aobaHelper.py | 10 ++++++++++
 2 files changed, 16 insertions(+)
 create mode 100644 helpers/aobaHelper.py

diff --git a/events/loginEvent.py b/events/loginEvent.py
index bcf08fb..383483d 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -7,6 +7,7 @@ from common.log import logUtils as log
 from common.ripple import userUtils
 from constants import exceptions
 from constants import serverPackets
+from helpers import aobaHelper
 from helpers import chatHelper as chat
 from helpers import countryHelper
 from helpers import locationHelper
@@ -221,6 +222,11 @@ def handle(tornadoRequest):
 		if userUtils.getCountry(userID) == "XX":
 			userUtils.setCountry(userID, countryLetters)
 
+		# BAN AINU CLIENT
+		if aobaHelper.getOsuVer(userID) == "0Ainu":
+			responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)")
+			userUtils.restrict(userID)
+
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
 			glob.streams.broadcast("main", serverPackets.userPanel(userID))
diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py
new file mode 100644
index 0000000..c333425
--- /dev/null
+++ b/helpers/aobaHelper.py
@@ -0,0 +1,10 @@
+from objects import glob
+
+def getOsuVer(userID):
+	"""
+	Get `userID`'s osu! version.
+
+	:param userID: user id
+	:return: osu! version
+	"""
+	return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"]
\ No newline at end of file
-- 
2.20.1


From 73f1e3efbf6aa2e52b6d56688598a4511502133f Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 12:41:39 +0700
Subject: [PATCH 57/88] Anti Ainu Client v2

---
 events/loginEvent.py | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/events/loginEvent.py b/events/loginEvent.py
index 383483d..0777642 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -223,9 +223,14 @@ def handle(tornadoRequest):
 			userUtils.setCountry(userID, countryLetters)
 
 		# BAN AINU CLIENT
-		if aobaHelper.getOsuVer(userID) == "0Ainu":
-			responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)")
-			userUtils.restrict(userID)
+		# 0Ainu = First Ainu build
+		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
+		# but still... no one play with b20190326.2 build right?
+		if aobaHelper.getOsuVer(userID) in ("0Ainu", "b20190326.2"):
+				if userUtils.isRestricted(userID):
+					userUtils.restrict(userID)
+				else:
+					responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)")
 
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
-- 
2.20.1


From adef8f741e67afd1069b91d8073ae0f4741c4e16 Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 12:42:34 +0700
Subject: [PATCH 58/88] TAB.

---
 events/loginEvent.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/events/loginEvent.py b/events/loginEvent.py
index 0777642..c8421c8 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -227,10 +227,10 @@ def handle(tornadoRequest):
 		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
 		# but still... no one play with b20190326.2 build right?
 		if aobaHelper.getOsuVer(userID) in ("0Ainu", "b20190326.2"):
-				if userUtils.isRestricted(userID):
-					userUtils.restrict(userID)
-				else:
-					responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)")
+			if userUtils.isRestricted(userID):
+				userUtils.restrict(userID)
+			else:
+				responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)")
 
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
-- 
2.20.1


From 88458a7d6e69e633f3d1de3f6c58c41baa0aec39 Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 12:56:38 +0700
Subject: [PATCH 59/88] OK, I fixed the issue

---
 events/loginEvent.py  | 12 ++++++++++++
 helpers/aobaHelper.py | 10 ++++++++++
 2 files changed, 22 insertions(+)
 create mode 100644 helpers/aobaHelper.py

diff --git a/events/loginEvent.py b/events/loginEvent.py
index bcf08fb..249c009 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -7,6 +7,7 @@ from common.log import logUtils as log
 from common.ripple import userUtils
 from constants import exceptions
 from constants import serverPackets
+from helpers import aobaHelper
 from helpers import chatHelper as chat
 from helpers import countryHelper
 from helpers import locationHelper
@@ -221,6 +222,17 @@ def handle(tornadoRequest):
 		if userUtils.getCountry(userID) == "XX":
 			userUtils.setCountry(userID, countryLetters)
 
+		# BAN AINU CLIENT
+		# 0Ainu = First Ainu build
+		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
+		# but still... no one play with b20190326.2 build right?
+
+		if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2"]:
+			if userUtils.isRestricted(userID):
+				responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
+			else:
+				userUtils.restrict(userID)
+
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
 			glob.streams.broadcast("main", serverPackets.userPanel(userID))
diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py
new file mode 100644
index 0000000..c333425
--- /dev/null
+++ b/helpers/aobaHelper.py
@@ -0,0 +1,10 @@
+from objects import glob
+
+def getOsuVer(userID):
+	"""
+	Get `userID`'s osu! version.
+
+	:param userID: user id
+	:return: osu! version
+	"""
+	return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"]
\ No newline at end of file
-- 
2.20.1


From 7abe3c7cf8e2b18276f979a101145d91dd3b3f5b Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 13:07:06 +0700
Subject: [PATCH 60/88] ID reset

---
 constants/serverPackets.py | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 91649f4..14d6b51 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -86,17 +86,14 @@ def userPanel(userID, force = False):
 	# Get user data
 	username = userToken.username
 	# Custom Timezone
-	if userID in (1000, 1055, 1114):
+	if userID in (1000):
 		timezone = 24+9
 	else:
 		timezone = 24+userToken.timeOffset
 	# Custom Countries for Users
 	# 111 = Japan
-	# 36 = Belarus
-	if userID in (1000, 1055, 1114):
+	if userID in (1000):
 		country = 111
-	elif userID == 1209:
-		country = 36
 	else:
 		country = userToken.country
 	gameRank = userToken.gameRank
@@ -111,11 +108,6 @@ def userPanel(userID, force = False):
 	# 1000 = Aoba's User ID
 	elif userID == 1000:
 		userRank |= userRanks.PEPPY
-	# 1000 = peppy's User ID
-	elif userID == 1114:
-		userRank |= userRanks.PEPPY
-	elif userID == 1055:
-		userRank |= userRanks.MOD
 	elif userUtils.isInPrivilegeGroup(userID, "developer"):
 		userRank |= userRanks.ADMIN
 	elif userUtils.isInPrivilegeGroup(userID, "chat mod"):
-- 
2.20.1


From 9c0313890be5762fba51ae08dcf81e6957d55164 Mon Sep 17 00:00:00 2001
From: Simon <45692977+denmarkistrash@users.noreply.github.com>
Date: Wed, 25 Dec 2019 16:14:40 +0100
Subject: [PATCH 61/88] i wanna join in :)

---
 constants/serverPackets.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 14d6b51..4ed660f 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -108,6 +108,8 @@ def userPanel(userID, force = False):
 	# 1000 = Aoba's User ID
 	elif userID == 1000:
 		userRank |= userRanks.PEPPY
+	elif userID == 1106;
+		userRank |= userRanks.PEPPY
 	elif userUtils.isInPrivilegeGroup(userID, "developer"):
 		userRank |= userRanks.ADMIN
 	elif userUtils.isInPrivilegeGroup(userID, "chat mod"):
-- 
2.20.1


From a9408a7a899169025d2c70b38b4fa624fcad6e8f Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 22:15:58 +0700
Subject: [PATCH 62/88] BIG BRUH MOMENT

---
 constants/serverPackets.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 4ed660f..11a8b98 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -86,13 +86,13 @@ def userPanel(userID, force = False):
 	# Get user data
 	username = userToken.username
 	# Custom Timezone
-	if userID in (1000):
+	if userID in (1000, 1106):
 		timezone = 24+9
 	else:
 		timezone = 24+userToken.timeOffset
 	# Custom Countries for Users
 	# 111 = Japan
-	if userID in (1000):
+	if userID in (1000, 1106):
 		country = 111
 	else:
 		country = userToken.country
-- 
2.20.1


From df3450bbcc81ed6788b0276407d35e7d75e47779 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Wed, 25 Dec 2019 22:16:32 +0700
Subject: [PATCH 63/88] Simon is foolish

---
 constants/serverPackets.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 11a8b98..9a161a7 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -108,7 +108,7 @@ def userPanel(userID, force = False):
 	# 1000 = Aoba's User ID
 	elif userID == 1000:
 		userRank |= userRanks.PEPPY
-	elif userID == 1106;
+	elif userID == 1106:
 		userRank |= userRanks.PEPPY
 	elif userUtils.isInPrivilegeGroup(userID, "developer"):
 		userRank |= userRanks.ADMIN
-- 
2.20.1


From 2e1e21516fb577eda2f76b3feb4f58b46a27209b Mon Sep 17 00:00:00 2001
From: Ainu Server <eaglejump.suzukaze@gmail.com>
Date: Thu, 26 Dec 2019 16:06:49 +0100
Subject: [PATCH 64/88] This is so annoying, let's shut it up.

---
 handlers/apiGetTheFuckOuttaHere.py | 12 ++++++++++++
 pep.py                             |  2 ++
 2 files changed, 14 insertions(+)
 create mode 100644 handlers/apiGetTheFuckOuttaHere.py

diff --git a/handlers/apiGetTheFuckOuttaHere.py b/handlers/apiGetTheFuckOuttaHere.py
new file mode 100644
index 0000000..a759a44
--- /dev/null
+++ b/handlers/apiGetTheFuckOuttaHere.py
@@ -0,0 +1,12 @@
+import tornado.gen
+import tornado.web
+
+from common.web import requestsManager
+
+
+class handler(requestsManager.asyncRequestHandler):
+	@tornado.web.asynchronous
+	@tornado.gen.engine
+	def asyncGet(self):
+		#self.set_status(404)
+		self.write("")
diff --git a/pep.py b/pep.py
index 2f932e9..1452f60 100644
--- a/pep.py
+++ b/pep.py
@@ -17,6 +17,7 @@ from common.log import logUtils as log
 from common.redis import pubSub
 from common.web import schiavo
 from handlers import apiFokabotMessageHandler
+from handlers import apiGetTheFuckOuttaHere
 from handlers import apiIsOnlineHandler
 from handlers import apiOnlineUsersHandler
 from handlers import apiServerStatusHandler
@@ -50,6 +51,7 @@ def make_app():
 		(r"/api/v1/ciTrigger", ciTriggerHandler.handler),
 		(r"/api/v1/verifiedStatus", apiVerifiedStatusHandler.handler),
 		(r"/api/v1/fokabotMessage", apiFokabotMessageHandler.handler),
+		(r"/api/v2/clients/.*", apiGetTheFuckOuttaHere.handler),
 		(r"/stress", heavyHandler.handler)
 	])
 
-- 
2.20.1


From 6404d970aa993028b46cdfc8e0817c740f861e8b Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 4 Jan 2020 18:16:43 +0700
Subject: [PATCH 65/88] Additional configuration

Signed-off-by: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
---
 helpers/configHelper.py |  9 +++++++--
 pep.py                  | 20 ++++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/helpers/configHelper.py b/helpers/configHelper.py
index 3bc47be..5501000 100644
--- a/helpers/configHelper.py
+++ b/helpers/configHelper.py
@@ -86,6 +86,8 @@ class config:
 
 			parsedConfig.get("localize", "enable")
 			parsedConfig.get("localize", "ipapiurl")
+
+			parsedConfig.get("custom", "config")
 			return True
 		except configparser.Error:
 			return False
@@ -119,12 +121,12 @@ class config:
 		self.config.set("server", "gzip", "1")
 		self.config.set("server", "gziplevel", "6")
 		self.config.set("server", "cikey", "changeme")
-		self.config.set("server", "letsapiurl", "http://.../letsapi")
+		self.config.set("server", "letsapiurl", "http://127.0.0.1:5002/letsapi")
 		self.config.set("server", "deltaurl", "delta.ppy.sh")
 		self.config.set("server", "publicdelta", "0")
 
 		self.config.add_section("cheesegull")
-		self.config.set("cheesegull", "apiurl", "http://cheesegu.ll/api")
+		self.config.set("cheesegull", "apiurl", "https://storage.ainu.pw/api")
 		self.config.set("cheesegull", "apikey", "")
 
 		self.config.add_section("debug")
@@ -156,6 +158,9 @@ 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()
diff --git a/pep.py b/pep.py
index 2f932e9..7ae0564 100644
--- a/pep.py
+++ b/pep.py
@@ -9,6 +9,9 @@ import tornado.web
 from raven.contrib.tornado import AsyncSentryClient
 import redis
 
+import json
+import shutil
+
 from common import generalUtils, agpl
 from common.constants import bcolors
 from common.db import dbConnector
@@ -86,6 +89,23 @@ if __name__ == "__main__":
 		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)
+			sys.exit()
+
 		# Create data folder if needed
 		consoleHelper.printNoNl("> Checking folders... ")
 		paths = [".data"]
-- 
2.20.1


From 87bfae193bd79afa163a035c5ac985c1faf2e04a Mon Sep 17 00:00:00 2001
From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
Date: Sat, 4 Jan 2020 18:27:24 +0700
Subject: [PATCH 66/88] lol

Signed-off-by: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com>
---
 pep.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pep.py b/pep.py
index 2a61b63..19a5a6e 100644
--- a/pep.py
+++ b/pep.py
@@ -96,7 +96,7 @@ if __name__ == "__main__":
 		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)
+				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:
-- 
2.20.1


From 3be3c4db7d5091a41bab67013d3cf46c6bfc533d Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Sun, 5 Jan 2020 20:50:32 +0000
Subject: [PATCH 67/88] Add anticheat mode

---
 common               |  2 +-
 events/loginEvent.py | 13 ++++++++-----
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/common b/common
index 480fbe1..aa8cd0b 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 480fbe11994876ff1f717d7a32a53e20308d1fcf
+Subproject commit aa8cd0bd871356d7dc31c2e42d4b18791f2096bb
diff --git a/events/loginEvent.py b/events/loginEvent.py
index 249c009..6efe4f2 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -225,13 +225,16 @@ def handle(tornadoRequest):
 		# BAN AINU CLIENT
 		# 0Ainu = First Ainu build
 		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
+		# b20191223.3 = Unknown Ainu Build
+		# b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Unknown Ainu Build 2
 		# but still... no one play with b20190326.2 build right?
 
-		if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2"]:
-			if userUtils.isRestricted(userID):
-				responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
-			else:
-				userUtils.restrict(userID)
+		if glob.conf.extra["mode"]["anticheat"]:
+			if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]:
+				if userUtils.isRestricted(userID):
+					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
+				else:
+					userUtils.restrict(userID)
 
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
-- 
2.20.1


From 8b3759940309d799e3ee28bb7473450bb93629d7 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Mon, 6 Jan 2020 16:12:16 +0700
Subject: [PATCH 68/88] Anti Ainu Client 2020

---
 events/loginEvent.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/events/loginEvent.py b/events/loginEvent.py
index 6efe4f2..b84cb3d 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -230,7 +230,13 @@ def handle(tornadoRequest):
 		# but still... no one play with b20190326.2 build right?
 
 		if glob.conf.extra["mode"]["anticheat"]:
-			if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]:
+			# Check Ainu New Year 2020 Client
+			if tornadoRequest.request.headers.get("ainu") == "happy":
+				if userUtils.isRestricted(userID):
+					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client... Happy New Year 2020 and Enjoy your restriction :)"))
+				else:
+					userUtils.restrict(userID)
+			elif aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]:
 				if userUtils.isRestricted(userID):
 					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
 				else:
-- 
2.20.1


From 5f3a4071d504871cdfe06424b5245b1cca6b70d6 Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 04:53:17 +0000
Subject: [PATCH 69/88] More AntiCheat stuff? idk what i do

---
 common               |  2 +-
 events/loginEvent.py | 52 ++++++++++++++++++++++++++++++++------------
 2 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/common b/common
index aa8cd0b..a6e28da 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit aa8cd0bd871356d7dc31c2e42d4b18791f2096bb
+Subproject commit a6e28daf8f85b152b0fb5264ed59ee45dedf0b0c
diff --git a/events/loginEvent.py b/events/loginEvent.py
index 6efe4f2..2eeada3 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -163,6 +163,41 @@ def handle(tornadoRequest):
 				# We are mod/admin, send warning notification and continue
 				responseToken.enqueue(serverPackets.notification("Bancho is in maintenance mode. Only mods/admins have full access to the server.\nType !system maintenance off in chat to turn off maintenance mode."))
 
+		# BAN CUSTOM CHEAT CLIENTS
+		# 0Ainu = First Ainu build
+		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
+		# b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Ainu Aoba's Birthday Build
+		# b20191223.3 = Unknown Ainu build? (Taken from most users osuver in cookiezi.pw)
+		# b20190226.2 = hqOsu (hq-af)
+		if glob.conf.extra["mode"]["anticheat"]:
+			# Ainu Client 2020 update
+			if tornadoRequest.request.headers.get("ainu") == "happy":
+				log.info("Account {} tried to use Ainu Client 2020!".format(userID))
+				if userUtils.isRestricted(userID):
+					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client... Happy New Year 2020 and Enjoy your restriction :)"))
+				else:
+					glob.tokens.deleteToken(userID)
+					userUtils.restrict(userID)
+					raise exceptions.loginCheatClientsException()
+			# Ainu Client 2019
+			elif aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]:
+				log.info("Account {} tried to use Ainu Client!".format(userID))
+				if userUtils.isRestricted(userID):
+					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
+				else:
+					glob.tokens.deleteToken(userID)
+					userUtils.restrict(userID)
+					raise exceptions.loginCheatClientsException()
+			# hqOsu
+			elif aobaHelper.getOsuVer(userID) == "b20190226.2":
+				log.info("Account {} tried to use hqOsu!".format(userID))
+				if userUtils.isRestricted(userID):
+					responseToken.enqueue(serverPackets.notification("Trying to use hqOsu in here? Well... No, sorry. We don't allow cheats here. Go play https://cookiezi.pw or others cheat server."))
+				else:
+					glob.tokens.deleteToken(userID)
+					userUtils.restrict(userID)
+					raise exceptions.loginCheatClientsException()
+
 		# Send all needed login packets
 		responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds))
 		responseToken.enqueue(serverPackets.userID(userID))
@@ -222,20 +257,6 @@ def handle(tornadoRequest):
 		if userUtils.getCountry(userID) == "XX":
 			userUtils.setCountry(userID, countryLetters)
 
-		# BAN AINU CLIENT
-		# 0Ainu = First Ainu build
-		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
-		# b20191223.3 = Unknown Ainu Build
-		# b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Unknown Ainu Build 2
-		# but still... no one play with b20190326.2 build right?
-
-		if glob.conf.extra["mode"]["anticheat"]:
-			if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]:
-				if userUtils.isRestricted(userID):
-					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
-				else:
-					userUtils.restrict(userID)
-
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
 			glob.streams.broadcast("main", serverPackets.userPanel(userID))
@@ -258,6 +279,9 @@ def handle(tornadoRequest):
 	except exceptions.loginLockedException:
 		# Login banned error packet
 		responseData += serverPackets.loginLocked()
+	except exceptions.loginCheatClientsException:
+		# Banned for logging in with cheats
+		responseData += serverPackets.loginCheats()
 	except exceptions.banchoMaintenanceException:
 		# Bancho is in maintenance mode
 		responseData = bytes()
-- 
2.20.1


From b48667e78d73e56e39d1f386698b700ad2161ec9 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 11:57:58 +0700
Subject: [PATCH 70/88] what?!

---
 events/loginEvent.py | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/events/loginEvent.py b/events/loginEvent.py
index 3b84874..2eeada3 100644
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -257,29 +257,6 @@ def handle(tornadoRequest):
 		if userUtils.getCountry(userID) == "XX":
 			userUtils.setCountry(userID, countryLetters)
 
-<<<<<<< HEAD
-=======
-		# BAN AINU CLIENT
-		# 0Ainu = First Ainu build
-		# b20190326.2 = Ainu build 2 (MPGH PAGE 10)
-		# b20191223.3 = Unknown Ainu Build
-		# b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Unknown Ainu Build 2
-		# but still... no one play with b20190326.2 build right?
-
-		if glob.conf.extra["mode"]["anticheat"]:
-			# Check Ainu New Year 2020 Client
-			if tornadoRequest.request.headers.get("ainu") == "happy":
-				if userUtils.isRestricted(userID):
-					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client... Happy New Year 2020 and Enjoy your restriction :)"))
-				else:
-					userUtils.restrict(userID)
-			elif aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]:
-				if userUtils.isRestricted(userID):
-					responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)"))
-				else:
-					userUtils.restrict(userID)
-
->>>>>>> 8b3759940309d799e3ee28bb7473450bb93629d7
 		# Send to everyone our userpanel if we are not restricted or tournament
 		if not responseToken.restricted:
 			glob.streams.broadcast("main", serverPackets.userPanel(userID))
-- 
2.20.1


From 125acb58e370d5bf35cbec79fffb7b12759e17ca Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 11:59:24 +0700
Subject: [PATCH 71/88] OK, I forgot this.

---
 constants/exceptions.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/constants/exceptions.py b/constants/exceptions.py
index 258ae0b..525106b 100644
--- a/constants/exceptions.py
+++ b/constants/exceptions.py
@@ -82,6 +82,9 @@ class haxException(Exception):
 class forceUpdateException(Exception):
 	pass
 
+class loginCheatClientsException(Exception):
+	pass
+
 class loginLockedException(Exception):
 	pass
 
@@ -107,4 +110,4 @@ class wrongChannelException(Exception):
 	pass
 
 class periodicLoopException(Exception):
-	pass
\ No newline at end of file
+	pass
-- 
2.20.1


From 3ca755f27d2b2d7b2848ffedfb1f308a81fe4233 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 12:00:52 +0700
Subject: [PATCH 72/88] Add loginCheats

---
 constants/serverPackets.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 9a161a7..8d45084 100644
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -27,6 +27,11 @@ def loginLocked():
 def loginError():
 	return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]])
 
+def loginCheats():
+	packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
+	packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.")
+	return packets
+
 def needSupporter():
 	return packetHelper.buildPacket(packetIDs.server_userID, [[-6, dataTypes.SINT32]])
 
-- 
2.20.1


From cb9e4ccb6c19a9655c98fafef4b47e2d1ed1116f Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 05:02:17 +0000
Subject: [PATCH 73/88] I need to commit what? :thonk:

---
 .gitignore                                 | 0
 .gitmodules                                | 0
 .landscape.yaml                            | 0
 LICENSE                                    | 0
 README.md                                  | 0
 constants/__init__.py                      | 0
 constants/clientPackets.py                 | 0
 constants/dataTypes.py                     | 0
 constants/exceptions.py                    | 0
 constants/fokabotCommands.py               | 0
 constants/matchModModes.py                 | 0
 constants/matchScoringTypes.py             | 0
 constants/matchTeamTypes.py                | 0
 constants/matchTeams.py                    | 0
 constants/messageTemplates.py              | 0
 constants/packetIDs.py                     | 0
 constants/serverPackets.py                 | 0
 constants/slotStatuses.py                  | 0
 constants/userRanks.py                     | 0
 events/__init__.py                         | 0
 events/cantSpectateEvent.py                | 0
 events/changeActionEvent.py                | 0
 events/changeMatchModsEvent.py             | 0
 events/changeMatchPasswordEvent.py         | 0
 events/changeMatchSettingsEvent.py         | 0
 events/changeSlotEvent.py                  | 0
 events/channelJoinEvent.py                 | 0
 events/channelPartEvent.py                 | 0
 events/createMatchEvent.py                 | 0
 events/friendAddEvent.py                   | 0
 events/friendRemoveEvent.py                | 0
 events/joinLobbyEvent.py                   | 0
 events/joinMatchEvent.py                   | 0
 events/loginEvent.py                       | 0
 events/logoutEvent.py                      | 0
 events/matchBeatmapEvent.py                | 0
 events/matchChangeTeamEvent.py             | 0
 events/matchCompleteEvent.py               | 0
 events/matchFailedEvent.py                 | 0
 events/matchFramesEvent.py                 | 0
 events/matchHasBeatmapEvent.py             | 0
 events/matchInviteEvent.py                 | 0
 events/matchLockEvent.py                   | 0
 events/matchNoBeatmapEvent.py              | 0
 events/matchPlayerLoadEvent.py             | 0
 events/matchReadyEvent.py                  | 0
 events/matchSkipEvent.py                   | 0
 events/matchStartEvent.py                  | 0
 events/matchTransferHostEvent.py           | 0
 events/partLobbyEvent.py                   | 0
 events/partMatchEvent.py                   | 0
 events/requestStatusUpdateEvent.py         | 0
 events/sendPrivateMessageEvent.py          | 0
 events/sendPublicMessageEvent.py           | 0
 events/setAwayMessageEvent.py              | 0
 events/spectateFramesEvent.py              | 0
 events/startSpectatingEvent.py             | 0
 events/stopSpectatingEvent.py              | 0
 events/tournamentJoinMatchChannelEvent.py  | 0
 events/tournamentLeaveMatchChannelEvent.py | 0
 events/tournamentMatchInfoRequestEvent.py  | 0
 events/userPanelRequestEvent.py            | 0
 events/userStatsRequestEvent.py            | 0
 filters.txt                                | 0
 full_build.sh                              | 0
 handlers/__init__.py                       | 0
 handlers/apiFokabotMessageHandler.py       | 0
 handlers/apiGetTheFuckOuttaHere.py         | 0
 handlers/apiIsOnlineHandler.py             | 0
 handlers/apiOnlineUsersHandler.py          | 0
 handlers/apiServerStatusHandler.py         | 0
 handlers/apiVerifiedStatusHandler.py       | 0
 handlers/chatHelper.py                     | 0
 handlers/ciTriggerHandler.py               | 0
 handlers/heavyHandler.py                   | 0
 handlers/mainHandler.pyx                   | 0
 helpers/__init__.py                        | 0
 helpers/aobaHelper.py                      | 0
 helpers/chatHelper.py                      | 0
 helpers/configHelper.py                    | 0
 helpers/consoleHelper.py                   | 0
 helpers/countryHelper.py                   | 0
 helpers/cryptHelper.py                     | 0
 helpers/kotrikhelper.py                    | 0
 helpers/locationHelper.py                  | 0
 helpers/packetHelper.pyx                   | 0
 helpers/systemHelper.py                    | 0
 irc/__init__.py                            | 0
 irc/ircserver.py                           | 0
 objects/__init__.py                        | 0
 objects/banchoConfig.py                    | 0
 objects/channel.py                         | 0
 objects/channelList.py                     | 0
 objects/chatFilters.py                     | 0
 objects/fokabot.py                         | 0
 objects/glob.py                            | 0
 objects/match.py                           | 0
 objects/matchList.py                       | 0
 objects/osuToken.py                        | 0
 objects/stream.py                          | 0
 objects/streamList.py                      | 0
 objects/tokenList.py                       | 0
 pep.py                                     | 0
 pubSubHandlers/__init__.py                 | 0
 pubSubHandlers/banHandler.py               | 0
 pubSubHandlers/changeUsernameHandler.py    | 0
 pubSubHandlers/disconnectHandler.py        | 0
 pubSubHandlers/notificationHandler.py      | 0
 pubSubHandlers/setMainMenuIconHandler.py   | 0
 pubSubHandlers/updateSilenceHandler.py     | 0
 pubSubHandlers/updateStatsHandler.py       | 0
 requirements.txt                           | 0
 setup.py                                   | 0
 version                                    | 0
 114 files changed, 0 insertions(+), 0 deletions(-)
 mode change 100644 => 100755 .gitignore
 mode change 100644 => 100755 .gitmodules
 mode change 100644 => 100755 .landscape.yaml
 mode change 100644 => 100755 LICENSE
 mode change 100644 => 100755 README.md
 mode change 100644 => 100755 constants/__init__.py
 mode change 100644 => 100755 constants/clientPackets.py
 mode change 100644 => 100755 constants/dataTypes.py
 mode change 100644 => 100755 constants/exceptions.py
 mode change 100644 => 100755 constants/fokabotCommands.py
 mode change 100644 => 100755 constants/matchModModes.py
 mode change 100644 => 100755 constants/matchScoringTypes.py
 mode change 100644 => 100755 constants/matchTeamTypes.py
 mode change 100644 => 100755 constants/matchTeams.py
 mode change 100644 => 100755 constants/messageTemplates.py
 mode change 100644 => 100755 constants/packetIDs.py
 mode change 100644 => 100755 constants/serverPackets.py
 mode change 100644 => 100755 constants/slotStatuses.py
 mode change 100644 => 100755 constants/userRanks.py
 mode change 100644 => 100755 events/__init__.py
 mode change 100644 => 100755 events/cantSpectateEvent.py
 mode change 100644 => 100755 events/changeActionEvent.py
 mode change 100644 => 100755 events/changeMatchModsEvent.py
 mode change 100644 => 100755 events/changeMatchPasswordEvent.py
 mode change 100644 => 100755 events/changeMatchSettingsEvent.py
 mode change 100644 => 100755 events/changeSlotEvent.py
 mode change 100644 => 100755 events/channelJoinEvent.py
 mode change 100644 => 100755 events/channelPartEvent.py
 mode change 100644 => 100755 events/createMatchEvent.py
 mode change 100644 => 100755 events/friendAddEvent.py
 mode change 100644 => 100755 events/friendRemoveEvent.py
 mode change 100644 => 100755 events/joinLobbyEvent.py
 mode change 100644 => 100755 events/joinMatchEvent.py
 mode change 100644 => 100755 events/loginEvent.py
 mode change 100644 => 100755 events/logoutEvent.py
 mode change 100644 => 100755 events/matchBeatmapEvent.py
 mode change 100644 => 100755 events/matchChangeTeamEvent.py
 mode change 100644 => 100755 events/matchCompleteEvent.py
 mode change 100644 => 100755 events/matchFailedEvent.py
 mode change 100644 => 100755 events/matchFramesEvent.py
 mode change 100644 => 100755 events/matchHasBeatmapEvent.py
 mode change 100644 => 100755 events/matchInviteEvent.py
 mode change 100644 => 100755 events/matchLockEvent.py
 mode change 100644 => 100755 events/matchNoBeatmapEvent.py
 mode change 100644 => 100755 events/matchPlayerLoadEvent.py
 mode change 100644 => 100755 events/matchReadyEvent.py
 mode change 100644 => 100755 events/matchSkipEvent.py
 mode change 100644 => 100755 events/matchStartEvent.py
 mode change 100644 => 100755 events/matchTransferHostEvent.py
 mode change 100644 => 100755 events/partLobbyEvent.py
 mode change 100644 => 100755 events/partMatchEvent.py
 mode change 100644 => 100755 events/requestStatusUpdateEvent.py
 mode change 100644 => 100755 events/sendPrivateMessageEvent.py
 mode change 100644 => 100755 events/sendPublicMessageEvent.py
 mode change 100644 => 100755 events/setAwayMessageEvent.py
 mode change 100644 => 100755 events/spectateFramesEvent.py
 mode change 100644 => 100755 events/startSpectatingEvent.py
 mode change 100644 => 100755 events/stopSpectatingEvent.py
 mode change 100644 => 100755 events/tournamentJoinMatchChannelEvent.py
 mode change 100644 => 100755 events/tournamentLeaveMatchChannelEvent.py
 mode change 100644 => 100755 events/tournamentMatchInfoRequestEvent.py
 mode change 100644 => 100755 events/userPanelRequestEvent.py
 mode change 100644 => 100755 events/userStatsRequestEvent.py
 mode change 100644 => 100755 filters.txt
 mode change 100644 => 100755 full_build.sh
 mode change 100644 => 100755 handlers/__init__.py
 mode change 100644 => 100755 handlers/apiFokabotMessageHandler.py
 mode change 100644 => 100755 handlers/apiGetTheFuckOuttaHere.py
 mode change 100644 => 100755 handlers/apiIsOnlineHandler.py
 mode change 100644 => 100755 handlers/apiOnlineUsersHandler.py
 mode change 100644 => 100755 handlers/apiServerStatusHandler.py
 mode change 100644 => 100755 handlers/apiVerifiedStatusHandler.py
 mode change 100644 => 100755 handlers/chatHelper.py
 mode change 100644 => 100755 handlers/ciTriggerHandler.py
 mode change 100644 => 100755 handlers/heavyHandler.py
 mode change 100644 => 100755 handlers/mainHandler.pyx
 mode change 100644 => 100755 helpers/__init__.py
 mode change 100644 => 100755 helpers/aobaHelper.py
 mode change 100644 => 100755 helpers/chatHelper.py
 mode change 100644 => 100755 helpers/configHelper.py
 mode change 100644 => 100755 helpers/consoleHelper.py
 mode change 100644 => 100755 helpers/countryHelper.py
 mode change 100644 => 100755 helpers/cryptHelper.py
 mode change 100644 => 100755 helpers/kotrikhelper.py
 mode change 100644 => 100755 helpers/locationHelper.py
 mode change 100644 => 100755 helpers/packetHelper.pyx
 mode change 100644 => 100755 helpers/systemHelper.py
 mode change 100644 => 100755 irc/__init__.py
 mode change 100644 => 100755 irc/ircserver.py
 mode change 100644 => 100755 objects/__init__.py
 mode change 100644 => 100755 objects/banchoConfig.py
 mode change 100644 => 100755 objects/channel.py
 mode change 100644 => 100755 objects/channelList.py
 mode change 100644 => 100755 objects/chatFilters.py
 mode change 100644 => 100755 objects/fokabot.py
 mode change 100644 => 100755 objects/glob.py
 mode change 100644 => 100755 objects/match.py
 mode change 100644 => 100755 objects/matchList.py
 mode change 100644 => 100755 objects/osuToken.py
 mode change 100644 => 100755 objects/stream.py
 mode change 100644 => 100755 objects/streamList.py
 mode change 100644 => 100755 objects/tokenList.py
 mode change 100644 => 100755 pep.py
 mode change 100644 => 100755 pubSubHandlers/__init__.py
 mode change 100644 => 100755 pubSubHandlers/banHandler.py
 mode change 100644 => 100755 pubSubHandlers/changeUsernameHandler.py
 mode change 100644 => 100755 pubSubHandlers/disconnectHandler.py
 mode change 100644 => 100755 pubSubHandlers/notificationHandler.py
 mode change 100644 => 100755 pubSubHandlers/setMainMenuIconHandler.py
 mode change 100644 => 100755 pubSubHandlers/updateSilenceHandler.py
 mode change 100644 => 100755 pubSubHandlers/updateStatsHandler.py
 mode change 100644 => 100755 requirements.txt
 mode change 100644 => 100755 setup.py
 mode change 100644 => 100755 version

diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
diff --git a/.gitmodules b/.gitmodules
old mode 100644
new mode 100755
diff --git a/.landscape.yaml b/.landscape.yaml
old mode 100644
new mode 100755
diff --git a/LICENSE b/LICENSE
old mode 100644
new mode 100755
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
diff --git a/constants/__init__.py b/constants/__init__.py
old mode 100644
new mode 100755
diff --git a/constants/clientPackets.py b/constants/clientPackets.py
old mode 100644
new mode 100755
diff --git a/constants/dataTypes.py b/constants/dataTypes.py
old mode 100644
new mode 100755
diff --git a/constants/exceptions.py b/constants/exceptions.py
old mode 100644
new mode 100755
diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
old mode 100644
new mode 100755
diff --git a/constants/matchModModes.py b/constants/matchModModes.py
old mode 100644
new mode 100755
diff --git a/constants/matchScoringTypes.py b/constants/matchScoringTypes.py
old mode 100644
new mode 100755
diff --git a/constants/matchTeamTypes.py b/constants/matchTeamTypes.py
old mode 100644
new mode 100755
diff --git a/constants/matchTeams.py b/constants/matchTeams.py
old mode 100644
new mode 100755
diff --git a/constants/messageTemplates.py b/constants/messageTemplates.py
old mode 100644
new mode 100755
diff --git a/constants/packetIDs.py b/constants/packetIDs.py
old mode 100644
new mode 100755
diff --git a/constants/serverPackets.py b/constants/serverPackets.py
old mode 100644
new mode 100755
diff --git a/constants/slotStatuses.py b/constants/slotStatuses.py
old mode 100644
new mode 100755
diff --git a/constants/userRanks.py b/constants/userRanks.py
old mode 100644
new mode 100755
diff --git a/events/__init__.py b/events/__init__.py
old mode 100644
new mode 100755
diff --git a/events/cantSpectateEvent.py b/events/cantSpectateEvent.py
old mode 100644
new mode 100755
diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py
old mode 100644
new mode 100755
diff --git a/events/changeMatchModsEvent.py b/events/changeMatchModsEvent.py
old mode 100644
new mode 100755
diff --git a/events/changeMatchPasswordEvent.py b/events/changeMatchPasswordEvent.py
old mode 100644
new mode 100755
diff --git a/events/changeMatchSettingsEvent.py b/events/changeMatchSettingsEvent.py
old mode 100644
new mode 100755
diff --git a/events/changeSlotEvent.py b/events/changeSlotEvent.py
old mode 100644
new mode 100755
diff --git a/events/channelJoinEvent.py b/events/channelJoinEvent.py
old mode 100644
new mode 100755
diff --git a/events/channelPartEvent.py b/events/channelPartEvent.py
old mode 100644
new mode 100755
diff --git a/events/createMatchEvent.py b/events/createMatchEvent.py
old mode 100644
new mode 100755
diff --git a/events/friendAddEvent.py b/events/friendAddEvent.py
old mode 100644
new mode 100755
diff --git a/events/friendRemoveEvent.py b/events/friendRemoveEvent.py
old mode 100644
new mode 100755
diff --git a/events/joinLobbyEvent.py b/events/joinLobbyEvent.py
old mode 100644
new mode 100755
diff --git a/events/joinMatchEvent.py b/events/joinMatchEvent.py
old mode 100644
new mode 100755
diff --git a/events/loginEvent.py b/events/loginEvent.py
old mode 100644
new mode 100755
diff --git a/events/logoutEvent.py b/events/logoutEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchBeatmapEvent.py b/events/matchBeatmapEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchChangeTeamEvent.py b/events/matchChangeTeamEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchCompleteEvent.py b/events/matchCompleteEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchFailedEvent.py b/events/matchFailedEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchFramesEvent.py b/events/matchFramesEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchHasBeatmapEvent.py b/events/matchHasBeatmapEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchInviteEvent.py b/events/matchInviteEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchLockEvent.py b/events/matchLockEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchNoBeatmapEvent.py b/events/matchNoBeatmapEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchPlayerLoadEvent.py b/events/matchPlayerLoadEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchReadyEvent.py b/events/matchReadyEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchSkipEvent.py b/events/matchSkipEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchStartEvent.py b/events/matchStartEvent.py
old mode 100644
new mode 100755
diff --git a/events/matchTransferHostEvent.py b/events/matchTransferHostEvent.py
old mode 100644
new mode 100755
diff --git a/events/partLobbyEvent.py b/events/partLobbyEvent.py
old mode 100644
new mode 100755
diff --git a/events/partMatchEvent.py b/events/partMatchEvent.py
old mode 100644
new mode 100755
diff --git a/events/requestStatusUpdateEvent.py b/events/requestStatusUpdateEvent.py
old mode 100644
new mode 100755
diff --git a/events/sendPrivateMessageEvent.py b/events/sendPrivateMessageEvent.py
old mode 100644
new mode 100755
diff --git a/events/sendPublicMessageEvent.py b/events/sendPublicMessageEvent.py
old mode 100644
new mode 100755
diff --git a/events/setAwayMessageEvent.py b/events/setAwayMessageEvent.py
old mode 100644
new mode 100755
diff --git a/events/spectateFramesEvent.py b/events/spectateFramesEvent.py
old mode 100644
new mode 100755
diff --git a/events/startSpectatingEvent.py b/events/startSpectatingEvent.py
old mode 100644
new mode 100755
diff --git a/events/stopSpectatingEvent.py b/events/stopSpectatingEvent.py
old mode 100644
new mode 100755
diff --git a/events/tournamentJoinMatchChannelEvent.py b/events/tournamentJoinMatchChannelEvent.py
old mode 100644
new mode 100755
diff --git a/events/tournamentLeaveMatchChannelEvent.py b/events/tournamentLeaveMatchChannelEvent.py
old mode 100644
new mode 100755
diff --git a/events/tournamentMatchInfoRequestEvent.py b/events/tournamentMatchInfoRequestEvent.py
old mode 100644
new mode 100755
diff --git a/events/userPanelRequestEvent.py b/events/userPanelRequestEvent.py
old mode 100644
new mode 100755
diff --git a/events/userStatsRequestEvent.py b/events/userStatsRequestEvent.py
old mode 100644
new mode 100755
diff --git a/filters.txt b/filters.txt
old mode 100644
new mode 100755
diff --git a/full_build.sh b/full_build.sh
old mode 100644
new mode 100755
diff --git a/handlers/__init__.py b/handlers/__init__.py
old mode 100644
new mode 100755
diff --git a/handlers/apiFokabotMessageHandler.py b/handlers/apiFokabotMessageHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/apiGetTheFuckOuttaHere.py b/handlers/apiGetTheFuckOuttaHere.py
old mode 100644
new mode 100755
diff --git a/handlers/apiIsOnlineHandler.py b/handlers/apiIsOnlineHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/apiServerStatusHandler.py b/handlers/apiServerStatusHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/apiVerifiedStatusHandler.py b/handlers/apiVerifiedStatusHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/chatHelper.py b/handlers/chatHelper.py
old mode 100644
new mode 100755
diff --git a/handlers/ciTriggerHandler.py b/handlers/ciTriggerHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/heavyHandler.py b/handlers/heavyHandler.py
old mode 100644
new mode 100755
diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx
old mode 100644
new mode 100755
diff --git a/helpers/__init__.py b/helpers/__init__.py
old mode 100644
new mode 100755
diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/chatHelper.py b/helpers/chatHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/configHelper.py b/helpers/configHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/consoleHelper.py b/helpers/consoleHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/countryHelper.py b/helpers/countryHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/cryptHelper.py b/helpers/cryptHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/kotrikhelper.py b/helpers/kotrikhelper.py
old mode 100644
new mode 100755
diff --git a/helpers/locationHelper.py b/helpers/locationHelper.py
old mode 100644
new mode 100755
diff --git a/helpers/packetHelper.pyx b/helpers/packetHelper.pyx
old mode 100644
new mode 100755
diff --git a/helpers/systemHelper.py b/helpers/systemHelper.py
old mode 100644
new mode 100755
diff --git a/irc/__init__.py b/irc/__init__.py
old mode 100644
new mode 100755
diff --git a/irc/ircserver.py b/irc/ircserver.py
old mode 100644
new mode 100755
diff --git a/objects/__init__.py b/objects/__init__.py
old mode 100644
new mode 100755
diff --git a/objects/banchoConfig.py b/objects/banchoConfig.py
old mode 100644
new mode 100755
diff --git a/objects/channel.py b/objects/channel.py
old mode 100644
new mode 100755
diff --git a/objects/channelList.py b/objects/channelList.py
old mode 100644
new mode 100755
diff --git a/objects/chatFilters.py b/objects/chatFilters.py
old mode 100644
new mode 100755
diff --git a/objects/fokabot.py b/objects/fokabot.py
old mode 100644
new mode 100755
diff --git a/objects/glob.py b/objects/glob.py
old mode 100644
new mode 100755
diff --git a/objects/match.py b/objects/match.py
old mode 100644
new mode 100755
diff --git a/objects/matchList.py b/objects/matchList.py
old mode 100644
new mode 100755
diff --git a/objects/osuToken.py b/objects/osuToken.py
old mode 100644
new mode 100755
diff --git a/objects/stream.py b/objects/stream.py
old mode 100644
new mode 100755
diff --git a/objects/streamList.py b/objects/streamList.py
old mode 100644
new mode 100755
diff --git a/objects/tokenList.py b/objects/tokenList.py
old mode 100644
new mode 100755
diff --git a/pep.py b/pep.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/__init__.py b/pubSubHandlers/__init__.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/banHandler.py b/pubSubHandlers/banHandler.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/changeUsernameHandler.py b/pubSubHandlers/changeUsernameHandler.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/disconnectHandler.py b/pubSubHandlers/disconnectHandler.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/notificationHandler.py b/pubSubHandlers/notificationHandler.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/setMainMenuIconHandler.py b/pubSubHandlers/setMainMenuIconHandler.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/updateSilenceHandler.py b/pubSubHandlers/updateSilenceHandler.py
old mode 100644
new mode 100755
diff --git a/pubSubHandlers/updateStatsHandler.py b/pubSubHandlers/updateStatsHandler.py
old mode 100644
new mode 100755
diff --git a/requirements.txt b/requirements.txt
old mode 100644
new mode 100755
diff --git a/setup.py b/setup.py
old mode 100644
new mode 100755
diff --git a/version b/version
old mode 100644
new mode 100755
-- 
2.20.1


From a2aca48e5f8cc6cc9dc860ded478795e093d1c2b Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 05:12:54 +0000
Subject: [PATCH 74/88] Where is the submodule

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index a6e28da..a6fbeb4 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit a6e28daf8f85b152b0fb5264ed59ee45dedf0b0c
+Subproject commit a6fbeb4533938afbd3db2efc7f792c229e417ed7
-- 
2.20.1


From cd751cccde3f7b811945046d04628d75b5d65b1e Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 05:16:09 +0000
Subject: [PATCH 75/88] Wrong build

---
 common | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common b/common
index a6fbeb4..8c621cb 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit a6fbeb4533938afbd3db2efc7f792c229e417ed7
+Subproject commit 8c621cbdee75bcf4603b8ad3bccdc61ad0813646
-- 
2.20.1


From 25d8896a37c39f534ba4732a1ce2f74de74a81cf Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 7 Jan 2020 10:48:18 +0000
Subject: [PATCH 76/88] RTX player when login with cheats client

---
 constants/serverPackets.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 8d45084..0e80d8f 100755
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -28,7 +28,9 @@ def loginError():
 	return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]])
 
 def loginCheats():
+	message = "You better quit cheating! >_< ~Aoba"
 	packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
+	packets += packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]])
 	packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.")
 	return packets
 
-- 
2.20.1


From 67d1690e358a9c4088d9a23fcf7379f8e4773265 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Thu, 9 Jan 2020 13:59:46 +0700
Subject: [PATCH 77/88] Change Bancho page

---
 handlers/mainHandler.pyx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx
index de6cc62..977e90d 100755
--- a/handlers/mainHandler.pyx
+++ b/handlers/mainHandler.pyx
@@ -244,6 +244,6 @@ class handler(requestsManager.asyncRequestHandler):
 	@tornado.gen.engine
 	def asyncGet(self):
 		html = 	"<html><head><title>Aoba's a cutie?</title>"
-		html += "<iframe src='https://ghostbin.co/paste/8j2ft' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>"
+		html += "<iframe src='https://ghostbin.co/paste/bwe8z' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>"
 		#Yes. I just wrote the credit... in it.
 		self.write(html)
-- 
2.20.1


From ed21cc1ee84e467502b98d271a4487c9cf267f7d Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Fri, 17 Jan 2020 00:24:07 +0000
Subject: [PATCH 78/88] Updated submodule

---
 common                     | 2 +-
 constants/exceptions.py    | 2 +-
 constants/serverPackets.py | 5 +++++
 events/loginEvent.py       | 2 +-
 4 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/common b/common
index 8c621cb..345e62a 160000
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 8c621cbdee75bcf4603b8ad3bccdc61ad0813646
+Subproject commit 345e62a7c5ec00e4698220574ebf6e7a1bc1ace7
diff --git a/constants/exceptions.py b/constants/exceptions.py
index 525106b..2543404 100755
--- a/constants/exceptions.py
+++ b/constants/exceptions.py
@@ -110,4 +110,4 @@ class wrongChannelException(Exception):
 	pass
 
 class periodicLoopException(Exception):
-	pass
+	pass
\ No newline at end of file
diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 0e80d8f..10e4f7d 100755
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -24,6 +24,11 @@ def loginLocked():
 	packets += notification("Well... Your account is locked but everything still in the website ya know? and uh... You can appeal us at Discord! (You can go to our website for the link!)")
 	return packets
 
+def loginCheats():
+	packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
+	packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.")
+	return packets
+
 def loginError():
 	return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]])
 
diff --git a/events/loginEvent.py b/events/loginEvent.py
index 2eeada3..47418b8 100755
--- a/events/loginEvent.py
+++ b/events/loginEvent.py
@@ -309,4 +309,4 @@ def handle(tornadoRequest):
 			log.info("Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP), "bunker")
 
 		# Return token string and data
-		return responseTokenString, responseData
+		return responseTokenString, responseData
\ No newline at end of file
-- 
2.20.1


From af02c7fd5fafa37901d50d2b586012300829cedf Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Fri, 17 Jan 2020 08:34:59 +0700
Subject: [PATCH 79/88] That ain't my fault! It's GitHub fault!

---
 constants/serverPackets.py | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/constants/serverPackets.py b/constants/serverPackets.py
index 10e4f7d..0e80d8f 100755
--- a/constants/serverPackets.py
+++ b/constants/serverPackets.py
@@ -24,11 +24,6 @@ def loginLocked():
 	packets += notification("Well... Your account is locked but everything still in the website ya know? and uh... You can appeal us at Discord! (You can go to our website for the link!)")
 	return packets
 
-def loginCheats():
-	packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
-	packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.")
-	return packets
-
 def loginError():
 	return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]])
 
-- 
2.20.1


From f20622749fb1c93057de4f5851f95fc76233a303 Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Wed, 29 Jan 2020 16:24:31 +0000
Subject: [PATCH 80/88] Add ability to use multiplayer commands for host and
 their refers

---
 constants/fokabotCommands.py | 33 +++++++++++++++++++++++++++++++++
 objects/fokabot.py           | 14 +++++++++++---
 objects/match.py             | 13 +++++++++++++
 3 files changed, 57 insertions(+), 3 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 6eda3f1..6a08834 100755
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -828,6 +828,36 @@ def getSpectatorHostUserIDFromChannel(chan):
 	return userID
 
 def multiplayer(fro, chan, message):
+	def mpListRefer():
+		_match = glob.matches.matches[getMatchIDFromChannel(chan)]
+		return str(_match.refers)
+
+	def mpAddRefer():
+		if len(message) < 2:
+			raise exceptions.invalidArgumentsException("Wrong syntax: !mp addref <user>")
+		_match = glob.matches.matches[getMatchIDFromChannel(chan)]
+		username = message[1].strip()
+		if not username:
+			raise exceptions.invalidArgumentsException("Please provide a username")
+		userID = userUtils.getIDSafe(username)
+		if userID is None:
+			raise exceptions.userNotFoundException("No such user")
+		_match.addRefer(userID)
+		return "Added {} to refers".format(username)
+
+	def mpRemoveRefer():
+		if len(message) < 2:
+			raise exceptions.invalidArgumentsException("Wrong syntax: !mp rmref <user>")
+		_match = glob.matches.matches[getMatchIDFromChannel(chan)]
+		username = message[1].strip()
+		if not username:
+			raise exceptions.invalidArgumentsException("Please provide a username")
+		userID = userUtils.getIDSafe(username)
+		if userID is None:
+			raise exceptions.userNotFoundException("No such user")
+		_match.removeRefer(userID)
+		return "Removed {} from refers".format(username)
+
 	def mpMake():
 		if len(message) < 2:
 			raise exceptions.invalidArgumentsException("Wrong syntax: !mp make <name>")
@@ -1153,6 +1183,9 @@ def multiplayer(fro, chan, message):
 
 	try:
 		subcommands = {
+			"listref": mpListRefer,
+			"addref": mpAddRefer,
+			"rmref": mpRemoveRefer,
 			"make": mpMake,
 			"close": mpClose,
 			"join": mpJoin,
diff --git a/objects/fokabot.py b/objects/fokabot.py
index 3ab179d..278577b 100755
--- a/objects/fokabot.py
+++ b/objects/fokabot.py
@@ -54,10 +54,18 @@ def fokabotResponse(fro, chan, message):
 			# message has triggered a command
 
 			# Make sure the user has right permissions
+			_userId = userUtils.getID(fro)
 			if i["privileges"] is not None:
-				# Rank = x
-				if userUtils.getPrivileges(userUtils.getID(fro)) & i["privileges"] == 0:
-					return False
+				if userUtils.getPrivileges(_userId) & i["privileges"] == 0:
+					if i["trigger"] == "!mp":
+						try:
+							refers = glob.matches.matches[fokabotCommands.getMatchIDFromChannel(chan)].refers
+							if not _userId in refers:
+								return False
+						except:
+							return False
+					else:
+						return False
 
 			# Check argument number
 			message = message.split(" ")
diff --git a/objects/match.py b/objects/match.py
index c0bed58..63d75d8 100755
--- a/objects/match.py
+++ b/objects/match.py
@@ -82,6 +82,19 @@ class match:
 		glob.channels.addHiddenChannel("#multi_{}".format(self.matchID))
 		log.info("MPROOM{}: {} match created!".format(self.matchID, "Tourney" if self.isTourney else "Normal"))
 
+		# Create referrs array that couls use !mp command from the bot.
+		self.refers = [hostUserID]
+
+	def addRefer(self, referUserId):
+		self.refers.append(referUserId)
+
+	def removeRefer(self, referUserId):
+		if self.userRefersToMatch(referUserId):
+			self.refers.remove(referUserId)
+
+	def userRefersToMatch(self, referUserId):
+		return referUserId in self.refers
+
 	def getMatchData(self, censored = False):
 		"""
 		Return binary match data structure for packetHelper
-- 
2.20.1


From 733d1eb5da944537b311a732ec4641e1ad36d1ea Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 11 Feb 2020 19:37:14 +0000
Subject: [PATCH 81/88] Improved Commands

---
 constants/fokabotCommands.py | 181 +++++++++++++++++++++++------------
 helpers/aobaHelper.py        | 133 ++++++++++++++++++++++++-
 helpers/configHelper.py      |   4 +
 3 files changed, 254 insertions(+), 64 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 6a08834..8bdc7f9 100755
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -14,6 +14,7 @@ from constants import exceptions, slotStatuses, matchModModes, matchTeams, match
 from common.constants import gameModes
 from common.constants import privileges
 from constants import serverPackets
+from helpers import aobaHelper
 from helpers import systemHelper
 from objects import fokabot
 from objects import glob
@@ -688,6 +689,22 @@ def tillerinoLast(fro, chan, message):
 		log.error(a)
 		return False
 
+
+def getBeatmapRequest(fro, chan, message): # Grab a random beatmap request. TODO: Add gamemode handling to this and !request
+	
+	request = glob.db.fetch("SELECT * FROM rank_requests LIMIT 1;")
+	if request is not None:
+		username = userUtils.getUsername(request['userid'])
+		mapData = glob.db.fetch("SELECT song_name, ranked FROM beatmaps WHERE beatmap_id = {} ORDER BY difficulty_std DESC LIMIT 1;".format(request['bid']))
+		glob.db.execute("DELETE FROM rank_requests WHERE id = {};".format(request['id']))
+		return "[https://ainu.pw/u/{userID} {username}] nominated beatmap: [https://osu.ppy.sh/b/{beatmapID} {songName}] for status change. {AinuBeatmapLink}The request has been deleted, so please decide it's status.".format(userID=request['userid'], username=username, beatmapID=request['bid'], songName=mapData['song_name'], AinuBeatmapLink='[https://ainu.pw/b/{} Ainu beatmap Link]. '.format(request['bid']))
+	else:
+		return "All nominations have been checked. Thank you for your hard work! :)"
+	
+	return "The beatmap ranking system has been reworked."
+
+
+
 def mm00(fro, chan, message):
 	random.seed()
 	return random.choice(["meme", "MA MAURO ESISTE?"])
@@ -1255,86 +1272,124 @@ def rtx(fro, chan, message):
 	userToken.enqueue(serverPackets.rtx(message))
 	return ":ok_hand:"
 	
-def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by cmyui and edited by Aoba
-	messages = [m.lower() for m in message]
+def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
+	# Put the gathered values into variables to be used later
+	messages = [m.lower() for m in message]  #!map rank set [something]
 	rankType = message[0]
 	mapType = message[1]
 	mapID = message[2]
-	
-	# Get persons username & ID
+	gameMode = message[3]
+
+	# Get persons userID, privileges, and token
 	userID = userUtils.getID(fro)
+	privileges = userUtils.getPrivileges(userID)
+	token = glob.tokens.getTokenFromUserID(userID)
 	name = userUtils.getUsername(userID)
-	
-	# What do I do here?
-	if rankType == 'rank':
-		rankTypeID = 2
-		freezeStatus = 1
-	elif rankType == 'unrank':
-		rankTypeID = 0
-		freezeStatus = 0
-		
+
+	# Only allow users to request maps in #admin channel or PMs with AC. Heavily reduced spam smh
+	if chan.startswith('#') and chan != '#admin' and not privileges & 8388608:
+		return "Map ranking is not permitted in regular channels, please do so in PMs with Mirai (or #admin if administrator)."
+
 	# Grab beatmapData from db
 	try:
-		beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
+		beatmapData = glob.db.fetch("SELECT beatmapset_id, song_name, ranked FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID))
 	except:
 		return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly."
-	
-	if mapType == 'set':
-		glob.db.execute(
-			"UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format(
-				rankTypeID, freezeStatus, beatmapData["beatmapset_id"]))
-		if freezeStatus == 1:
-			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps
-					WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(
-				beatmapData["beatmapset_id"]))
-		typeBM = 'set'
-	elif mapType == 'map':
-		glob.db.execute(
-			"UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format(
-				rankTypeID, freezeStatus, mapID))
-		if freezeStatus == 1:
-			glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps
-					WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(
-				beatmapData["beatmap_id"]))
-		typeBM = 'beatmap'
+
+	# Handle gameMode
+	if 's' in gameMode.lower() or ('o' in gameMode.lower() and not 'm' in gameMode.lower() and not 'c' in gameMode.lower() and not 't' in gameMode.lower()):
+		gameMode = "osu!"
+	elif 'c' in gameMode.lower():
+		gameMode = "osu!catch"
+	elif 'm' in gameMode.lower():
+		gameMode = "osu!mania"
+	elif 't' in gameMode.lower():
+		gameMode = "osu!taiko"
 	else:
-		return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'"
-	
-	# Announce that YOOOOOOO THIS MAP IS RANKED!!!
-	if rankType == "rank":
-		log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID),
-				True)
-		if mapType == 'set':
-			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType,
-																				beatmapData["beatmapset_id"],
-																				beatmapData["song_name"])
-		else:
-			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID,
-																			beatmapData["song_name"])
-		glob.db.execute(
-			"UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format(
-				beatmapData["beatmap_id"]))
+		return "Please enter a valid gamemode (std, ctb, taiko, mania)."
+
+	if 's' in mapType.lower():
+		mapType = 'set'
+	elif 'd' in mapType.lower() or 'm' in mapType.lower():
+		mapType = 'map'
 	else:
-		log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID),
-				True)
+		return "Please specify whether your request is a single difficulty, or a full set (map/set). Example: '!map unrank/rank/love set/map 256123 mania'."
+
+	# User has AdminManageBeatmaps perm
+	if privileges & 256:
+
+		# Figure out which ranked status we're requesting to
+		if 'r' in rankType.lower() and 'u' not in rankType.lower():
+			rankType = 'rank'
+			rankTypeID = 2
+			freezeStatus = 1
+		elif 'l' in rankType.lower():
+			rankType = 'love'
+			rankTypeID = 5
+			freezeStatus = 2
+		elif 'u' in rankType.lower() or 'g' in rankType.lower():
+			rankType = 'unrank'
+			rankTypeID = 0
+			freezeStatus = 0
+		else:
+			return "Please enter a valid ranked status (rank, love, unrank)."
+
+		if beatmapData['ranked'] == rankTypeID:
+			return "This map is already {}ed".format(rankType)
+
+
 		if mapType == 'set':
-			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType,
-																				beatmapData["beatmapset_id"],
-																				beatmapData["song_name"])
+			numDiffs = glob.db.fetch("SELECT COUNT(id) FROM beatmaps WHERE beatmapset_id = {}".format(beatmapData["beatmapset_id"]))
+			glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmapset_id = {} LIMIT {}".format(rankTypeID, freezeStatus, userID, beatmapData["beatmapset_id"], numDiffs["COUNT(id)"]))
 		else:
-			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID,
-																			beatmapData["song_name"])
+			glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, userID, mapID ))
 
-			glob.db.execute(
-				"UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format(
-					beatmapData["beatmap_id"]))
-		
-	chat.sendMessage(glob.BOT_NAME, "#ranked", msg)
-	return msg
+		# Announce / Log to admin panel logs when ranked status is changed
+		log.rap(userID, "has {}ed beatmap ({}): {} ({}), on gamemode {}.".format(rankType, mapType, beatmapData["song_name"], mapID, gameMode), True)
+		if mapType.lower() == 'set':
+			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"], gameMode)
+		else:
+			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, mapID, beatmapData["song_name"], gameMode)
+
+		chat.sendMessage(glob.BOT_NAME, "#announce", msg)
+		if rankType == "love":
+			status = "Loved"
+		elif rankType == "rank":
+			status = "Ranked"
+		else:
+			status = "Unranked"
+
+		if rankType == "love":
+			if mapType == "set":
+				webhookdesp = "{} (set) has been loved by {}".format(beatmapData["song_name"], name)
+			else:
+				webhookdesp = "{} has been loved by {}".format(beatmapData["song_name"], name)
+		else:
+			if mapType == "set":
+				webhookdesp = "{} (set) has been {}ed by {}".format(beatmapData["song_name"], rankType, name)
+			else:
+				webhookdesp = "{} has been {}ed by {}".format(beatmapData["song_name"], rankType, name)
+
+		webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was ranked on osu!Ainu")
+		webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID)))
+		webhook.set_title(title="New {} Map!".format(status), url='https://osu.ppy.sh/s/{}'.format(str(beatmapData["beatmapset_id"])))
+		webhook.set_desc(webhookdesp)
+		webhook.set_image("https://assets.ppy.sh/beatmaps/{}/covers/cover.jpg".format(str(beatmapData["beatmapset_id"])))
+		webhook.post()
+		return msg
 
 def postAnnouncement(fro, chan, message): # Post to #announce ingame
 	announcement = ' '.join(message[0:])
 	chat.sendMessage(glob.BOT_NAME, "#announce", announcement)
+	userID = userUtils.getID(fro)
+	name = userUtils.getUsername(userID)
+
+	webhook = aobaHelper.Webhook(glob.conf.config["discord"]["announcement"], color=0xadd8e6, footer="This announcement was posted in-game")
+	webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID)))
+	webhook.set_title(title="=-= ANNOUNCEMENT =-=")
+	webhook.set_desc(announcement)
+	webhook.post()
+
 	return "Announcement successfully sent."
 
 def usePPBoard(fro, chan, message):
@@ -1384,7 +1439,7 @@ def whitelistUserPPLimit(fro, chan, message):
 
 	userUtils.whitelistUserPPLimit(userID, rx)
 	return "{user} has been whitelisted from autorestrictions on {rx}.".format(user=target, rx='relax' if rx else 'vanilla')
-	
+
 def bloodcat(fro, chan, message):
 	try:
 		matchID = getMatchIDFromChannel(chan)
diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py
index c333425..296044a 100755
--- a/helpers/aobaHelper.py
+++ b/helpers/aobaHelper.py
@@ -1,3 +1,9 @@
+import json
+import requests
+import datetime
+
+from collections import defaultdict
+
 from objects import glob
 
 def getOsuVer(userID):
@@ -7,4 +13,129 @@ def getOsuVer(userID):
 	:param userID: user id
 	:return: osu! version
 	"""
-	return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"]
\ No newline at end of file
+	return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"]
+
+class Webhook:
+	def __init__(self, url, **kwargs):
+
+		"""
+		Initialise a Webhook Embed Object
+		Thanks to osu!fx's secret
+		Commit ID: 77a9136422ceaa792452556c5218a050207aa76a
+		"""
+
+		self.url = url 
+		self.msg = kwargs.get('msg')
+		self.color = kwargs.get('color')
+		self.title = kwargs.get('title')
+		self.title_url = kwargs.get('title_url')
+		self.author = kwargs.get('author')
+		self.author_icon = kwargs.get('author_icon')
+		self.author_url = kwargs.get('author_url')
+		self.desc = kwargs.get('desc')
+		self.fields = kwargs.get('fields', [])
+		self.image = kwargs.get('image')
+		self.thumbnail = kwargs.get('thumbnail')
+		self.footer = kwargs.get('footer')
+		self.footer_icon = kwargs.get('footer_icon')
+		self.ts = kwargs.get('ts')
+
+
+	def add_field(self,**kwargs):
+		'''Adds a field to `self.fields`'''
+		name = kwargs.get('name')
+		value = kwargs.get('value')
+		inline = kwargs.get('inline', True)
+
+		field = { 
+
+		'name' : name,
+		'value' : value,
+		'inline' : inline
+
+		}
+
+		self.fields.append(field)
+
+	def set_desc(self,desc):
+		self.desc = desc
+
+	def set_author(self, **kwargs):
+		self.author = kwargs.get('name')
+		self.author_icon = kwargs.get('icon')
+		self.author_url = kwargs.get('url')
+
+	def set_title(self, **kwargs):
+		self.title = kwargs.get('title')
+		self.title_url = kwargs.get('url')
+
+	def set_thumbnail(self, url):
+		self.thumbnail = url
+
+	def set_image(self, url):
+		self.image = url
+
+	def set_footer(self,**kwargs):
+		self.footer = kwargs.get('text')
+		self.footer_icon = kwargs.get('icon')
+		ts = kwargs.get('ts')
+		if ts == True:
+			self.ts = str(datetime.datetime.utcfromtimestamp(time.time()))
+		else:
+			self.ts = str(datetime.datetime.utcfromtimestamp(ts))
+
+
+	def del_field(self, index):
+		self.fields.pop(index)
+
+	@property
+	def json(self,*arg):
+		'''
+		Formats the data into a payload
+		'''
+
+		data = {}
+
+		data["embeds"] = []
+		embed = defaultdict(dict)
+		if self.msg: data["content"] = self.msg
+		if self.author: embed["author"]["name"] = self.author
+		if self.author_icon: embed["author"]["icon_url"] = self.author_icon
+		if self.author_url: embed["author"]["url"] = self.author_url
+		if self.color: embed["color"] = self.color 
+		if self.desc: embed["description"] = self.desc 
+		if self.title: embed["title"] = self.title 
+		if self.title_url: embed["url"] = self.title_url 
+		if self.image: embed["image"]['url'] = self.image
+		if self.thumbnail: embed["thumbnail"]['url'] = self.thumbnail
+		if self.footer: embed["footer"]['text'] = self.footer
+		if self.footer_icon: embed['footer']['icon_url'] = self.footer_icon
+		if self.ts: embed["timestamp"] = self.ts 
+
+		if self.fields:
+			embed["fields"] = []
+			for field in self.fields:
+				f = {}
+				f["name"] = field['name']
+				f["value"] = field['value']
+				f["inline"] = field['inline'] 
+				embed["fields"].append(f)
+
+		data["embeds"].append(dict(embed))
+
+		empty = all(not d for d in data["embeds"])
+
+		if empty and 'content' not in data:
+			print('You cant post an empty payload.')
+		if empty: data['embeds'] = []
+
+		return json.dumps(data, indent=4)
+
+	def post(self):
+		"""
+		Send the JSON formated object to the specified `self.url`.
+		"""
+
+		headers = {'Content-Type': 'application/json'}
+
+		requests.post(self.url, data=self.json, headers=headers)
diff --git a/helpers/configHelper.py b/helpers/configHelper.py
index 5501000..81821b3 100755
--- a/helpers/configHelper.py
+++ b/helpers/configHelper.py
@@ -75,6 +75,8 @@ class config:
 			parsedConfig.get("discord", "enable")
 			parsedConfig.get("discord", "boturl")
 			parsedConfig.get("discord", "devgroup")
+			parsedConfig.get("discord", "ranked")
+			parsedConfig.get("discord", "announcement")
 
 			parsedConfig.get("datadog", "enable")
 			parsedConfig.get("datadog", "apikey")
@@ -143,6 +145,8 @@ class config:
 		self.config.set("discord", "enable", "0")
 		self.config.set("discord", "boturl", "")
 		self.config.set("discord", "devgroup", "")
+		self.config.set("discord", "ranked", "")
+		self.config.set("discord", "announcement", "")
 
 		self.config.add_section("datadog")
 		self.config.set("datadog", "enable", "0")
-- 
2.20.1


From bae1dabb5a73eeaa652221a31dc6cafd7a773396 Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 11 Feb 2020 19:47:27 +0000
Subject: [PATCH 82/88] Removed gamemode from editMap func

---
 constants/fokabotCommands.py | 29 ++++++++---------------------
 1 file changed, 8 insertions(+), 21 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index 8bdc7f9..e29ff1c 100755
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1278,7 +1278,6 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 	rankType = message[0]
 	mapType = message[1]
 	mapID = message[2]
-	gameMode = message[3]
 
 	# Get persons userID, privileges, and token
 	userID = userUtils.getID(fro)
@@ -1296,18 +1295,6 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 	except:
 		return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly."
 
-	# Handle gameMode
-	if 's' in gameMode.lower() or ('o' in gameMode.lower() and not 'm' in gameMode.lower() and not 'c' in gameMode.lower() and not 't' in gameMode.lower()):
-		gameMode = "osu!"
-	elif 'c' in gameMode.lower():
-		gameMode = "osu!catch"
-	elif 'm' in gameMode.lower():
-		gameMode = "osu!mania"
-	elif 't' in gameMode.lower():
-		gameMode = "osu!taiko"
-	else:
-		return "Please enter a valid gamemode (std, ctb, taiko, mania)."
-
 	if 's' in mapType.lower():
 		mapType = 'set'
 	elif 'd' in mapType.lower() or 'm' in mapType.lower():
@@ -1345,19 +1332,19 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 			glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, userID, mapID ))
 
 		# Announce / Log to admin panel logs when ranked status is changed
-		log.rap(userID, "has {}ed beatmap ({}): {} ({}), on gamemode {}.".format(rankType, mapType, beatmapData["song_name"], mapID, gameMode), True)
+		log.rap(userID, "has {}ed beatmap ({}): {} ({})".format(rankType, mapType, beatmapData["song_name"], mapID), True)
 		if mapType.lower() == 'set':
-			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"], gameMode)
+			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"])
 		else:
-			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, mapID, beatmapData["song_name"], gameMode)
+			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, mapID, beatmapData["song_name"])
 
 		chat.sendMessage(glob.BOT_NAME, "#announce", msg)
 		if rankType == "love":
-			status = "Loved"
+			status = "loved"
 		elif rankType == "rank":
-			status = "Ranked"
+			status = "ranked"
 		else:
-			status = "Unranked"
+			status = "unranked"
 
 		if rankType == "love":
 			if mapType == "set":
@@ -1370,9 +1357,9 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 			else:
 				webhookdesp = "{} has been {}ed by {}".format(beatmapData["song_name"], rankType, name)
 
-		webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was ranked on osu!Ainu")
+		webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was {} on osu!Ainu".format(status))
 		webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID)))
-		webhook.set_title(title="New {} Map!".format(status), url='https://osu.ppy.sh/s/{}'.format(str(beatmapData["beatmapset_id"])))
+		webhook.set_title(title="New {} map!".format(status), url='https://osu.ppy.sh/s/{}'.format(str(beatmapData["beatmapset_id"])))
 		webhook.set_desc(webhookdesp)
 		webhook.set_image("https://assets.ppy.sh/beatmaps/{}/covers/cover.jpg".format(str(beatmapData["beatmapset_id"])))
 		webhook.post()
-- 
2.20.1


From 721e599a44fb1fe667fd5c9cfe49a9254c7fa5fc Mon Sep 17 00:00:00 2001
From: Ainu Server <neko.aoba.suzukaze@gmail.com>
Date: Tue, 11 Feb 2020 19:51:46 +0000
Subject: [PATCH 83/88] loveed

---
 constants/fokabotCommands.py | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index e29ff1c..f5fa090 100755
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1346,16 +1346,10 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 		else:
 			status = "unranked"
 
-		if rankType == "love":
-			if mapType == "set":
-				webhookdesp = "{} (set) has been loved by {}".format(beatmapData["song_name"], name)
-			else:
-				webhookdesp = "{} has been loved by {}".format(beatmapData["song_name"], name)
+		if mapType == "set":
+			webhookdesp = "{} (set) has been {} by {}".format(beatmapData["song_name"], status, name)
 		else:
-			if mapType == "set":
-				webhookdesp = "{} (set) has been {}ed by {}".format(beatmapData["song_name"], rankType, name)
-			else:
-				webhookdesp = "{} has been {}ed by {}".format(beatmapData["song_name"], rankType, name)
+			webhookdesp = "{} has been {} by {}".format(beatmapData["song_name"], status, name)
 
 		webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was {} on osu!Ainu".format(status))
 		webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID)))
-- 
2.20.1


From fa7bcddea461e7133e8c15ff4a0c397f9fc9877c Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Wed, 12 Feb 2020 08:20:50 +0700
Subject: [PATCH 84/88] Whoops Atoka

---
 constants/fokabotCommands.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index f5fa090..e69b64b 100755
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1287,7 +1287,7 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 
 	# Only allow users to request maps in #admin channel or PMs with AC. Heavily reduced spam smh
 	if chan.startswith('#') and chan != '#admin' and not privileges & 8388608:
-		return "Map ranking is not permitted in regular channels, please do so in PMs with Mirai (or #admin if administrator)."
+		return "Map ranking is not permitted in regular channels, please do so in PMs with AC (or #admin if administrator)."
 
 	# Grab beatmapData from db
 	try:
-- 
2.20.1


From 00e46c01fe39eaec326eed42e4f2f18eea3a80e0 Mon Sep 17 00:00:00 2001
From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com>
Date: Fri, 14 Feb 2020 21:17:03 +0700
Subject: [PATCH 85/88] loveed... again!?

---
 constants/fokabotCommands.py | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py
index e69b64b..c45e5da 100755
--- a/constants/fokabotCommands.py
+++ b/constants/fokabotCommands.py
@@ -1320,10 +1320,16 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 			freezeStatus = 0
 		else:
 			return "Please enter a valid ranked status (rank, love, unrank)."
-
+		
+		if rankType == "love":
+			status = "loved"
+		elif rankType == "rank":
+			status = "ranked"
+		else:
+			status = "unranked"
+		
 		if beatmapData['ranked'] == rankTypeID:
-			return "This map is already {}ed".format(rankType)
-
+			return "This map is already {}".format(status)
 
 		if mapType == 'set':
 			numDiffs = glob.db.fetch("SELECT COUNT(id) FROM beatmaps WHERE beatmapset_id = {}".format(beatmapData["beatmapset_id"]))
@@ -1332,20 +1338,14 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit
 			glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, userID, mapID ))
 
 		# Announce / Log to admin panel logs when ranked status is changed
-		log.rap(userID, "has {}ed beatmap ({}): {} ({})".format(rankType, mapType, beatmapData["song_name"], mapID), True)
+		log.rap(userID, "has {} beatmap ({}): {} ({})".format(status, mapType, beatmapData["song_name"], mapID), True)
 		if mapType.lower() == 'set':
-			msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"])
+			msg = "{} has {} beatmap set: [https://osu.ppy.sh/s/{} {}]".format(fro, status, beatmapData["beatmapset_id"], beatmapData["song_name"])
 		else:
-			msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, mapID, beatmapData["song_name"])
+			msg = "{} has {} beatmap: [https://osu.ppy.sh/s/{} {}]".format(fro, status, mapID, beatmapData["song_name"])
 
 		chat.sendMessage(glob.BOT_NAME, "#announce", msg)
-		if rankType == "love":
-			status = "loved"
-		elif rankType == "rank":
-			status = "ranked"
-		else:
-			status = "unranked"
-
+		
 		if mapType == "set":
 			webhookdesp = "{} (set) has been {} by {}".format(beatmapData["song_name"], status, name)
 		else:
-- 
2.20.1


From 3063dfb6e6910750b22ca06cfc48118f224d1b20 Mon Sep 17 00:00:00 2001
From: Chitsanupong C <lumilous@noreply.example.org>
Date: Sat, 7 Mar 2020 14:27:23 +0000
Subject: [PATCH 86/88] Update 'README.md'

---
 README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
 mode change 100755 => 100644 README.md

diff --git a/README.md b/README.md
old mode 100755
new mode 100644
index 9a03d58..8c5685d
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
+# Old Ripple bAncho?
+
 ## pep.py
-This is Ripple's bancho server but osu!thailand forked edition. It handles:
+This is Ripple's bancho server but osu!lumilous forked edition. It handles:
 - Client login
 - Online users listing and statuses
 - Public and private chat
-- 
2.20.1


From 0d3b8f2ec57cad747bdbf3d496d7de5f4a333959 Mon Sep 17 00:00:00 2001
From: Chitsanupong C <lumilous@noreply.example.org>
Date: Sun, 8 Mar 2020 08:21:28 +0000
Subject: [PATCH 87/88] Delete '.gitignore'

---
 .gitignore | 10 ----------
 1 file changed, 10 deletions(-)
 delete mode 100755 .gitignore

diff --git a/.gitignore b/.gitignore
deleted file mode 100755
index 808cd02..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-**/__pycache__
-**/build
-config.ini
-filters.txt
-.data
-.idea
-redistest.py
-*.c
-*.so
-.pyenv
\ No newline at end of file
-- 
2.20.1


From 646a2b2378815a6554d9907b67a9509b33c5bae1 Mon Sep 17 00:00:00 2001
From: Chitsanupong C <lumilous@noreply.example.org>
Date: Sun, 8 Mar 2020 08:21:42 +0000
Subject: [PATCH 88/88] Delete '.gitmodules'

---
 .gitmodules | 3 ---
 1 file changed, 3 deletions(-)
 delete mode 100755 .gitmodules

diff --git a/.gitmodules b/.gitmodules
deleted file mode 100755
index a4ead52..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "common"]
-	path = common
-	url = https://github.com/osuthailand/common.git
-- 
2.20.1


_Original pull request patch:_ ``` From 38adda759baac3a3773d4de5c421d64a742790b4 Mon Sep 17 00:00:00 2001 From: osu!thailand <43190206+osuthailand@users.noreply.github.com> Date: Wed, 6 Mar 2019 18:06:57 +0700 Subject: [PATCH 01/88] Bot name patch + Match room fix --- constants/fokabotCommands.py | 47 ++- constants/serverPackets.py | 6 +- events/changeMatchSettingsEvent.py | 8 +- events/createMatchEvent.py | 8 +- events/setAwayMessageEvent.py | 6 +- handlers/apiFokabotMessageHandler.py | 2 +- handlers/chatHelper.py | 433 +++++++++++++++++++++++++++ helpers/chatHelper.py | 16 +- objects/channel.py | 2 +- objects/fokabot.py | 1 + objects/glob.py | 2 + objects/match.py | 20 +- objects/osuToken.py | 8 +- 13 files changed, 502 insertions(+), 57 deletions(-) create mode 100644 handlers/chatHelper.py diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index d4b6b72..ff97fae 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -52,15 +52,14 @@ def instantRestart(fro, chan, message): def faq(fro, chan, message): # TODO: Unhardcode this messages = { - "rules": "Please make sure to check (Ripple's rules)[https://ripple.moe/doc/rules].", + "rules": "Please make sure to check (osu!thailand's rules)[https://bigtu.vip/doc/rules].", "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://blog.ripple.moe]!", - "changelog": "Check the (changelog)[https://ripple.moe/changelog] !", - "status": "Check the server status (here!)[https://status.ripple.moe]", + "github": "(osu!Ainu's Github page!)[https://github.com/osuripple/ripple]", + "discord": "(Join Ainu Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]", + "changelog": "Check the (changelog)[https://bigtu.vip/changelog] !", + "status": "Check the server status (here!)[https://status.bigtu.vip]", "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." @@ -137,7 +136,7 @@ def kickAll(fro, chan, message): def kick(fro, chan, message): # Get parameters target = message[0].lower() - if target == "fokabot": + if target == glob.BOT_NAME.lower(): return "Nope." # Get target token and make sure is connected @@ -153,11 +152,11 @@ def kick(fro, chan, message): return "{} has been kicked from the server.".format(target) def fokabotReconnect(fro, chan, message): - # Check if fokabot is already connected + # Check if the bot is already connected if glob.tokens.getTokenFromUserID(999) is not None: - return "Fokabot is already connected to Bancho" + return "{} is already connected to Bancho".format(glob.BOT_NAME) - # Fokabot is not connected, connect it + # Bot is not connected, connect it fokabot.connect() return False @@ -371,12 +370,12 @@ def systemStatus(fro, chan, message): # Final message letsVersion = glob.redis.get("lets:version") if letsVersion is None: - letsVersion = "\_(xd)_/" + letsVersion = "\_(-w-)_/" else: letsVersion = letsVersion.decode("utf-8") msg = "pep.py bancho server v{}\n".format(glob.VERSION) msg += "LETS scores server v{}\n".format(letsVersion) - msg += "made by the Ripple team\n" + msg += "made by the osu!thailand\n" msg += "\n" msg += "=== BANCHO STATS ===\n" msg += "Connected users: {}\n".format(data["connectedUsers"]) @@ -620,7 +619,7 @@ def tillerinoLast(fro, chan, message): rank = generalUtils.getRank(data["play_mode"], data["mods"], data["accuracy"], data["300_count"], data["100_count"], data["50_count"], data["misses_count"]) - ifPlayer = "{0} | ".format(fro) if chan != "FokaBot" else "" + ifPlayer = "{0} | ".format(fro) if chan != glob.BOT_NAME else "" ifFc = " (FC)" if data["max_combo"] == data["fc"] else " {0}x/{1}x".format(data["max_combo"], data["fc"]) beatmapLink = "[http://osu.ppy.sh/b/{1} {0}]".format(data["sn"], data["bid"]) @@ -738,7 +737,7 @@ def report(fro, chan, message): target = chat.fixUsernameForBancho(target) # Make sure the target is not foka - if target.lower() == "fokabot": + if target.lower() == glob.BOT_NAME.lower(): raise exceptions.invalidUserException() # Make sure the user exists @@ -762,10 +761,10 @@ def report(fro, chan, message): adminMsg = "{user} has reported {target} for {reason} ({info})".format(user=fro, target=target, reason=reason, info=additionalInfo) # Log report in #admin and on discord - chat.sendMessage("FokaBot", "#admin", adminMsg) + chat.sendMessage(glob.BOT_NAME, "#admin", adminMsg) log.warning(adminMsg, discord="cm") except exceptions.invalidUserException: - msg = "Hello, FokaBot here! You can't report me. I won't forget what you've tried to do. Watch out." + msg = "Hello, {} here! You can't report me. I won't forget what you've tried to do. Watch out." except exceptions.invalidArgumentsException: msg = "Invalid report command syntax. To report an user, click on it and select 'Report user'." except exceptions.userNotFoundException: @@ -779,7 +778,7 @@ def report(fro, chan, message): token = glob.tokens.getTokenFromUsername(fro) if token is not None: if token.irc: - chat.sendMessage("FokaBot", fro, msg) + chat.sendMessage(glob.BOT_NAME, fro, msg) else: token.enqueue(serverPackets.notification(msg)) return False @@ -891,10 +890,10 @@ def multiplayer(fro, chan, message): matchID = getMatchIDFromChannel(chan) success = glob.matches.matches[matchID].start() if not success: - chat.sendMessage("FokaBot", chan, "Couldn't start match. Make sure there are enough players and " + chat.sendMessage(glob.BOT_NAME, chan, "Couldn't start match. Make sure there are enough players and " "teams are valid. The match has been unlocked.") else: - chat.sendMessage("FokaBot", chan, "Have fun!") + chat.sendMessage(glob.BOT_NAME, chan, "Have fun!") def _decreaseTimer(t): @@ -902,7 +901,7 @@ def multiplayer(fro, chan, message): _start() else: if t % 10 == 0 or t <= 5: - chat.sendMessage("FokaBot", chan, "Match starts in {} seconds.".format(t)) + chat.sendMessage(glob.BOT_NAME, chan, "Match starts in {} seconds.".format(t)) threading.Timer(1.00, _decreaseTimer, [t - 1]).start() if len(message) < 2 or not message[1].isdigit(): @@ -949,8 +948,8 @@ def multiplayer(fro, chan, message): raise exceptions.invalidUserException("That user is not connected to bancho right now.") _match = glob.matches.matches[getMatchIDFromChannel(chan)] _match.invite(999, userID) - token.enqueue(serverPackets.notification("Please accept the invite you've just received from FokaBot to " - "enter your tourney match.")) + token.enqueue(serverPackets.notification("Please accept the invite you've just received from {} to " + "enter your tourney match.".format(glob.BOT_NAME))) return "An invite to this match has been sent to {}".format(username) def mpMap(): @@ -1243,7 +1242,7 @@ commands = [ "callback": report }, { "trigger": "!help", - "response": "Click (here)[https://ripple.moe/index.php?p=16&id=4] for FokaBot's full command list" + "response": "Click (here)[https://bigtu.vip/index.php?p=16&id=4] for full command list" }, #{ #"trigger": "!ask", #"syntax": "<question>", @@ -1276,7 +1275,7 @@ commands = [ "privileges": privileges.ADMIN_KICK_USERS, "callback": kick }, { - "trigger": "!fokabot reconnect", + "trigger": "!bot reconnect", "privileges": privileges.ADMIN_MANAGE_SERVERS, "callback": fokabotReconnect }, { diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 299d0b1..c386274 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -94,8 +94,12 @@ def userPanel(userID, force = False): # Get username color according to rank # Only admins and normal users are currently supported userRank = 0 - if username == "FokaBot": + if username == glob.BOT_NAME: userRank |= userRanks.MOD + elif username == "Aoba": + userRank |= userRanks.PEPPY + elif username == "Natsue": + userRank |= userRanks.PEPPY elif userUtils.isInPrivilegeGroup(userID, "developer"): userRank |= userRanks.ADMIN elif userUtils.isInPrivilegeGroup(userID, "chat mod"): diff --git a/events/changeMatchSettingsEvent.py b/events/changeMatchSettingsEvent.py index ac8d241..047b0ad 100644 --- a/events/changeMatchSettingsEvent.py +++ b/events/changeMatchSettingsEvent.py @@ -28,8 +28,8 @@ def handle(userToken, packetData): # Some dank memes easter egg memeTitles = [ - "RWC 2020", - "Fokabot is a duck", + "OWC 2020", + "AC is a duck", "Dank memes", "1337ms Ping", "Iscriviti a Xenotoze", @@ -38,7 +38,7 @@ def handle(userToken, packetData): "The brace is on fire", "print_foot()", "#FREEZEBARKEZ", - "Ripple devs are actually cats", + "osu!thailand devs are actually cats", "Thank Mr Shaural", "NEVER GIVE UP", "T I E D W I T H U N I T E D", @@ -49,7 +49,7 @@ def handle(userToken, packetData): "TATOE", "This is not your drama landfill.", "I like cheese", - "NYO IS NOT A CAT HE IS A DO(N)G", + "AOBA IS NOT A CAT HE IS A DO(N)G", "Datingu startuato" ] diff --git a/events/createMatchEvent.py b/events/createMatchEvent.py index 22d5aa9..66911aa 100644 --- a/events/createMatchEvent.py +++ b/events/createMatchEvent.py @@ -20,7 +20,7 @@ def handle(userToken, packetData): # Create a match object # 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 if matchID not in glob.matches.matches: raise exceptions.matchCreateError() @@ -29,6 +29,12 @@ def handle(userToken, packetData): # Join that match userToken.joinMatch(matchID) + # Multiplayer Room Patch + 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() diff --git a/events/setAwayMessageEvent.py b/events/setAwayMessageEvent.py index f69a6e6..6b43e93 100644 --- a/events/setAwayMessageEvent.py +++ b/events/setAwayMessageEvent.py @@ -1,7 +1,7 @@ from common.log import logUtils as log from constants import clientPackets from constants import serverPackets - +from objects import glob def handle(userToken, packetData): # get token data @@ -13,10 +13,10 @@ def handle(userToken, packetData): # Set token away message userToken.awayMessage = packetData["awayMessage"] - # Send private message from fokabot + # Send private message from the bot if packetData["awayMessage"] == "": fokaMessage = "Your away message has been reset" else: fokaMessage = "Your away message is now: {}".format(packetData["awayMessage"]) - userToken.enqueue(serverPackets.sendMessage("FokaBot", username, fokaMessage)) + userToken.enqueue(serverPackets.sendMessage(glob.BOT_NAME, username, fokaMessage)) log.info("{} has changed their away message to: {}".format(username, packetData["awayMessage"])) diff --git a/handlers/apiFokabotMessageHandler.py b/handlers/apiFokabotMessageHandler.py index 85e2729..eed14ef 100644 --- a/handlers/apiFokabotMessageHandler.py +++ b/handlers/apiFokabotMessageHandler.py @@ -28,7 +28,7 @@ class handler(requestsManager.asyncRequestHandler): raise exceptions.invalidArgumentsException() chatHelper.sendMessage( - "FokaBot", + glob.BOT_NAME, self.get_argument("to").encode().decode("ASCII", "ignore"), self.get_argument("msg").encode().decode("ASCII", "ignore") ) diff --git a/handlers/chatHelper.py b/handlers/chatHelper.py new file mode 100644 index 0000000..347755c --- /dev/null +++ b/handlers/chatHelper.py @@ -0,0 +1,433 @@ +from common.log import logUtils as log +from common.ripple import userUtils +from constants import exceptions +from constants import messageTemplates +from constants import serverPackets +from events import logoutEvent +from objects import fokabot +from objects import glob + + +def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=False): + """ + Join a channel + + :param userID: user ID of the user that joins the channel. Optional. token can be used instead. + :param token: user token object of user that joins the channel. Optional. userID can be used instead. + :param channel: channel name + :param 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: + # Get token if not defined + if token is None: + token = glob.tokens.getTokenFromUserID(userID) + # Make sure the token exists + if token is None: + raise exceptions.userNotFoundException + else: + token = token + + # Normal channel, do check stuff + # Make sure the channel exists + 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) + + # Send channel joined (IRC) + if glob.irc and not toIRC: + glob.ircServer.banchoJoinChannel(token.username, channel) + + # Console output + log.info("{} joined channel {}".format(token.username, channel)) + + # IRC code return + return 0 + except exceptions.channelNoPermissionsException: + log.warning("{} attempted to join channel {}, but they have no read permissions".format(token.username, channel)) + return 403 + except exceptions.channelUnknownException: + log.warning("{} attempted to join an unknown channel ({})".format(token.username, channel)) + return 403 + except exceptions.userAlreadyInChannelException: + log.warning("User {} already in channel {}".format(token.username, channel)) + return 403 + except exceptions.userNotFoundException: + log.warning("User not connected to IRC/Bancho") + return 403 # idk + +def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False, force=False): + """ + Part a channel + + :param userID: user ID of the user that parts the channel. Optional. token can be used instead. + :param token: user token object of user that parts 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. 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: + # Make sure the client is not drunk and sends partChannel when closing a PM tab + if not channel.startswith("#"): + return + + # Get token if not defined + if token is None: + token = glob.tokens.getTokenFromUserID(userID) + # Make sure the token exists + if token is None: + raise exceptions.userNotFoundException() + else: + token = token + + # Determine internal/client name if needed + # (toclient is used clientwise for #multiplayer and #spectator channels) + channelClient = channel + if channel == "#spectator": + if token.spectating is None: + s = userID + else: + s = token.spectatingUserID + channel = "#spect_{}".format(s) + elif channel == "#multiplayer": + channel = "#multi_{}".format(token.matchID) + elif channel.startswith("#spect_"): + channelClient = "#spectator" + elif channel.startswith("#multi_"): + channelClient = "#multiplayer" + + # Make sure the channel exists + 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) + 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: + glob.channels.removeChannel(channelObject.name) + + # Force close tab if needed + # NOTE: Maybe always needed, will check later + if kick: + token.enqueue(serverPackets.channelKicked(channelClient)) + + # IRC part + if glob.irc and toIRC: + glob.ircServer.banchoPartChannel(token.username, channel) + + # Console output + log.info("{} parted channel {} ({})".format(token.username, channel, channelClient)) + + # Return IRC code + return 0 + except exceptions.channelUnknownException: + log.warning("{} attempted to part an unknown channel ({})".format(token.username, channel)) + return 403 + except exceptions.userNotInChannelException: + log.warning("{} attempted to part {}, but he's not in that channel".format(token.username, channel)) + return 442 + except exceptions.userNotFoundException: + log.warning("User not connected to IRC/Bancho") + return 442 # idk + +def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True): + """ + Send a message to osu!bancho and IRC server + + :param fro: sender username. Optional. token can be used instead + :param to: receiver channel (if starts with #) or username + :param message: text of the message + :param token: sender token object. Optional. fro can be used instead + :param toIRC: if True, send the message to IRC. If False, send it to Bancho only. Default: True + :return: 0 if joined or other IRC code in case of error. Needed only on IRC-side + """ + try: + #tokenString = "" + # Get token object if not passed + if token is None: + token = glob.tokens.getTokenFromUsername(fro) + if token is None: + raise exceptions.userNotFoundException() + else: + # token object alredy passed, get its string and its username (fro) + fro = token.username + #tokenString = token.token + + # Make sure this is not a tournament client + # if token.tournament: + # raise exceptions.userTournamentException() + + # Make sure the user is not in restricted mode + if token.restricted: + raise exceptions.userRestrictedException() + + # Make sure the user is not silenced + if token.isSilenced(): + raise exceptions.userSilencedException() + + # Redirect !report to the bot + if message.startswith("!report"): + to = glob.BOT_NAME + + # Determine internal name if needed + # (toclient is used clientwise for #multiplayer and #spectator channels) + toClient = to + if to == "#spectator": + if token.spectating is None: + s = token.userID + else: + s = token.spectatingUserID + to = "#spect_{}".format(s) + elif to == "#multiplayer": + to = "#multi_{}".format(token.matchID) + elif to.startswith("#spect_"): + toClient = "#spectator" + elif to.startswith("#multi_"): + toClient = "#multiplayer" + + # Make sure the message is valid + if not message.strip(): + raise exceptions.invalidArgumentsException() + + # Truncate message if > 2048 characters + message = message[:2048]+"..." if len(message) > 2048 else message + + # Check for word filters + message = glob.chatFilters.filterMessage(message) + + # Build packet bytes + packet = serverPackets.sendMessage(token.username, toClient, message) + + # Send the message + isChannel = to.startswith("#") + if isChannel: + # CHANNEL + # Make sure the channel exists + if to not in glob.channels.channels: + raise exceptions.channelUnknownException() + + # Make sure the channel is not in moderated mode + if glob.channels.channels[to].moderated and not token.admin: + 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: + raise exceptions.channelNoPermissionsException() + + # Add message in buffer + token.addMessageInBuffer(to, message) + + # Everything seems fine, build recipients list and send packet + glob.streams.broadcast("chat/{}".format(to), packet, but=[token.token]) + else: + # USER + # Make sure recipient user is connected + recipientToken = glob.tokens.getTokenFromUsername(to) + if recipientToken is None: + raise exceptions.userNotFoundException() + + # Make sure the recipient is not a tournament client + #if recipientToken.tournament: + # raise exceptions.userTournamentException() + + # Make sure the recipient is not restricted or we are your bot + if recipientToken.restricted 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 + + # Away check + if recipientToken.awayCheck(token.userID): + sendMessage(to, fro, "\x01ACTION is away: {}\x01".format(recipientToken.awayMessage)) + + # Check message templates (mods/admins only) + if message in messageTemplates.templates and token.admin: + sendMessage(fro, to, messageTemplates.templates[message]) + + # Everything seems fine, send packet + recipientToken.enqueue(packet) + + # Send the message to IRC + if glob.irc and toIRC: + messageSplitInLines = message.encode("latin-1").decode("utf-8").split("\n") + for line in messageSplitInLines: + if line == messageSplitInLines[:1] and line == "": + continue + glob.ircServer.banchoMessage(fro, to, line) + + # Spam protection (ignore your bot) + if token.userID > 999: + token.spamProtection() + + # bot message + if isChannel 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) + + # File and discord logs (public chat only) + if to.startswith("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")): + log.chat("{fro} @ {to}: {message}".format(fro=token.username, to=to, message=message.encode("latin-1").decode("utf-8"))) + glob.schiavo.sendChatlog("**{fro} @ {to}:** {message}".format(fro=token.username, to=to, message=message.encode("latin-1").decode("utf-8"))) + return 0 + except exceptions.userSilencedException: + token.enqueue(serverPackets.silenceEndTime(token.getSilenceSecondsLeft())) + log.warning("{} tried to send a message during silence".format(token.username)) + return 404 + except exceptions.channelModeratedException: + log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(token.username, to)) + return 404 + except exceptions.channelUnknownException: + log.warning("{} tried to send a message to an unknown channel ({})".format(token.username, to)) + return 403 + except exceptions.channelNoPermissionsException: + log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(token.username, to)) + return 404 + except exceptions.userRestrictedException: + log.warning("{} tried to send a message {}, but the recipient is in restricted mode".format(token.username, to)) + return 404 + except exceptions.userTournamentException: + log.warning("{} tried to send a message {}, but the recipient is a tournament client".format(token.username, to)) + return 404 + except exceptions.userNotFoundException: + log.warning("User not connected to IRC/Bancho") + return 401 + except exceptions.invalidArgumentsException: + log.warning("{} tried to send an invalid message to {}".format(token.username, to)) + return 404 + + +""" IRC-Bancho Connect/Disconnect/Join/Part interfaces""" +def fixUsernameForBancho(username): + """ + Convert username from IRC format (without spaces) to Bancho format (with spaces) + + :param username: username to convert + :return: converted username + """ + # If there are no spaces or underscores in the name + # return it + if " " not in username and "_" not in username: + return username + + # Exact match first + result = glob.db.fetch("SELECT id FROM users WHERE username = %s LIMIT 1", [username]) + if result is not None: + return username + + # Username not found, replace _ with space + return username.replace("_", " ") + +def fixUsernameForIRC(username): + """ + Convert an username from Bancho format to IRC format (underscores instead of spaces) + + :param username: username to convert + :return: converted username + """ + return username.replace(" ", "_") + +def IRCConnect(username): + """ + Handle IRC login bancho-side. + Add token and broadcast login packet. + + :param username: username + :return: + """ + userID = userUtils.getID(username) + if not userID: + log.warning("{} doesn't exist".format(username)) + return + glob.tokens.deleteOldTokens(userID) + glob.tokens.addToken(userID, irc=True) + glob.streams.broadcast("main", serverPackets.userPanel(userID)) + log.info("{} logged in from IRC".format(username)) + +def IRCDisconnect(username): + """ + Handle IRC logout bancho-side. + Remove token and broadcast logout packet. + + :param username: username + :return: + """ + token = glob.tokens.getTokenFromUsername(username) + if token is None: + log.warning("{} doesn't exist".format(username)) + return + logoutEvent.handle(token) + log.info("{} disconnected from IRC".format(username)) + +def IRCJoinChannel(username, channel): + """ + Handle IRC channel join bancho-side. + + :param username: username + :param channel: channel name + :return: IRC return code + """ + userID = userUtils.getID(username) + if not userID: + log.warning("{} doesn't exist".format(username)) + return + # NOTE: This should have also `toIRC` = False` tho, + # since we send JOIN message later on ircserver.py. + # Will test this later + return joinChannel(userID, channel) + +def IRCPartChannel(username, channel): + """ + Handle IRC channel part bancho-side. + + :param username: username + :param channel: channel name + :return: IRC return code + """ + userID = userUtils.getID(username) + if not userID: + log.warning("{} doesn't exist".format(username)) + return + return partChannel(userID, channel) + +def IRCAway(username, message): + """ + Handle IRC away command bancho-side. + + :param username: + :param message: away message + :return: IRC return code + """ + userID = userUtils.getID(username) + if not userID: + log.warning("{} doesn't exist".format(username)) + return + glob.tokens.getTokenFromUserID(userID).awayMessage = message + return 305 if message == "" else 306 \ No newline at end of file diff --git a/helpers/chatHelper.py b/helpers/chatHelper.py index 229eb33..c50ad0e 100644 --- a/helpers/chatHelper.py +++ b/helpers/chatHelper.py @@ -186,9 +186,9 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True): if token.isSilenced(): raise exceptions.userSilencedException() - # Redirect !report to FokaBot + # Redirect !report to the bot if message.startswith("!report"): - to = "FokaBot" + to = glob.BOT_NAME # Determine internal name if needed # (toclient is used clientwise for #multiplayer and #spectator channels) @@ -257,8 +257,8 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True): #if recipientToken.tournament: # raise exceptions.userTournamentException() - # Make sure the recipient is not restricted or we are FokaBot - if recipientToken.restricted and fro.lower() != "fokabot": + # Make sure the recipient is not restricted or we are the bot + if recipientToken.restricted 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 @@ -282,15 +282,15 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True): continue glob.ircServer.banchoMessage(fro, to, line) - # Spam protection (ignore FokaBot) + # Spam protection (ignore the bot) if token.userID > 999: token.spamProtection() - # Fokabot message - if isChannel or to.lower() == "fokabot": + # Some bot message + if isChannel or to.lower() == glob.BOT_NAME.lower(): fokaMessage = fokabot.fokabotResponse(token.username, to, message) if fokaMessage: - sendMessage("FokaBot", to if isChannel else fro, fokaMessage) + sendMessage(glob.BOT_NAME, to if isChannel else fro, fokaMessage) # File and discord logs (public chat only) if to.startswith("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")): diff --git a/objects/channel.py b/objects/channel.py index 9d84142..91740cd 100644 --- a/objects/channel.py +++ b/objects/channel.py @@ -29,7 +29,7 @@ class channel: try: fokaToken.joinChannel(self) except exceptions.userAlreadyInChannelException: - logging.warning("FokaBot has already joined channel {}".format(self.name)) + logging.warning("Bot has already joined channel {}".format(self.name)) @property def isSpecial(self): diff --git a/objects/fokabot.py b/objects/fokabot.py index d45a43c..24afa43 100644 --- a/objects/fokabot.py +++ b/objects/fokabot.py @@ -17,6 +17,7 @@ def connect(): :return: """ + glob.BOT_NAME = userUtils.getUsername(999) token = glob.tokens.addToken(999) token.actionID = actions.IDLE glob.streams.broadcast("main", serverPackets.userPanel(999)) diff --git a/objects/glob.py b/objects/glob.py index c47a78e..afe5259 100644 --- a/objects/glob.py +++ b/objects/glob.py @@ -18,6 +18,8 @@ except: VERSION = "Unknown" DATADOG_PREFIX = "peppy" +BOT_NAME = "AC" +# YOU CAN CHANGE TO YOUR BOT NAME! # application = None db = None redis = None diff --git a/objects/match.py b/objects/match.py index de33379..d8a8e50 100644 --- a/objects/match.py +++ b/objects/match.py @@ -253,7 +253,7 @@ class match: else: newStatus = slotStatuses.LOCKED - # Send updated settings to kicked user, so he returns to lobby + # Send updated settings to kicked user, so THEY!!! (WTF RIPPLE??) 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)) @@ -439,24 +439,24 @@ class match: 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) + chat.sendMessage(glob.BOT_NAME, chanName, "Match history available [{} here]".format( + "https://multi.bigtu.vip/match/{}".format(self.vinseID) )) if not self.bloodcatAlert: chat.sendMessage( - "FokaBot", + glob.BOT_NAME, chanName, - "Oh by the way, in case you're playing unranked or broken maps " + "and uh... 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!" + "currently selected map from Bloodcat! or just use osu!direct !" ) self.bloodcatAlert = True # If this is a tournament match, then we send a notification in the chat # saying that the match has completed. if self.isTourney and (chanName in glob.channels.channels): - chat.sendMessage("FokaBot", chanName, "Match has just finished.") + chat.sendMessage(glob.BOT_NAME, chanName, "Match has just finished.") def resetSlots(self): for i in range(0,16): @@ -686,9 +686,9 @@ class match: if froToken is None or toToken is None: return - # FokaBot is too busy + # BOT IS BUSY!!! if to == 999: - chat.sendMessage("FokaBot", froToken.username, "I would love to join your match, but I'm busy keeping ripple up and running. Sorry. Beep Boop.") + chat.sendMessage(glob.BOT_NAME, froToken.username, "I would love to join your match, but I'm busy keeping the server up and running. Sorry. Beep Boop.") # Send message message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName) @@ -878,7 +878,7 @@ class match: if totalUsers == 0: message = "The match is now empty." - chat.sendMessage("FokaBot", chanName, message) + chat.sendMessage(glob.BOT_NAME, chanName, message) def __enter__(self): # 🌚🌚🌚🌚🌚 diff --git a/objects/osuToken.py b/objects/osuToken.py index bdfe44a..18211c6 100644 --- a/objects/osuToken.py +++ b/objects/osuToken.py @@ -110,7 +110,7 @@ class token: # Acquire the buffer lock self._bufferLock.acquire() - # Never enqueue for IRC clients or Foka + # Never enqueue for IRC clients or Bot if self.irc or self.userID < 999: return @@ -386,7 +386,7 @@ class token: :param seconds: silence length in seconds. If None, get it from db. Default: None :param reason: silence reason. Default: empty string - :param author: userID of who has silenced the user. Default: 999 (FokaBot) + :param author: userID of who has silenced the user. Default: 999 (Your Bot Name lol) :return: """ if seconds is None: @@ -487,7 +487,7 @@ class token: :return: """ self.restricted = True - chat.sendMessage("FokaBot", self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.") + chat.sendMessage(glob.BOT_NAME, self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.") def resetRestricted(self): """ @@ -496,7 +496,7 @@ class token: :return: """ - chat.sendMessage("FokaBot", self.username, "Your account has been unrestricted! Please log in again.") + chat.sendMessage(glob.BOT_NAME, self.username, "Your account has been unrestricted! Please log in again.") def joinStream(self, name): """ -- 2.20.1 From a5373bd3f498889e5eafb3f344be93458fb012a2 Mon Sep 17 00:00:00 2001 From: osu!thailand <43190206+osuthailand@users.noreply.github.com> Date: Wed, 6 Mar 2019 18:11:11 +0700 Subject: [PATCH 02/88] Edit little README.md --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2c51f67..35bffa4 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ -## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master) +## pep.py -- Origin: https://git.zxq.co/ripple/pep.py -- Mirror: https://github.com/osuripple/pep.py - -This is Ripple's bancho server. It handles: +This is Ripple's bancho server but osu!thailand forked edition. It handles: - Client login - Online users listing and statuses - Public and private chat - Spectator - Multiplayer -- Fokabot +- The Bot ## Requirements -- Python 3.5 +- Python 3.6 - Cython - C compiler - MySQLdb (`mysqlclient`) -- 2.20.1 From 7bb45966c5f476395622a9966dfb2a8d9d920ad3 Mon Sep 17 00:00:00 2001 From: osu!thailand <43190206+osuthailand@users.noreply.github.com> Date: Wed, 6 Mar 2019 18:12:09 +0700 Subject: [PATCH 03/88] Use official common (but GitHub instead) --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index d934dd7..f3fe6f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "common"] path = common - url = https://zxq.co/ripple/ripple-python-common.git + url = https://github.com/osuripple/ripple-python-common.git -- 2.20.1 From c5c6ed0727a13fb4b9186f8357492ef19001726f Mon Sep 17 00:00:00 2001 From: osu!thailand <43190206+osuthailand@users.noreply.github.com> Date: Wed, 6 Mar 2019 20:10:50 +0700 Subject: [PATCH 04/88] Ok, I guess... I'll go back to mine instead. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index f3fe6f6..a4ead52 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "common"] path = common - url = https://github.com/osuripple/ripple-python-common.git + url = https://github.com/osuthailand/common.git -- 2.20.1 From 622ba118137ee2fff974d0a78ed788bbb16a8587 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <minecraft172zzz@gmail.com> Date: Mon, 21 Oct 2019 17:39:27 +0700 Subject: [PATCH 05/88] Add !beatconnect command --- constants/fokabotCommands.py | 65 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 14a1151..0ea3862 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -30,6 +30,15 @@ def bloodcatMessage(beatmapID): beatmap["song_name"], ) +def beatconnectMessage(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://beatconnect.io/b/{} {}] from Beatconnect".format( + beatmap["beatmapset_id"], + beatmap["song_name"], + ) + """ Commands callbacks @@ -56,10 +65,9 @@ def faq(fro, chan, message): "swearing": "Please don't abuse swearing", "spam": "Please don't spam", "offend": "Please don't offend other players", - "github": "(osu!Ainu's Github page!)[https://github.com/osuripple/ripple]", - "discord": "(Join Ainu Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]", + "github": "(osu!Ainu's Github page!)[https://github.com/osuthailand]", + "discord": "(Join Ainu Discord!)[https://discord.gg/cnaDpVY]", "changelog": "Check the (changelog)[https://bigtu.vip/changelog] !", - "status": "Check the server status (here!)[https://status.bigtu.vip]", "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." @@ -1186,29 +1194,6 @@ def switchServer(fro, chan, message): # userToken.kick() return "{} has been connected to {}".format(target, newServer) -def reloadConfig(fro, chan, message): - if chan.startswith("#"): - return - try: - if not glob.conf.reload(): - return "Invalid configuration file structure. The new configuration file was not reloaded." - except Exception as e: - return "Unhandled exception while reloading the configuration file: {}".format(str(e)) - return "Configuration file reloaded successfully" - -def delta(fro, chan, message): - if chan.startswith("#"): - return - if not glob.conf.config["server"]["deltaurl"].strip(): - return "Delta is disabled." - userToken = glob.tokens.getTokenFromUserID(userUtils.getID(fro), ignoreIRC=True, _all=False) - if userToken is None: - return "You must be connected from a game client to switch to delta" - if not generalUtils.stringToBool(glob.conf.config["server"]["publicdelta"]) and not userToken.admin: - return "You can't use delta yet. Try again later." - userToken.enqueue(serverPackets.switchServer(glob.conf.config["server"]["deltaurl"])) - return "Connecting to delta..." - def rtx(fro, chan, message): target = message[0] message = " ".join(message[1:]).strip() @@ -1242,7 +1227,27 @@ def bloodcat(fro, chan, message): beatmapID = spectatorHostToken.beatmapID return bloodcatMessage(beatmapID) +def beatconnect(fro, chan, message): + try: + matchID = getMatchIDFromChannel(chan) + except exceptions.wrongChannelException: + matchID = None + try: + spectatorHostUserID = getSpectatorHostUserIDFromChannel(chan) + except exceptions.wrongChannelException: + spectatorHostUserID = None + if matchID is not None: + if matchID not in glob.matches.matches: + return "This match doesn't seem to exist... Or does it...?" + beatmapID = glob.matches.matches[matchID].beatmapID + else: + spectatorHostToken = glob.tokens.getTokenFromUserID(spectatorHostUserID, ignoreIRC=True) + if spectatorHostToken is None: + return "The spectator host is offline." + beatmapID = spectatorHostToken.beatmapID + return beatconnectMessage(beatmapID) + """ Commands list @@ -1396,12 +1401,8 @@ commands = [ "trigger": "!bloodcat", "callback": bloodcat }, { - "trigger": "!delta", - "callback": delta - }, { - "trigger": "!reloadconfig", - "privileges": privileges.ADMIN_MANAGE_SERVERS, - "callback": reloadConfig + "trigger": "!beatconnect", + "callback": beatconnect } # # "trigger": "!acc", -- 2.20.1 From f6e45f58bbf25e8e0371ae56671b70a17889135b Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <minecraft172zzz@gmail.com> Date: Tue, 22 Oct 2019 18:46:35 +0700 Subject: [PATCH 06/88] Add more ways to download the beatmap while spectating --- constants/fokabotCommands.py | 38 +++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 0ea3862..c169396 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -39,6 +39,17 @@ def beatconnectMessage(beatmapID): beatmap["song_name"], ) +def mirrorMessage(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 {} from [https://beatconnect.io/b/{} Beatconnect], [https://bloodcat.com/osu/s/{} Bloodcat] or [osu://dl/{} osu!direct].".format( + beatmap["song_name"], + beatmap["beatmapset_id"], + beatmap["beatmapset_id"], + beatmap["beatmapset_id"], + ) + """ Commands callbacks @@ -470,13 +481,13 @@ def getPPMessage(userID, just_data = False): def tillerinoNp(fro, chan, message): try: - # Bloodcat trigger for #spect_ + # Mirror list 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) + return mirrorMessage(spectatorHostToken.beatmapID) # Run the command in PM only if chan.startswith("#"): @@ -772,7 +783,7 @@ def report(fro, chan, message): chat.sendMessage(glob.BOT_NAME, "#admin", adminMsg) log.warning(adminMsg, discord="cm") except exceptions.invalidUserException: - msg = "Hello, {} here! You can't report me. I won't forget what you've tried to do. Watch out." + msg = "Hello, {} here! You can't report me. I won't forget what you've tried to do. Watch out.".format(glob.BOT_NAME) except exceptions.invalidArgumentsException: msg = "Invalid report command syntax. To report an user, click on it and select 'Report user'." except exceptions.userNotFoundException: @@ -1247,6 +1258,27 @@ def beatconnect(fro, chan, message): return "The spectator host is offline." beatmapID = spectatorHostToken.beatmapID return beatconnectMessage(beatmapID) + +def mirror(fro, chan, message): + try: + matchID = getMatchIDFromChannel(chan) + except exceptions.wrongChannelException: + matchID = None + try: + spectatorHostUserID = getSpectatorHostUserIDFromChannel(chan) + except exceptions.wrongChannelException: + spectatorHostUserID = None + + if matchID is not None: + if matchID not in glob.matches.matches: + return "This match doesn't seem to exist... Or does it...?" + beatmapID = glob.matches.matches[matchID].beatmapID + else: + spectatorHostToken = glob.tokens.getTokenFromUserID(spectatorHostUserID, ignoreIRC=True) + if spectatorHostToken is None: + return "The spectator host is offline." + beatmapID = spectatorHostToken.beatmapID + return mirrorMessage(beatmapID) """ Commands list -- 2.20.1 From 43755b9fbfa7228b49d5c28a58b37286edabbf70 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 12:05:15 +0700 Subject: [PATCH 07/88] Custom stuff for user ;p --- constants/serverPackets.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index c386274..7d61e37 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -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 support@ripple.moe from the email address you've used to sign up.") + packets += notification("You are banned! I don't know what did you do but you can appeal after one month since your ban by contacting Discord! (You can join by going to the website!)") 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 support@ripple.moe from the email address you've used to sign up.") + packets += notification("Well... Your account is locked but everything still in the website ya know? and uh... You can appeal us at Discord! (You can go to our website for the link!)") return packets def loginError(): @@ -85,8 +85,20 @@ def userPanel(userID, force = False): # Get user data username = userToken.username - timezone = 24+userToken.timeOffset - country = userToken.country + # Custom Timezone + if userID in (1000, 1055, 1114): + timezone = 24+9 + else: + timezone = 24+userToken.timeOffset + # Custom Countries for Users + # 111 = Japan + # 36 = Belarus + if userID in (1000, 1055, 1114): + country = 111 + elif userID == 1209: + country = 36 + else: + country = userToken.country gameRank = userToken.gameRank latitude = userToken.getLatitude() longitude = userToken.getLongitude() @@ -96,10 +108,14 @@ def userPanel(userID, force = False): userRank = 0 if username == glob.BOT_NAME: userRank |= userRanks.MOD - elif username == "Aoba": + # 1000 = Aoba's User ID + elif userID == 1000: userRank |= userRanks.PEPPY - elif username == "Natsue": + # 1000 = peppy's User ID + elif userID == 1114: userRank |= userRanks.PEPPY + elif userID == 1055: + userRank |= userRanks.NORMAL elif userUtils.isInPrivilegeGroup(userID, "developer"): userRank |= userRanks.ADMIN elif userUtils.isInPrivilegeGroup(userID, "chat mod"): -- 2.20.1 From 104e87de3343d060b6af030d87d161b071110816 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 12:06:29 +0700 Subject: [PATCH 08/88] If you're restricted, then no you're not --- events/loginEvent.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/events/loginEvent.py b/events/loginEvent.py index 611ada1..99efcd7 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -130,7 +130,10 @@ def handle(tornadoRequest): # Get supporter/GMT userGMT = False - userSupporter = True + if not userUtils.isRestricted(userID): + userSupporter = True + else: + userSupporter = False userTournament = False if responseToken.admin: userGMT = True -- 2.20.1 From e62d5c33c1a68bf6e10ecc1d0dce1daabdd70249 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 12:07:09 +0700 Subject: [PATCH 09/88] AC is from Japan :+1: --- objects/fokabot.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/objects/fokabot.py b/objects/fokabot.py index 24afa43..42c52db 100644 --- a/objects/fokabot.py +++ b/objects/fokabot.py @@ -11,7 +11,7 @@ from objects import glob # Tillerino np regex, compiled only once to increase performance npRegex = re.compile("^https?:\\/\\/osu\\.ppy\\.sh\\/b\\/(\\d*)") -def connect(): +def connect(timeOffset = 9): """ Connect FokaBot to Bancho @@ -20,6 +20,14 @@ def connect(): glob.BOT_NAME = userUtils.getUsername(999) token = glob.tokens.addToken(999) token.actionID = actions.IDLE + token.actionText = "\n-- Welcome to Ainu --" + token.pp = 727 + token.accuracy = 0.9885 + token.playcount = 26956 + token.totalScore = 237228316533 + token.timeOffset = timeOffset + token.timezone = 24+token.timeOffset + token.country = 111 glob.streams.broadcast("main", serverPackets.userPanel(999)) glob.streams.broadcast("main", serverPackets.userStats(999)) -- 2.20.1 From bd04506e6412c6c2ec77a8a11b792788bcdfab37 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 12:07:40 +0700 Subject: [PATCH 10/88] Add !beatconnect when playing #multi --- objects/match.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/objects/match.py b/objects/match.py index d8a8e50..c0bed58 100644 --- a/objects/match.py +++ b/objects/match.py @@ -449,7 +449,8 @@ class match: "and uh... 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! or just use osu!direct !" + "currently selected map from Bloodcat! If osu!direct is not working, " + "You can still use '!beatconnect' as a mirror too! " ) self.bloodcatAlert = True -- 2.20.1 From 656a99319dec60589ddccb0246f7c968435f1949 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 12:08:25 +0700 Subject: [PATCH 11/88] Man, this stuff looks nice. --- objects/osuToken.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/objects/osuToken.py b/objects/osuToken.py index 18211c6..dc2a5dd 100644 --- a/objects/osuToken.py +++ b/objects/osuToken.py @@ -66,8 +66,14 @@ class token: self.spamRate = 0 # Stats cache - self.actionID = actions.IDLE - self.actionText = "" + if userID == 1000: + self.actionID = actions.WATCHING + else: + self.actionID = actions.IDLE + if userID == 1000: + self.actionText = "HentaiHaven" + else: + self.actionText = "" self.actionMd5 = "" self.actionMods = 0 self.gameMode = gameModes.STD -- 2.20.1 From b82fc9c2829b8c69f843625878cb814029b8a7ce Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 12:09:07 +0700 Subject: [PATCH 12/88] AC is not a player, so... no. --- handlers/apiOnlineUsersHandler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py index 4960c8b..5d6b380 100644 --- a/handlers/apiOnlineUsersHandler.py +++ b/handlers/apiOnlineUsersHandler.py @@ -14,10 +14,11 @@ class handler(requestsManager.asyncRequestHandler): @sentry.captureTornado def asyncGet(self): statusCode = 400 + ass = 1 data = {"message": "unknown error"} try: # Get online users count - data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8")) + data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8")) - int(ass) # Status code and message statusCode = 200 -- 2.20.1 From 6709c3bc7fa89edfca3cbda7782fe2fb5f570748 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 24 Oct 2019 13:10:38 +0700 Subject: [PATCH 13/88] big MEME --- handlers/mainHandler.pyx | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx index 1341581..ade78d1 100644 --- a/handlers/mainHandler.pyx +++ b/handlers/mainHandler.pyx @@ -197,7 +197,7 @@ class handler(requestsManager.asyncRequestHandler): except exceptions.tokenNotFoundException: # Token not found. Disconnect that user responseData = serverPackets.loginError() - responseData += serverPackets.notification("Whoops! Something went wrong, please login again.") + responseData += serverPackets.notification("Oh no! Ainu have something wrong at the moment... Maybe try login again?") log.warning("Received packet from unknown token ({}).".format(requestTokenString)) log.info("{} has been disconnected (invalid token)".format(requestTokenString)) finally: @@ -243,24 +243,9 @@ 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%}</style></head><body><pre>" - html += " _ __<br>" - html += " (_) / /<br>" - html += " ______ __ ____ ____ / /____<br>" - html += " / ___/ / _ \\/ _ \\/ / _ \\<br>" - html += " / / / / /_) / /_) / / ____/<br>" - html += "/__/ /__/ .___/ .___/__/ \\_____/<br>" - html += " / / / /<br>" - html += " /__/ /__/<br>" - html += "<b>PYTHON > ALL VERSION</b><br><br>" - html += "<marquee style='white-space:pre;'><br>" - html += " .. o .<br>" - html += " o.o o . o<br>" - html += " oo...<br>" - html += " __[]__<br>" - html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>" - 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><i>&copy; Ripple team, 2016</i></pre></body></html>" - self.write(html) \ No newline at end of file + html = "<html><head><title>Aoba's a cutie?</title><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fontello.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fonts.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/inter-ui.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/master.less' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2-bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-ansi.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-pygments.css' type='text/css'></head><body><div class='code' id='code'>" + html += "<script>NekoType='spooky'</script>" + html += "<h1 id=nl><script src='https://webneko.net/n20171213.js'></script></h1>" + html += "<iframe src='https://ghostbin.co/paste/8j2ft' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>" + #Yes. I just wrote the credit... in it. + self.write(html) -- 2.20.1 From ae973dc20801e64157d7f9283310cfd931217dd3 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <minecraft172zzz@gmail.com> Date: Thu, 24 Oct 2019 13:21:00 +0700 Subject: [PATCH 14/88] Oh well... I still using this... --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4ed9153..7e72ba3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -# ⚠️ As of 26th June 2019, this repository has been deprecated and is not used in Ripple's stack anymore. It's left here just for historical reasons. ## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master) This is Ripple's bancho server but osu!thailand forked edition. It handles: -- 2.20.1 From 4eb990b202e8fea7a4dec07b977b2716cabf860e Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <minecraft172zzz@gmail.com> Date: Fri, 25 Oct 2019 00:15:57 +0700 Subject: [PATCH 15/88] He wants Mod color :p --- constants/serverPackets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 7d61e37..91649f4 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -115,7 +115,7 @@ def userPanel(userID, force = False): elif userID == 1114: userRank |= userRanks.PEPPY elif userID == 1055: - userRank |= userRanks.NORMAL + userRank |= userRanks.MOD elif userUtils.isInPrivilegeGroup(userID, "developer"): userRank |= userRanks.ADMIN elif userUtils.isInPrivilegeGroup(userID, "chat mod"): @@ -298,4 +298,4 @@ def banchoRestart(msUntilReconnection): return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.UINT32]]) def rtx(message): - return packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]]) \ No newline at end of file + return packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]]) -- 2.20.1 From 8fdb7ed3071ff338441ce1ab0050fa5e2bf8490f Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 25 Oct 2019 19:14:45 +0700 Subject: [PATCH 16/88] You can't ban or restrict AC! Remember it Simon. --- constants/fokabotCommands.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index c169396..dd19268 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -261,7 +261,8 @@ def ban(fro, chan, message): userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) - + if targetUserID in (999, 1000): + return "NO!" # Set allowed to 0 userUtils.ban(targetUserID) @@ -302,7 +303,9 @@ def restrict(fro, chan, message): userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) - + if targetUserID in (999, 1000): + return "NO!" + # Put this user in restricted mode userUtils.restrict(targetUserID) -- 2.20.1 From 9df9ffef460bb91219be9105d17d5d8daf6f1ef6 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 25 Oct 2019 19:20:25 +0700 Subject: [PATCH 17/88] Alert you when you're playing Relax or disabled it --- events/changeActionEvent.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py index 5e541fd..dc9b9b9 100644 --- a/events/changeActionEvent.py +++ b/events/changeActionEvent.py @@ -2,6 +2,7 @@ from common.log import logUtils as log from constants import clientPackets from constants import serverPackets from objects import glob +from common.constants import mods def handle(userToken, packetData): # Get usertoken data @@ -40,11 +41,30 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us # Always update action id, text, md5 and beatmapID userToken.actionID = packetData["actionID"] - userToken.actionText = packetData["actionText"] + #userToken.actionID = packetData["actionText"] userToken.actionMd5 = packetData["actionMd5"] userToken.actionMods = packetData["actionMods"] userToken.beatmapID = packetData["beatmapID"] + + if bool(packetData["actionMods"] & 128) == True: + userToken.relaxing = True + UserText = packetData["actionText"] + " on Relax" + userToken.actionText = UserText + userToken.updateCachedStats() + if userToken.relaxAnnounce == False: + userToken.relaxAnnounce = True + userToken.enqueue(serverPackets.notification("You're playing with Relax, we've changed the leaderboard to Relax.")) + else: + UserText = packetData["actionText"] + userToken.actionText = UserText + userToken.relaxing = False + userToken.autobotting = False + userToken.updateCachedStats() + if userToken.relaxAnnounce == True: + userToken.relaxAnnounce = False + userToken.enqueue(serverPackets.notification("You've disabled relax. We've changed back to the Regular leaderboard.")) + glob.db.execute("UPDATE users_stats SET current_status = %s WHERE id = %s", [UserText, userID]) # Enqueue our new user panel and stats to us and our spectators recipients = [userToken] if len(userToken.spectators) > 0: -- 2.20.1 From 5ed0649f385df74c3362ba9e184056d2cf80ea41 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 25 Oct 2019 19:21:49 +0700 Subject: [PATCH 18/88] Where is this came from?! --- events/changeActionEvent.py | 1 - 1 file changed, 1 deletion(-) diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py index dc9b9b9..5c2f79e 100644 --- a/events/changeActionEvent.py +++ b/events/changeActionEvent.py @@ -59,7 +59,6 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us UserText = packetData["actionText"] userToken.actionText = UserText userToken.relaxing = False - userToken.autobotting = False userToken.updateCachedStats() if userToken.relaxAnnounce == True: userToken.relaxAnnounce = False -- 2.20.1 From e0b3828e55f65584a29dc22358d81d246e8d08d4 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 25 Oct 2019 19:27:01 +0700 Subject: [PATCH 19/88] They're offline, and then set it to offline. --- events/logoutEvent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/events/logoutEvent.py b/events/logoutEvent.py index 3a8d55e..f9b5a39 100644 --- a/events/logoutEvent.py +++ b/events/logoutEvent.py @@ -44,6 +44,7 @@ def handle(userToken, _=None, deleteToken=True): else: userToken.kicked = True + glob.db.execute("UPDATE users_stats SET current_status = 'Offline' WHERE id = %s", [userID]) # Change username if needed newUsername = glob.redis.get("ripple:change_username_pending:{}".format(userID)) if newUsername is not None: -- 2.20.1 From 4c7d72c476678305c663291b6ddbac746ec44e49 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 25 Oct 2019 19:36:57 +0700 Subject: [PATCH 20/88] Add Relax stats --- objects/osuToken.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/objects/osuToken.py b/objects/osuToken.py index dc2a5dd..771992e 100644 --- a/objects/osuToken.py +++ b/objects/osuToken.py @@ -84,6 +84,10 @@ class token: self.totalScore = 0 self.gameRank = 0 self.pp = 0 + + # Relax + self.relaxing = False + self.relaxAnnounce = False # Generate/set token if token_ is not None: @@ -450,16 +454,26 @@ class token: :return: """ stats = userUtils.getUserStats(self.userID, self.gameMode) + stats_relax = userUtils.getUserStatsRx(self.userID, self.gameMode) log.debug(str(stats)) + if stats is None: log.warning("Stats query returned None") return - self.rankedScore = stats["rankedScore"] - self.accuracy = stats["accuracy"]/100 - self.playcount = stats["playcount"] - self.totalScore = stats["totalScore"] - self.gameRank = stats["gameRank"] - self.pp = stats["pp"] + if self.relaxing == True: + self.gameRank = stats_relax["gameRank"] + self.pp = stats_relax["pp"] + self.rankedScore = stats_relax["rankedScore"] + self.accuracy = stats_relax["accuracy"]/100 + self.playcount = stats_relax["playcount"] + self.totalScore = stats_relax["totalScore"] + else: + self.rankedScore = stats["rankedScore"] + self.accuracy = stats["accuracy"]/100 + self.playcount = stats["playcount"] + self.totalScore = stats["totalScore"] + self.gameRank = stats["gameRank"] + self.pp = stats["pp"] def checkRestricted(self): """ @@ -493,7 +507,7 @@ class token: :return: """ self.restricted = True - chat.sendMessage(glob.BOT_NAME, self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.") + chat.sendMessage(glob.BOT_NAME, self.username, "Your account is currently in restricted mode. Please visit Ainu's website for more information.") def resetRestricted(self): """ -- 2.20.1 From deb3362a62ee5c9d6a0b7e743bfeb8aa51b1de6b Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 25 Oct 2019 19:37:16 +0700 Subject: [PATCH 21/88] No one is on the boat :( --- helpers/consoleHelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/consoleHelper.py b/helpers/consoleHelper.py index 6f9a17f..352c45a 100644 --- a/helpers/consoleHelper.py +++ b/helpers/consoleHelper.py @@ -21,13 +21,13 @@ def printServerStartHeader(asciiArt=True): print(" o.o o . o") print(" oo...") print(" __[]__") - print(" nyo --> _\\:D/_/o_o_o_|__ u wot m8") + print(" ______/o_o_o_|__ everybody is gone :(") 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("> Made by the Ripple team, custom fork by osu!thailand", bcolors.GREEN) printColored("> {}https://zxq.co/ripple/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN) printColored("> Press CTRL+C to exit\n", bcolors.GREEN) -- 2.20.1 From e7cf92e6e60d6ff0429bb9a00c79c1197692a082 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <minecraft172zzz@gmail.com> Date: Sat, 26 Oct 2019 11:26:45 +0700 Subject: [PATCH 22/88] Fix space stuff in online panel --- events/changeActionEvent.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py index 5c2f79e..6ca1d94 100644 --- a/events/changeActionEvent.py +++ b/events/changeActionEvent.py @@ -49,7 +49,10 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us if bool(packetData["actionMods"] & 128) == True: userToken.relaxing = True - UserText = packetData["actionText"] + " on Relax" + if userToken.actionID in (0, 1, 14): + UserText = packetData["actionText"] + "on Relax" + else: + UserText = packetData["actionText"] + " on Relax" userToken.actionText = UserText userToken.updateCachedStats() if userToken.relaxAnnounce == False: -- 2.20.1 From 654daee37e899fac549c5c55d181c5c867307d71 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <minecraft172zzz@gmail.com> Date: Fri, 1 Nov 2019 19:01:21 +0700 Subject: [PATCH 23/88] R.I.P pep.py I will miss you :') --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7e72ba3..d7b9adc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master) - +# ⚠️ As of 1st November 2019, this repository has been deprecated and is not used in Ainu's stack anymore. It's left here just for historical reasons. +## pep.py This is Ripple's bancho server but osu!thailand forked edition. It handles: - Client login - Online users listing and statuses -- 2.20.1 From 7d62cc18394360b8cc592a0892426c73ede3c331 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 2 Nov 2019 23:17:47 +0700 Subject: [PATCH 24/88] pep.py's last message before death --- common | 2 +- objects/osuToken.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common b/common index 1b6fbdd..4a270cd 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1b6fbdd0d5e3c128a0850145dbe2c84a40a478a0 +Subproject commit 4a270cdc8d18bc929a6da54d0c8ea88464792f91 diff --git a/objects/osuToken.py b/objects/osuToken.py index 771992e..504314e 100644 --- a/objects/osuToken.py +++ b/objects/osuToken.py @@ -460,7 +460,7 @@ class token: if stats is None: log.warning("Stats query returned None") return - if self.relaxing == True: + if self.relaxing: self.gameRank = stats_relax["gameRank"] self.pp = stats_relax["pp"] self.rankedScore = stats_relax["rankedScore"] @@ -468,12 +468,12 @@ class token: self.playcount = stats_relax["playcount"] self.totalScore = stats_relax["totalScore"] else: + self.gameRank = stats["gameRank"] + self.pp = stats["pp"] self.rankedScore = stats["rankedScore"] self.accuracy = stats["accuracy"]/100 self.playcount = stats["playcount"] self.totalScore = stats["totalScore"] - self.gameRank = stats["gameRank"] - self.pp = stats["pp"] def checkRestricted(self): """ -- 2.20.1 From 92abeab94ae0c2e100ca6097f0bf6df712e86faf Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Thu, 14 Nov 2019 22:14:29 +0700 Subject: [PATCH 25/88] Hello again, Ripple. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d7b9adc..9a03d58 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -# ⚠️ As of 1st November 2019, this repository has been deprecated and is not used in Ainu's stack anymore. It's left here just for historical reasons. ## pep.py This is Ripple's bancho server but osu!thailand forked edition. It handles: - Client login -- 2.20.1 From c25287ccae24906623bba4241d6f6f1e9e1568e8 Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Thu, 14 Nov 2019 23:42:23 +0700 Subject: [PATCH 26/88] Kurikku is my idol --- constants/clientPackets.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/constants/clientPackets.py b/constants/clientPackets.py index c5e42b6..60059a1 100644 --- a/constants/clientPackets.py +++ b/constants/clientPackets.py @@ -89,34 +89,25 @@ def matchSettings(stream): struct.append(["slot{}Team".format(str(i)), dataTypes.BYTE]) # Read first part - data.append(packetHelper.readPacketData(stream, struct)) + slotData = 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 + s = slotData["slot{}Status".format(str(i))] + if s & (4 | 8 | 16 | 32 | 64) > 0: + struct.append(["slot{}UserId".format(str(i)), dataTypes.SINT32]) # Other settings - struct = [ + struct.extend([ ["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)) + ]) - result = {} - for i in data: - result.update(i) + # Results goes here + result = packetHelper.readPacketData(stream, struct) return result def createMatch(stream): -- 2.20.1 From 7c6b3511a284a9b0e06ab2ad7fd96567475bfdc8 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 15 Nov 2019 00:38:22 +0700 Subject: [PATCH 27/88] Updated submodules --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 4a270cd..1be36af 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 4a270cdc8d18bc929a6da54d0c8ea88464792f91 +Subproject commit 1be36afaa4ee104158b9e1f4dc32a535021e123a -- 2.20.1 From d7420cdbc083a4528cb71f7c11bb78ebfb1d0fc8 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 15 Nov 2019 20:09:49 +0700 Subject: [PATCH 28/88] Added !map command to rank maps in game cuz you don't have time to rank it in old panel :+1: --- constants/fokabotCommands.py | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index dd19268..7d48b7a 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1219,6 +1219,49 @@ def rtx(fro, chan, message): userToken = glob.tokens.getTokenFromUserID(targetUserID, ignoreIRC=True, _all=False) userToken.enqueue(serverPackets.rtx(message)) return ":ok_hand:" + +def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by cmyui and edited by Aoba + messages = [m.lower() for m in message] + rankType = message[0] + mapType = message[1] + mapID = message[2] + + # Get persons username & ID + userID = userUtils.getID(fro) + name = userUtils.getUsername(userID) + + # What do I do here? + if rankType == 'rank': + rankTypeID = 2 + freezeStatus = 1 + elif rankType == 'unrank': + rankTypeID = 0 + freezeStatus = 0 + + # Grab beatmapData from db + beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) + + if mapType == 'set': + glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format(rankTypeID, freezeStatus, beatmapData["beatmapset_id"])) + if freezeStatus == 1: + glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps + WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmapset_id"])) + elif mapType == 'map': + glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, mapID)) + if freezeStatus == 1: + glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps + WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmap_id"])) + else: + return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'" + + log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), True) + if mapType == 'set': + msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"]) + else: + msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, mapID, beatmapData["song_name"]) + + chat.sendMessage(glob.BOT_NAME, "#ranked", msg) + return msg def bloodcat(fro, chan, message): try: @@ -1311,6 +1354,12 @@ commands = [ #"syntax": "<question>", #"callback": ask #}, { + { + "trigger": "!map", + "syntax": "<rank/unrank> <set/map> <ID>", + "privileges": privileges.ADMIN_MANAGE_BEATMAPS, + "callback": editMap + }, { "trigger": "!mm00", "callback": mm00 -- 2.20.1 From ffa642b5f74aad9d6be2fd7183c95344da76cc6e Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 15 Nov 2019 20:17:27 +0700 Subject: [PATCH 29/88] *internal screaming* --- constants/fokabotCommands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 7d48b7a..a4f4da2 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1258,7 +1258,7 @@ def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by if mapType == 'set': msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"]) else: - msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, mapID, beatmapData["song_name"]) + msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, beatmapData["song_name"]) chat.sendMessage(glob.BOT_NAME, "#ranked", msg) return msg -- 2.20.1 From f9dc2a000c4cd514bc0f1cf6df576dfb2d70d4dd Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Fri, 15 Nov 2019 20:44:46 +0700 Subject: [PATCH 30/88] Added !announce command --- constants/fokabotCommands.py | 71 +++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index a4f4da2..0bacc0d 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1239,30 +1239,69 @@ def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by freezeStatus = 0 # Grab beatmapData from db - beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) + try: + beatmapData = glob.db.fetch("SELECT beatmapset_id, song_name, ranked, blacklisted FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) + except: + return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly." if mapType == 'set': - glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format(rankTypeID, freezeStatus, beatmapData["beatmapset_id"])) + glob.db.execute( + "UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format( + rankTypeID, freezeStatus, beatmapData["beatmapset_id"])) if freezeStatus == 1: - glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps - WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmapset_id"])) + glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps + WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format( + beatmapData["beatmapset_id"])) + typeBM = 'set' elif mapType == 'map': - glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, mapID)) + glob.db.execute( + "UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format( + rankTypeID, freezeStatus, mapID)) if freezeStatus == 1: - glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps - WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format(beatmapData["beatmap_id"])) + glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps + WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format( + beatmapData["beatmap_id"])) + typeBM = 'beatmap' else: return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'" - log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), True) - if mapType == 'set': - msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"]) + # Announce that YOOOOOOO THIS MAP IS RANKED!!! + if rankType == "rank": + log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), + True) + if mapType == 'set': + msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, + beatmapData["beatmapset_id"], + beatmapData["song_name"]) + else: + msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, + beatmapData["song_name"]) + glob.db.execute( + "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( + beatmapData["beatmap_id"])) else: - msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, beatmapData["song_name"]) + log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), + True) + if mapType == 'set': + msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, + beatmapData["beatmapset_id"], + beatmapData["song_name"]) + else: + msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, + beatmapData["song_name"]) + + glob.db.execute( + "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( + beatmapData["beatmap_id"])) chat.sendMessage(glob.BOT_NAME, "#ranked", msg) return msg +def postAnnouncement(fro, chan, message): # Post to #announce ingame + announcement = ' '.join(message[0:]) + chat.sendMessage(glob.BOT_NAME, "#announce", announcement) + return "Announcement successfully sent." + def bloodcat(fro, chan, message): try: matchID = getMatchIDFromChannel(chan) @@ -1349,7 +1388,12 @@ commands = [ }, { "trigger": "!help", "response": "Click (here)[https://bigtu.vip/index.php?p=16&id=4] for full command list" - }, #{ + }, { + "trigger": "!announce", + "syntax": "<announcement>", + "privileges": privileges.ADMIN_SEND_ALERTS, + "callback": postAnnouncement + }, #{ #"trigger": "!ask", #"syntax": "<question>", #"callback": ask @@ -1359,8 +1403,7 @@ commands = [ "syntax": "<rank/unrank> <set/map> <ID>", "privileges": privileges.ADMIN_MANAGE_BEATMAPS, "callback": editMap - }, - { + }, { "trigger": "!mm00", "callback": mm00 }, { -- 2.20.1 From 91a26d01eeb808bd1b95908043c1396cdcaa7d8b Mon Sep 17 00:00:00 2001 From: _Cherry <42537021+xxCherry@users.noreply.github.com> Date: Sat, 16 Nov 2019 18:37:43 +0300 Subject: [PATCH 31/88] send bancho restart packet instead login erro --- handlers/mainHandler.pyx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx index ade78d1..29eb508 100644 --- a/handlers/mainHandler.pyx +++ b/handlers/mainHandler.pyx @@ -195,11 +195,9 @@ class handler(requestsManager.asyncRequestHandler): responseData = userToken.queue userToken.resetQueue() except exceptions.tokenNotFoundException: - # Token not found. Disconnect that user - responseData = serverPackets.loginError() - responseData += serverPackets.notification("Oh no! Ainu have something wrong at the moment... Maybe try login again?") - log.warning("Received packet from unknown token ({}).".format(requestTokenString)) - log.info("{} has been disconnected (invalid token)".format(requestTokenString)) + # Token not found. Send bancho restart packet to user + responseData = serverPackets.banchoRestart(1) + finally: # Unlock token if userToken is not None: -- 2.20.1 From 6724274c3a607d6fd15760b00d04d52f9526b132 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 16 Nov 2019 23:15:28 +0700 Subject: [PATCH 32/88] Updated submodules --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 1be36af..dfe5762 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1be36afaa4ee104158b9e1f4dc32a535021e123a +Subproject commit dfe576268bc6cb8fd4c1c1aa624a0c31f4a716d8 -- 2.20.1 From c0ea3d9b5d10bbadb099edff085c912276159dfa Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 16 Nov 2019 23:49:40 +0700 Subject: [PATCH 33/88] Revert "send bancho restart packet instead login erro" This reverts commit 91a26d01eeb808bd1b95908043c1396cdcaa7d8b. --- handlers/mainHandler.pyx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx index 29eb508..ade78d1 100644 --- a/handlers/mainHandler.pyx +++ b/handlers/mainHandler.pyx @@ -195,9 +195,11 @@ class handler(requestsManager.asyncRequestHandler): responseData = userToken.queue userToken.resetQueue() except exceptions.tokenNotFoundException: - # Token not found. Send bancho restart packet to user - responseData = serverPackets.banchoRestart(1) - + # Token not found. Disconnect that user + responseData = serverPackets.loginError() + responseData += serverPackets.notification("Oh no! Ainu have something wrong at the moment... Maybe try login again?") + log.warning("Received packet from unknown token ({}).".format(requestTokenString)) + log.info("{} has been disconnected (invalid token)".format(requestTokenString)) finally: # Unlock token if userToken is not None: -- 2.20.1 From 6e8afb046edb6cfc256aaffbea7c90205c1902b4 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 16 Nov 2019 23:51:20 +0700 Subject: [PATCH 34/88] No, Cherry... Let's just keep it is. --- handlers/mainHandler.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx index ade78d1..de6cc62 100644 --- a/handlers/mainHandler.pyx +++ b/handlers/mainHandler.pyx @@ -243,9 +243,7 @@ class handler(requestsManager.asyncRequestHandler): @tornado.web.asynchronous @tornado.gen.engine def asyncGet(self): - html = "<html><head><title>Aoba's a cutie?</title><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fontello.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/fonts.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/inter-ui.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/master.less' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2-bootstrap.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/select2.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-ansi.css' type='text/css'><link rel='stylesheet' href='https://raw.githubusercontent.com/Hazuki-san/spectre/v1-stable/public/css/theme-pygments.css' type='text/css'></head><body><div class='code' id='code'>" - html += "<script>NekoType='spooky'</script>" - html += "<h1 id=nl><script src='https://webneko.net/n20171213.js'></script></h1>" + html = "<html><head><title>Aoba's a cutie?</title>" html += "<iframe src='https://ghostbin.co/paste/8j2ft' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>" #Yes. I just wrote the credit... in it. self.write(html) -- 2.20.1 From f38273ba36635995e034a6e397940249a4c1de41 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 17 Nov 2019 01:44:21 +0700 Subject: [PATCH 35/88] Updated submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 160000 => 120000 common diff --git a/common b/common deleted file mode 160000 index dfe5762..0000000 --- a/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dfe576268bc6cb8fd4c1c1aa624a0c31f4a716d8 diff --git a/common b/common new file mode 120000 index 0000000..8047583 --- /dev/null +++ b/common @@ -0,0 +1 @@ +E:/GitHub/common \ No newline at end of file -- 2.20.1 From 52e0f7dc1b6c67d8ee5715e7e7e664a7690d91af Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 17 Nov 2019 01:48:02 +0700 Subject: [PATCH 36/88] Wait... Where did the submodule go!? --- common | 1 - 1 file changed, 1 deletion(-) delete mode 120000 common diff --git a/common b/common deleted file mode 120000 index 8047583..0000000 --- a/common +++ /dev/null @@ -1 +0,0 @@ -E:/GitHub/common \ No newline at end of file -- 2.20.1 From da543f5c8332d02a5e0ff960c81e06fd22304b33 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 17 Nov 2019 01:57:04 +0700 Subject: [PATCH 37/88] Hello... Is there anybody... there --- .gitmodules | 2 +- common | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 common diff --git a/.gitmodules b/.gitmodules index a4ead52..c04cfe0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "common"] path = common - url = https://github.com/osuthailand/common.git + url = https://github.com/osuthailand/common diff --git a/common b/common new file mode 160000 index 0000000..f098b7b --- /dev/null +++ b/common @@ -0,0 +1 @@ +Subproject commit f098b7b89ced5648c3b12bf51933796dfba58243 -- 2.20.1 From 8f527abc205a80acbd974fb9c4769bff804c2eb7 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 17 Nov 2019 02:05:32 +0700 Subject: [PATCH 38/88] Updated submodule --- .gitmodules | 2 +- common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index c04cfe0..a4ead52 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "common"] path = common - url = https://github.com/osuthailand/common + url = https://github.com/osuthailand/common.git diff --git a/common b/common index f098b7b..8bacaa8 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit f098b7b89ced5648c3b12bf51933796dfba58243 +Subproject commit 8bacaa80fd95e7f06c2d20f1fb58642875dfe95d -- 2.20.1 From ec63d4d264178372cc4f62d229173ba75744e39d Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 17 Nov 2019 02:24:47 +0700 Subject: [PATCH 39/88] Updated submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 8bacaa8..6055fc1 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 8bacaa80fd95e7f06c2d20f1fb58642875dfe95d +Subproject commit 6055fc11b8ae196ee3636522dbc41be578301c54 -- 2.20.1 From 686babd223e3b88119f87027e03a2e986f655dbe Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Fri, 22 Nov 2019 01:27:55 +0700 Subject: [PATCH 40/88] Fix beatmap not found in map command --- constants/fokabotCommands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 0bacc0d..1cc0160 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1240,7 +1240,7 @@ def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by # Grab beatmapData from db try: - beatmapData = glob.db.fetch("SELECT beatmapset_id, song_name, ranked, blacklisted FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) + beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) except: return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly." -- 2.20.1 From 4ac1bf8d9380088f4fecb1f05dfc2bc0096e375d Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Thu, 5 Dec 2019 20:29:37 +0700 Subject: [PATCH 41/88] bigtu.vip > ainu.pw --- constants/fokabotCommands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 1cc0160..9f9e706 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -72,13 +72,13 @@ def instantRestart(fro, chan, message): def faq(fro, chan, message): # TODO: Unhardcode this messages = { - "rules": "Please make sure to check (osu!thailand's rules)[https://bigtu.vip/doc/rules].", + "rules": "Please make sure to check (osu!thailand's rules)[https://ainu.pw/doc/rules].", "swearing": "Please don't abuse swearing", "spam": "Please don't spam", "offend": "Please don't offend other players", "github": "(osu!Ainu's Github page!)[https://github.com/osuthailand]", - "discord": "(Join Ainu Discord!)[https://discord.gg/cnaDpVY]", - "changelog": "Check the (changelog)[https://bigtu.vip/changelog] !", + "discord": "(Join Ainu Discord!)[https://discord.gg/Qp3WQU8]", + "changelog": "Check the (changelog)[https://ainu.pw/changelog] !", "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." @@ -1387,7 +1387,7 @@ commands = [ "callback": report }, { "trigger": "!help", - "response": "Click (here)[https://bigtu.vip/index.php?p=16&id=4] for full command list" + "response": "Click (here)[https://ainu.pw/index.php?p=16&id=4] for full command list" }, { "trigger": "!announce", "syntax": "<announcement>", -- 2.20.1 From a3388efd908f4050a27841cdbdababd00177eb6f Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Fri, 6 Dec 2019 07:03:20 +0700 Subject: [PATCH 42/88] Updated submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 6055fc1..d114fc1 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 6055fc11b8ae196ee3636522dbc41be578301c54 +Subproject commit d114fc1a86dccf67a8fb918de802d7b2ced36aab -- 2.20.1 From c76be19c80614eee475d7d0f9c65fadcc44736da Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Sat, 7 Dec 2019 19:33:28 +0700 Subject: [PATCH 43/88] Updated submodule --- common | 2 +- objects/fokabot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common b/common index d114fc1..d6f0f9b 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit d114fc1a86dccf67a8fb918de802d7b2ced36aab +Subproject commit d6f0f9bd9e2b6a8fc6f75ee5621814f63e55027d diff --git a/objects/fokabot.py b/objects/fokabot.py index 42c52db..3ab179d 100644 --- a/objects/fokabot.py +++ b/objects/fokabot.py @@ -71,4 +71,4 @@ def fokabotResponse(fro, chan, message): return i["callback"](fro, chan, message[1:]) # No commands triggered - return False \ No newline at end of file + return False -- 2.20.1 From 3506289371fc5cc369e9ba15ff835a843848ab8e Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Sat, 7 Dec 2019 20:54:13 +0700 Subject: [PATCH 44/88] Update submoudle v999 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index d6f0f9b..2e011c9 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit d6f0f9bd9e2b6a8fc6f75ee5621814f63e55027d +Subproject commit 2e011c942c9ab6904dded6fecc1cc2cf3a967416 -- 2.20.1 From c74ac5e794091bafa192ca2db9ee5f4268db4f3f Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 8 Dec 2019 17:09:47 +0700 Subject: [PATCH 45/88] I know it's annoying. --- events/changeActionEvent.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py index 6ca1d94..ca3db49 100644 --- a/events/changeActionEvent.py +++ b/events/changeActionEvent.py @@ -55,17 +55,21 @@ if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and us UserText = packetData["actionText"] + " on Relax" userToken.actionText = UserText userToken.updateCachedStats() + """ if userToken.relaxAnnounce == False: userToken.relaxAnnounce = True userToken.enqueue(serverPackets.notification("You're playing with Relax, we've changed the leaderboard to Relax.")) + """ else: UserText = packetData["actionText"] userToken.actionText = UserText userToken.relaxing = False userToken.updateCachedStats() + """ if userToken.relaxAnnounce == True: userToken.relaxAnnounce = False userToken.enqueue(serverPackets.notification("You've disabled relax. We've changed back to the Regular leaderboard.")) + """ glob.db.execute("UPDATE users_stats SET current_status = %s WHERE id = %s", [UserText, userID]) # Enqueue our new user panel and stats to us and our spectators recipients = [userToken] -- 2.20.1 From d831ab7bb8af59ced039b4d5921cb3aa9c097f12 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Tue, 10 Dec 2019 19:39:41 +0700 Subject: [PATCH 46/88] Revert "AC is not a player, so... no." This reverts commit b82fc9c2829b8c69f843625878cb814029b8a7ce. --- handlers/apiOnlineUsersHandler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py index 5d6b380..4960c8b 100644 --- a/handlers/apiOnlineUsersHandler.py +++ b/handlers/apiOnlineUsersHandler.py @@ -14,11 +14,10 @@ class handler(requestsManager.asyncRequestHandler): @sentry.captureTornado def asyncGet(self): statusCode = 400 - ass = 1 data = {"message": "unknown error"} try: # Get online users count - data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8")) - int(ass) + data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8")) # Status code and message statusCode = 200 -- 2.20.1 From 914cd3e95b0b4a5d4f49e150b642466302debc11 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 14 Dec 2019 11:14:40 +0700 Subject: [PATCH 47/88] Updated submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 2e011c9..037e453 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 2e011c942c9ab6904dded6fecc1cc2cf3a967416 +Subproject commit 037e453812e69fbc2a0eb8f2172ffa7bc1e15bd2 -- 2.20.1 From 404845ee24a419c228d3bee91bd04cb0447995ac Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 14 Dec 2019 11:17:37 +0700 Subject: [PATCH 48/88] Add whitelist from autorestrictions --- constants/fokabotCommands.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 9f9e706..9fd2f5d 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1301,6 +1301,24 @@ def postAnnouncement(fro, chan, message): # Post to #announce ingame announcement = ' '.join(message[0:]) chat.sendMessage(glob.BOT_NAME, "#announce", announcement) return "Announcement successfully sent." + +def whitelistUserPPLimit(fro, chan, message): + messages = [m.lower() for m in message] + target = message[0] + relax = message[1] + + userID = userUtils.getID(target) + + if userID == 0: + return "That user does not exist." + + if 'x' in relax: + rx = True + else: + rx = False + + userUtils.whitelistUserPPLimit(userID, rx) + return "{user} has been whitelisted from autorestrictions on {rx}.".format(user=target, rx='relax' if rx else 'vanilla') def bloodcat(fro, chan, message): try: -- 2.20.1 From 43a1b082343b01d55f64885b5d16753a16b32970 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 14 Dec 2019 11:19:48 +0700 Subject: [PATCH 49/88] Aoba forgot everything v2 --- constants/fokabotCommands.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 9fd2f5d..249c38b 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1406,6 +1406,11 @@ commands = [ }, { "trigger": "!help", "response": "Click (here)[https://ainu.pw/index.php?p=16&id=4] for full command list" + }, { + "trigger": "!whitelist", + "privileges": privileges.ADMIN_BAN_USERS, + "syntax": "<target> <relax/vanilla>", + "callback": whitelistUserPPLimit }, { "trigger": "!announce", "syntax": "<announcement>", -- 2.20.1 From c5c3265cf3c298e8e2fde4606822208916138fe1 Mon Sep 17 00:00:00 2001 From: Mike Shenin <i@kotrik.ru> Date: Wed, 27 Nov 2019 22:13:03 +0300 Subject: [PATCH 50/88] feat: logging user osuVer --- events/loginEvent.py | 4 ++++ helpers/kotrikhelper.py | 4 ++++ ...elper.py~64f4109... feat: logging user osuVer | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 helpers/kotrikhelper.py create mode 100644 helpers/kotrikhelper.py~64f4109... feat: logging user osuVer diff --git a/events/loginEvent.py b/events/loginEvent.py index 99efcd7..bcf08fb 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -10,6 +10,7 @@ from constants import serverPackets from helpers import chatHelper as chat from helpers import countryHelper from helpers import locationHelper +from helpers import kotrikhelper from objects import glob @@ -100,6 +101,9 @@ def handle(tornadoRequest): # Log user IP userUtils.logIP(userID, requestIP) + # Log user osuver + kotrikhelper.setUserLastOsuVer(userID, osuVersion) + # Delete old tokens for that user and generate a new one isTournament = "tourney" in osuVersion if not isTournament: diff --git a/helpers/kotrikhelper.py b/helpers/kotrikhelper.py new file mode 100644 index 0000000..7c45ac7 --- /dev/null +++ b/helpers/kotrikhelper.py @@ -0,0 +1,4 @@ +import math + +def secondsToFormatted(length): + return f"{math.floor(length / 60)}:{str(length % 60)}" \ No newline at end of file diff --git a/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer b/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer new file mode 100644 index 0000000..2e72edc --- /dev/null +++ b/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer @@ -0,0 +1,16 @@ +import math +from objects import glob + +def secondsToFormatted(length): + return f"{math.floor(length / 60)}:{str(length % 60)}" + + +def setUserLastOsuVer(userID: int, osuVer: str): + """ + Set userID's osu ver + + :param userID int: user id + :param osuVer str: osu ver + :return: + """ + glob.db.execute("UPDATE users SET osuver = %s WHERE id = %s LIMIT 1", [osuVer, userID]) \ No newline at end of file -- 2.20.1 From e83a1b5bf6494d4ed565f9d054a0d433b4f3ac26 Mon Sep 17 00:00:00 2001 From: Aoba's Phone <neko.aoba.suzukaze@gmail.com> Date: Fri, 20 Dec 2019 08:56:23 +0700 Subject: [PATCH 51/88] ??? --- helpers/kotrikhelper.py | 15 ++++++++++++++- ...elper.py~64f4109... feat: logging user osuVer | 16 ---------------- 2 files changed, 14 insertions(+), 17 deletions(-) delete mode 100644 helpers/kotrikhelper.py~64f4109... feat: logging user osuVer diff --git a/helpers/kotrikhelper.py b/helpers/kotrikhelper.py index 7c45ac7..07d2f20 100644 --- a/helpers/kotrikhelper.py +++ b/helpers/kotrikhelper.py @@ -1,4 +1,17 @@ import math +from objects import glob def secondsToFormatted(length): - return f"{math.floor(length / 60)}:{str(length % 60)}" \ No newline at end of file + return f"{math.floor(length / 60)}:{str(length % 60)}" + +def setUserLastOsuVer(userID: int, osuVer: str): + """ + Set userID's osu ver + Thanks to osu!Kurikku's pep.py + Commit ID: 64f4109b6507ee218fdaa7c3e45433df0a02a8e5 + + :param userID int: user id + :param osuVer str: osu ver + :return: + """ + glob.db.execute("UPDATE users SET osuver = %s WHERE id = %s LIMIT 1", [osuVer, userID]) \ No newline at end of file diff --git a/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer b/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer deleted file mode 100644 index 2e72edc..0000000 --- a/helpers/kotrikhelper.py~64f4109... feat: logging user osuVer +++ /dev/null @@ -1,16 +0,0 @@ -import math -from objects import glob - -def secondsToFormatted(length): - return f"{math.floor(length / 60)}:{str(length % 60)}" - - -def setUserLastOsuVer(userID: int, osuVer: str): - """ - Set userID's osu ver - - :param userID int: user id - :param osuVer str: osu ver - :return: - """ - glob.db.execute("UPDATE users SET osuver = %s WHERE id = %s LIMIT 1", [osuVer, userID]) \ No newline at end of file -- 2.20.1 From bc72d12e671ae6b8ab0599767abe8a464dcb12ff Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 21 Dec 2019 07:08:23 +0700 Subject: [PATCH 52/88] Update submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 037e453..bea974a 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 037e453812e69fbc2a0eb8f2172ffa7bc1e15bd2 +Subproject commit bea974a4db6dcf08480b410b8b0866b2f4a04c4e -- 2.20.1 From e61d187897b18332b6942e641a7acc27dbd42f25 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 21 Dec 2019 07:54:42 +0700 Subject: [PATCH 53/88] Add RX to Tillerino --- constants/fokabotCommands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 249c38b..e7ea136 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -559,8 +559,8 @@ def tillerinoMods(fro, chan, message): modsList = [message[0][i:i+2].upper() for i in range(0, len(message[0]), 2)] modsEnum = 0 for i in modsList: - if i not in ["NO", "NF", "EZ", "HD", "HR", "DT", "HT", "NC", "FL", "SO"]: - return "Invalid mods. Allowed mods: NO, NF, EZ, HD, HR, DT, HT, NC, FL, SO. Do not use spaces for multiple mods." + if i not in ["NO", "NF", "EZ", "HD", "HR", "DT", "HT", "NC", "FL", "SO", "RX"]: + return "Invalid mods. Allowed mods: NO, NF, EZ, HD, HR, DT, HT, NC, FL, SO, RX. Do not use spaces for multiple mods." if i == "NO": modsEnum = 0 break @@ -582,6 +582,8 @@ def tillerinoMods(fro, chan, message): modsEnum += mods.FLASHLIGHT elif i == "SO": modsEnum += mods.SPUNOUT + elif i == "RX": + modsEnum += mods.RELAX # Set mods token.tillerino[1] = modsEnum -- 2.20.1 From 879624e9b5ce1341c6460c1462c5c14b1dc34477 Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 22 Dec 2019 15:34:43 +0700 Subject: [PATCH 54/88] ppboard and scoreboard command --- common | 2 +- constants/fokabotCommands.py | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/common b/common index bea974a..e551e08 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit bea974a4db6dcf08480b410b8b0866b2f4a04c4e +Subproject commit e551e086d757bf5d1aecc4f0d5f15a8aa1654e2e diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index e7ea136..6eda3f1 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1304,6 +1304,36 @@ def postAnnouncement(fro, chan, message): # Post to #announce ingame chat.sendMessage(glob.BOT_NAME, "#announce", announcement) return "Announcement successfully sent." +def usePPBoard(fro, chan, message): + messages = [m.lower() for m in message] + relax = message[0] + + userID = userUtils.getID(fro) + + if 'x' in relax: + rx = True + else: + rx = False + + # Set PPBoard value in user_stats table + userUtils.setPPBoard(userID, rx) + return "You're using PPBoard in {rx}.".format(rx='relax' if rx else 'vanilla') + +def useScoreBoard(fro, chan, message): + messages = [m.lower() for m in message] + relax = message[0] + + userID = userUtils.getID(fro) + + if 'x' in relax: + rx = True + else: + rx = False + + # Set PPBoard value in user_stats table + userUtils.setScoreBoard(userID, rx) + return "You're using Scoreboard in {rx}.".format(rx='relax' if rx else 'vanilla') + def whitelistUserPPLimit(fro, chan, message): messages = [m.lower() for m in message] target = message[0] @@ -1408,6 +1438,14 @@ commands = [ }, { "trigger": "!help", "response": "Click (here)[https://ainu.pw/index.php?p=16&id=4] for full command list" + }, { + "trigger": "!ppboard", + "syntax": "<relax/vanilla>", + "callback": usePPBoard + }, { + "trigger": "!scoreboard", + "syntax": "<relax/vanilla>", + "callback": useScoreBoard }, { "trigger": "!whitelist", "privileges": privileges.ADMIN_BAN_USERS, -- 2.20.1 From faefe70822d130e49b7cb243bddb833a7beacc4a Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sun, 22 Dec 2019 15:52:56 +0700 Subject: [PATCH 55/88] Updated submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index e551e08..480fbe1 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e551e086d757bf5d1aecc4f0d5f15a8aa1654e2e +Subproject commit 480fbe11994876ff1f717d7a32a53e20308d1fcf -- 2.20.1 From a7c9d355d222ae4fae45b47d8040594733596b5f Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 11:54:16 +0700 Subject: [PATCH 56/88] Anti Ainu Client --- events/loginEvent.py | 6 ++++++ helpers/aobaHelper.py | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 helpers/aobaHelper.py diff --git a/events/loginEvent.py b/events/loginEvent.py index bcf08fb..383483d 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -7,6 +7,7 @@ from common.log import logUtils as log from common.ripple import userUtils from constants import exceptions from constants import serverPackets +from helpers import aobaHelper from helpers import chatHelper as chat from helpers import countryHelper from helpers import locationHelper @@ -221,6 +222,11 @@ def handle(tornadoRequest): if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) + # BAN AINU CLIENT + if aobaHelper.getOsuVer(userID) == "0Ainu": + responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)") + userUtils.restrict(userID) + # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py new file mode 100644 index 0000000..c333425 --- /dev/null +++ b/helpers/aobaHelper.py @@ -0,0 +1,10 @@ +from objects import glob + +def getOsuVer(userID): + """ + Get `userID`'s osu! version. + + :param userID: user id + :return: osu! version + """ + return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"] \ No newline at end of file -- 2.20.1 From 73f1e3efbf6aa2e52b6d56688598a4511502133f Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 12:41:39 +0700 Subject: [PATCH 57/88] Anti Ainu Client v2 --- events/loginEvent.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/events/loginEvent.py b/events/loginEvent.py index 383483d..0777642 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -223,9 +223,14 @@ def handle(tornadoRequest): userUtils.setCountry(userID, countryLetters) # BAN AINU CLIENT - if aobaHelper.getOsuVer(userID) == "0Ainu": - responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)") - userUtils.restrict(userID) + # 0Ainu = First Ainu build + # b20190326.2 = Ainu build 2 (MPGH PAGE 10) + # but still... no one play with b20190326.2 build right? + if aobaHelper.getOsuVer(userID) in ("0Ainu", "b20190326.2"): + if userUtils.isRestricted(userID): + userUtils.restrict(userID) + else: + responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)") # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: -- 2.20.1 From adef8f741e67afd1069b91d8073ae0f4741c4e16 Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 12:42:34 +0700 Subject: [PATCH 58/88] TAB. --- events/loginEvent.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/events/loginEvent.py b/events/loginEvent.py index 0777642..c8421c8 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -227,10 +227,10 @@ def handle(tornadoRequest): # b20190326.2 = Ainu build 2 (MPGH PAGE 10) # but still... no one play with b20190326.2 build right? if aobaHelper.getOsuVer(userID) in ("0Ainu", "b20190326.2"): - if userUtils.isRestricted(userID): - userUtils.restrict(userID) - else: - responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)") + if userUtils.isRestricted(userID): + userUtils.restrict(userID) + else: + responseToken.enqueue(serverPackets.notification("Nice, You logged in with Ainu Client. Enjoy your restriction :)") # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: -- 2.20.1 From 88458a7d6e69e633f3d1de3f6c58c41baa0aec39 Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 12:56:38 +0700 Subject: [PATCH 59/88] OK, I fixed the issue --- events/loginEvent.py | 12 ++++++++++++ helpers/aobaHelper.py | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 helpers/aobaHelper.py diff --git a/events/loginEvent.py b/events/loginEvent.py index bcf08fb..249c009 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -7,6 +7,7 @@ from common.log import logUtils as log from common.ripple import userUtils from constants import exceptions from constants import serverPackets +from helpers import aobaHelper from helpers import chatHelper as chat from helpers import countryHelper from helpers import locationHelper @@ -221,6 +222,17 @@ def handle(tornadoRequest): if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) + # BAN AINU CLIENT + # 0Ainu = First Ainu build + # b20190326.2 = Ainu build 2 (MPGH PAGE 10) + # but still... no one play with b20190326.2 build right? + + if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2"]: + if userUtils.isRestricted(userID): + responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) + else: + userUtils.restrict(userID) + # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py new file mode 100644 index 0000000..c333425 --- /dev/null +++ b/helpers/aobaHelper.py @@ -0,0 +1,10 @@ +from objects import glob + +def getOsuVer(userID): + """ + Get `userID`'s osu! version. + + :param userID: user id + :return: osu! version + """ + return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"] \ No newline at end of file -- 2.20.1 From 7abe3c7cf8e2b18276f979a101145d91dd3b3f5b Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 13:07:06 +0700 Subject: [PATCH 60/88] ID reset --- constants/serverPackets.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 91649f4..14d6b51 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -86,17 +86,14 @@ def userPanel(userID, force = False): # Get user data username = userToken.username # Custom Timezone - if userID in (1000, 1055, 1114): + if userID in (1000): timezone = 24+9 else: timezone = 24+userToken.timeOffset # Custom Countries for Users # 111 = Japan - # 36 = Belarus - if userID in (1000, 1055, 1114): + if userID in (1000): country = 111 - elif userID == 1209: - country = 36 else: country = userToken.country gameRank = userToken.gameRank @@ -111,11 +108,6 @@ def userPanel(userID, force = False): # 1000 = Aoba's User ID elif userID == 1000: userRank |= userRanks.PEPPY - # 1000 = peppy's User ID - elif userID == 1114: - userRank |= userRanks.PEPPY - elif userID == 1055: - userRank |= userRanks.MOD elif userUtils.isInPrivilegeGroup(userID, "developer"): userRank |= userRanks.ADMIN elif userUtils.isInPrivilegeGroup(userID, "chat mod"): -- 2.20.1 From 9c0313890be5762fba51ae08dcf81e6957d55164 Mon Sep 17 00:00:00 2001 From: Simon <45692977+denmarkistrash@users.noreply.github.com> Date: Wed, 25 Dec 2019 16:14:40 +0100 Subject: [PATCH 61/88] i wanna join in :) --- constants/serverPackets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 14d6b51..4ed660f 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -108,6 +108,8 @@ def userPanel(userID, force = False): # 1000 = Aoba's User ID elif userID == 1000: userRank |= userRanks.PEPPY + elif userID == 1106; + userRank |= userRanks.PEPPY elif userUtils.isInPrivilegeGroup(userID, "developer"): userRank |= userRanks.ADMIN elif userUtils.isInPrivilegeGroup(userID, "chat mod"): -- 2.20.1 From a9408a7a899169025d2c70b38b4fa624fcad6e8f Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 22:15:58 +0700 Subject: [PATCH 62/88] BIG BRUH MOMENT --- constants/serverPackets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 4ed660f..11a8b98 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -86,13 +86,13 @@ def userPanel(userID, force = False): # Get user data username = userToken.username # Custom Timezone - if userID in (1000): + if userID in (1000, 1106): timezone = 24+9 else: timezone = 24+userToken.timeOffset # Custom Countries for Users # 111 = Japan - if userID in (1000): + if userID in (1000, 1106): country = 111 else: country = userToken.country -- 2.20.1 From df3450bbcc81ed6788b0276407d35e7d75e47779 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Wed, 25 Dec 2019 22:16:32 +0700 Subject: [PATCH 63/88] Simon is foolish --- constants/serverPackets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 11a8b98..9a161a7 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -108,7 +108,7 @@ def userPanel(userID, force = False): # 1000 = Aoba's User ID elif userID == 1000: userRank |= userRanks.PEPPY - elif userID == 1106; + elif userID == 1106: userRank |= userRanks.PEPPY elif userUtils.isInPrivilegeGroup(userID, "developer"): userRank |= userRanks.ADMIN -- 2.20.1 From 2e1e21516fb577eda2f76b3feb4f58b46a27209b Mon Sep 17 00:00:00 2001 From: Ainu Server <eaglejump.suzukaze@gmail.com> Date: Thu, 26 Dec 2019 16:06:49 +0100 Subject: [PATCH 64/88] This is so annoying, let's shut it up. --- handlers/apiGetTheFuckOuttaHere.py | 12 ++++++++++++ pep.py | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 handlers/apiGetTheFuckOuttaHere.py diff --git a/handlers/apiGetTheFuckOuttaHere.py b/handlers/apiGetTheFuckOuttaHere.py new file mode 100644 index 0000000..a759a44 --- /dev/null +++ b/handlers/apiGetTheFuckOuttaHere.py @@ -0,0 +1,12 @@ +import tornado.gen +import tornado.web + +from common.web import requestsManager + + +class handler(requestsManager.asyncRequestHandler): + @tornado.web.asynchronous + @tornado.gen.engine + def asyncGet(self): + #self.set_status(404) + self.write("") diff --git a/pep.py b/pep.py index 2f932e9..1452f60 100644 --- a/pep.py +++ b/pep.py @@ -17,6 +17,7 @@ from common.log import logUtils as log from common.redis import pubSub from common.web import schiavo from handlers import apiFokabotMessageHandler +from handlers import apiGetTheFuckOuttaHere from handlers import apiIsOnlineHandler from handlers import apiOnlineUsersHandler from handlers import apiServerStatusHandler @@ -50,6 +51,7 @@ def make_app(): (r"/api/v1/ciTrigger", ciTriggerHandler.handler), (r"/api/v1/verifiedStatus", apiVerifiedStatusHandler.handler), (r"/api/v1/fokabotMessage", apiFokabotMessageHandler.handler), + (r"/api/v2/clients/.*", apiGetTheFuckOuttaHere.handler), (r"/stress", heavyHandler.handler) ]) -- 2.20.1 From 6404d970aa993028b46cdfc8e0817c740f861e8b Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 4 Jan 2020 18:16:43 +0700 Subject: [PATCH 65/88] Additional configuration Signed-off-by: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> --- helpers/configHelper.py | 9 +++++++-- pep.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/helpers/configHelper.py b/helpers/configHelper.py index 3bc47be..5501000 100644 --- a/helpers/configHelper.py +++ b/helpers/configHelper.py @@ -86,6 +86,8 @@ class config: parsedConfig.get("localize", "enable") parsedConfig.get("localize", "ipapiurl") + + parsedConfig.get("custom", "config") return True except configparser.Error: return False @@ -119,12 +121,12 @@ class config: self.config.set("server", "gzip", "1") self.config.set("server", "gziplevel", "6") self.config.set("server", "cikey", "changeme") - self.config.set("server", "letsapiurl", "http://.../letsapi") + self.config.set("server", "letsapiurl", "http://127.0.0.1:5002/letsapi") self.config.set("server", "deltaurl", "delta.ppy.sh") self.config.set("server", "publicdelta", "0") self.config.add_section("cheesegull") - self.config.set("cheesegull", "apiurl", "http://cheesegu.ll/api") + self.config.set("cheesegull", "apiurl", "https://storage.ainu.pw/api") self.config.set("cheesegull", "apikey", "") self.config.add_section("debug") @@ -156,6 +158,9 @@ 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() diff --git a/pep.py b/pep.py index 2f932e9..7ae0564 100644 --- a/pep.py +++ b/pep.py @@ -9,6 +9,9 @@ import tornado.web from raven.contrib.tornado import AsyncSentryClient import redis +import json +import shutil + from common import generalUtils, agpl from common.constants import bcolors from common.db import dbConnector @@ -86,6 +89,23 @@ if __name__ == "__main__": 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) + sys.exit() + # Create data folder if needed consoleHelper.printNoNl("> Checking folders... ") paths = [".data"] -- 2.20.1 From 87bfae193bd79afa163a035c5ac985c1faf2e04a Mon Sep 17 00:00:00 2001 From: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> Date: Sat, 4 Jan 2020 18:27:24 +0700 Subject: [PATCH 66/88] lol Signed-off-by: Aoba Suzukaze <neko.aoba.suzukaze@gmail.com> --- pep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep.py b/pep.py index 2a61b63..19a5a6e 100644 --- a/pep.py +++ b/pep.py @@ -96,7 +96,7 @@ if __name__ == "__main__": 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) + 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: -- 2.20.1 From 3be3c4db7d5091a41bab67013d3cf46c6bfc533d Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Sun, 5 Jan 2020 20:50:32 +0000 Subject: [PATCH 67/88] Add anticheat mode --- common | 2 +- events/loginEvent.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/common b/common index 480fbe1..aa8cd0b 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 480fbe11994876ff1f717d7a32a53e20308d1fcf +Subproject commit aa8cd0bd871356d7dc31c2e42d4b18791f2096bb diff --git a/events/loginEvent.py b/events/loginEvent.py index 249c009..6efe4f2 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -225,13 +225,16 @@ def handle(tornadoRequest): # BAN AINU CLIENT # 0Ainu = First Ainu build # b20190326.2 = Ainu build 2 (MPGH PAGE 10) + # b20191223.3 = Unknown Ainu Build + # b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Unknown Ainu Build 2 # but still... no one play with b20190326.2 build right? - if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2"]: - if userUtils.isRestricted(userID): - responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) - else: - userUtils.restrict(userID) + if glob.conf.extra["mode"]["anticheat"]: + if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]: + if userUtils.isRestricted(userID): + responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) + else: + userUtils.restrict(userID) # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: -- 2.20.1 From 8b3759940309d799e3ee28bb7473450bb93629d7 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Mon, 6 Jan 2020 16:12:16 +0700 Subject: [PATCH 68/88] Anti Ainu Client 2020 --- events/loginEvent.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/events/loginEvent.py b/events/loginEvent.py index 6efe4f2..b84cb3d 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -230,7 +230,13 @@ def handle(tornadoRequest): # but still... no one play with b20190326.2 build right? if glob.conf.extra["mode"]["anticheat"]: - if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]: + # Check Ainu New Year 2020 Client + if tornadoRequest.request.headers.get("ainu") == "happy": + if userUtils.isRestricted(userID): + responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client... Happy New Year 2020 and Enjoy your restriction :)")) + else: + userUtils.restrict(userID) + elif aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]: if userUtils.isRestricted(userID): responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) else: -- 2.20.1 From 5f3a4071d504871cdfe06424b5245b1cca6b70d6 Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 04:53:17 +0000 Subject: [PATCH 69/88] More AntiCheat stuff? idk what i do --- common | 2 +- events/loginEvent.py | 52 ++++++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/common b/common index aa8cd0b..a6e28da 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit aa8cd0bd871356d7dc31c2e42d4b18791f2096bb +Subproject commit a6e28daf8f85b152b0fb5264ed59ee45dedf0b0c diff --git a/events/loginEvent.py b/events/loginEvent.py index 6efe4f2..2eeada3 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -163,6 +163,41 @@ def handle(tornadoRequest): # We are mod/admin, send warning notification and continue responseToken.enqueue(serverPackets.notification("Bancho is in maintenance mode. Only mods/admins have full access to the server.\nType !system maintenance off in chat to turn off maintenance mode.")) + # BAN CUSTOM CHEAT CLIENTS + # 0Ainu = First Ainu build + # b20190326.2 = Ainu build 2 (MPGH PAGE 10) + # b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Ainu Aoba's Birthday Build + # b20191223.3 = Unknown Ainu build? (Taken from most users osuver in cookiezi.pw) + # b20190226.2 = hqOsu (hq-af) + if glob.conf.extra["mode"]["anticheat"]: + # Ainu Client 2020 update + if tornadoRequest.request.headers.get("ainu") == "happy": + log.info("Account {} tried to use Ainu Client 2020!".format(userID)) + if userUtils.isRestricted(userID): + responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client... Happy New Year 2020 and Enjoy your restriction :)")) + else: + glob.tokens.deleteToken(userID) + userUtils.restrict(userID) + raise exceptions.loginCheatClientsException() + # Ainu Client 2019 + elif aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]: + log.info("Account {} tried to use Ainu Client!".format(userID)) + if userUtils.isRestricted(userID): + responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) + else: + glob.tokens.deleteToken(userID) + userUtils.restrict(userID) + raise exceptions.loginCheatClientsException() + # hqOsu + elif aobaHelper.getOsuVer(userID) == "b20190226.2": + log.info("Account {} tried to use hqOsu!".format(userID)) + if userUtils.isRestricted(userID): + responseToken.enqueue(serverPackets.notification("Trying to use hqOsu in here? Well... No, sorry. We don't allow cheats here. Go play https://cookiezi.pw or others cheat server.")) + else: + glob.tokens.deleteToken(userID) + userUtils.restrict(userID) + raise exceptions.loginCheatClientsException() + # Send all needed login packets responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds)) responseToken.enqueue(serverPackets.userID(userID)) @@ -222,20 +257,6 @@ def handle(tornadoRequest): if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) - # BAN AINU CLIENT - # 0Ainu = First Ainu build - # b20190326.2 = Ainu build 2 (MPGH PAGE 10) - # b20191223.3 = Unknown Ainu Build - # b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Unknown Ainu Build 2 - # but still... no one play with b20190326.2 build right? - - if glob.conf.extra["mode"]["anticheat"]: - if aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]: - if userUtils.isRestricted(userID): - responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) - else: - userUtils.restrict(userID) - # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) @@ -258,6 +279,9 @@ def handle(tornadoRequest): except exceptions.loginLockedException: # Login banned error packet responseData += serverPackets.loginLocked() + except exceptions.loginCheatClientsException: + # Banned for logging in with cheats + responseData += serverPackets.loginCheats() except exceptions.banchoMaintenanceException: # Bancho is in maintenance mode responseData = bytes() -- 2.20.1 From b48667e78d73e56e39d1f386698b700ad2161ec9 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 11:57:58 +0700 Subject: [PATCH 70/88] what?! --- events/loginEvent.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/events/loginEvent.py b/events/loginEvent.py index 3b84874..2eeada3 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -257,29 +257,6 @@ def handle(tornadoRequest): if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) -<<<<<<< HEAD -======= - # BAN AINU CLIENT - # 0Ainu = First Ainu build - # b20190326.2 = Ainu build 2 (MPGH PAGE 10) - # b20191223.3 = Unknown Ainu Build - # b20190401.22f56c084ba339eefd9c7ca4335e246f80 = Unknown Ainu Build 2 - # but still... no one play with b20190326.2 build right? - - if glob.conf.extra["mode"]["anticheat"]: - # Check Ainu New Year 2020 Client - if tornadoRequest.request.headers.get("ainu") == "happy": - if userUtils.isRestricted(userID): - responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client... Happy New Year 2020 and Enjoy your restriction :)")) - else: - userUtils.restrict(userID) - elif aobaHelper.getOsuVer(userID) in ["0Ainu", "b20190326.2", "b20190401.22f56c084ba339eefd9c7ca4335e246f80", "b20191223.3"]: - if userUtils.isRestricted(userID): - responseToken.enqueue(serverPackets.notification("You're banned because you're currently using Ainu Client. Enjoy your restriction :)")) - else: - userUtils.restrict(userID) - ->>>>>>> 8b3759940309d799e3ee28bb7473450bb93629d7 # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) -- 2.20.1 From 125acb58e370d5bf35cbec79fffb7b12759e17ca Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 11:59:24 +0700 Subject: [PATCH 71/88] OK, I forgot this. --- constants/exceptions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/constants/exceptions.py b/constants/exceptions.py index 258ae0b..525106b 100644 --- a/constants/exceptions.py +++ b/constants/exceptions.py @@ -82,6 +82,9 @@ class haxException(Exception): class forceUpdateException(Exception): pass +class loginCheatClientsException(Exception): + pass + class loginLockedException(Exception): pass @@ -107,4 +110,4 @@ class wrongChannelException(Exception): pass class periodicLoopException(Exception): - pass \ No newline at end of file + pass -- 2.20.1 From 3ca755f27d2b2d7b2848ffedfb1f308a81fe4233 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 12:00:52 +0700 Subject: [PATCH 72/88] Add loginCheats --- constants/serverPackets.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 9a161a7..8d45084 100644 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -27,6 +27,11 @@ def loginLocked(): def loginError(): return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]]) +def loginCheats(): + packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]]) + packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.") + return packets + def needSupporter(): return packetHelper.buildPacket(packetIDs.server_userID, [[-6, dataTypes.SINT32]]) -- 2.20.1 From cb9e4ccb6c19a9655c98fafef4b47e2d1ed1116f Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 05:02:17 +0000 Subject: [PATCH 73/88] I need to commit what? :thonk: --- .gitignore | 0 .gitmodules | 0 .landscape.yaml | 0 LICENSE | 0 README.md | 0 constants/__init__.py | 0 constants/clientPackets.py | 0 constants/dataTypes.py | 0 constants/exceptions.py | 0 constants/fokabotCommands.py | 0 constants/matchModModes.py | 0 constants/matchScoringTypes.py | 0 constants/matchTeamTypes.py | 0 constants/matchTeams.py | 0 constants/messageTemplates.py | 0 constants/packetIDs.py | 0 constants/serverPackets.py | 0 constants/slotStatuses.py | 0 constants/userRanks.py | 0 events/__init__.py | 0 events/cantSpectateEvent.py | 0 events/changeActionEvent.py | 0 events/changeMatchModsEvent.py | 0 events/changeMatchPasswordEvent.py | 0 events/changeMatchSettingsEvent.py | 0 events/changeSlotEvent.py | 0 events/channelJoinEvent.py | 0 events/channelPartEvent.py | 0 events/createMatchEvent.py | 0 events/friendAddEvent.py | 0 events/friendRemoveEvent.py | 0 events/joinLobbyEvent.py | 0 events/joinMatchEvent.py | 0 events/loginEvent.py | 0 events/logoutEvent.py | 0 events/matchBeatmapEvent.py | 0 events/matchChangeTeamEvent.py | 0 events/matchCompleteEvent.py | 0 events/matchFailedEvent.py | 0 events/matchFramesEvent.py | 0 events/matchHasBeatmapEvent.py | 0 events/matchInviteEvent.py | 0 events/matchLockEvent.py | 0 events/matchNoBeatmapEvent.py | 0 events/matchPlayerLoadEvent.py | 0 events/matchReadyEvent.py | 0 events/matchSkipEvent.py | 0 events/matchStartEvent.py | 0 events/matchTransferHostEvent.py | 0 events/partLobbyEvent.py | 0 events/partMatchEvent.py | 0 events/requestStatusUpdateEvent.py | 0 events/sendPrivateMessageEvent.py | 0 events/sendPublicMessageEvent.py | 0 events/setAwayMessageEvent.py | 0 events/spectateFramesEvent.py | 0 events/startSpectatingEvent.py | 0 events/stopSpectatingEvent.py | 0 events/tournamentJoinMatchChannelEvent.py | 0 events/tournamentLeaveMatchChannelEvent.py | 0 events/tournamentMatchInfoRequestEvent.py | 0 events/userPanelRequestEvent.py | 0 events/userStatsRequestEvent.py | 0 filters.txt | 0 full_build.sh | 0 handlers/__init__.py | 0 handlers/apiFokabotMessageHandler.py | 0 handlers/apiGetTheFuckOuttaHere.py | 0 handlers/apiIsOnlineHandler.py | 0 handlers/apiOnlineUsersHandler.py | 0 handlers/apiServerStatusHandler.py | 0 handlers/apiVerifiedStatusHandler.py | 0 handlers/chatHelper.py | 0 handlers/ciTriggerHandler.py | 0 handlers/heavyHandler.py | 0 handlers/mainHandler.pyx | 0 helpers/__init__.py | 0 helpers/aobaHelper.py | 0 helpers/chatHelper.py | 0 helpers/configHelper.py | 0 helpers/consoleHelper.py | 0 helpers/countryHelper.py | 0 helpers/cryptHelper.py | 0 helpers/kotrikhelper.py | 0 helpers/locationHelper.py | 0 helpers/packetHelper.pyx | 0 helpers/systemHelper.py | 0 irc/__init__.py | 0 irc/ircserver.py | 0 objects/__init__.py | 0 objects/banchoConfig.py | 0 objects/channel.py | 0 objects/channelList.py | 0 objects/chatFilters.py | 0 objects/fokabot.py | 0 objects/glob.py | 0 objects/match.py | 0 objects/matchList.py | 0 objects/osuToken.py | 0 objects/stream.py | 0 objects/streamList.py | 0 objects/tokenList.py | 0 pep.py | 0 pubSubHandlers/__init__.py | 0 pubSubHandlers/banHandler.py | 0 pubSubHandlers/changeUsernameHandler.py | 0 pubSubHandlers/disconnectHandler.py | 0 pubSubHandlers/notificationHandler.py | 0 pubSubHandlers/setMainMenuIconHandler.py | 0 pubSubHandlers/updateSilenceHandler.py | 0 pubSubHandlers/updateStatsHandler.py | 0 requirements.txt | 0 setup.py | 0 version | 0 114 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .gitmodules mode change 100644 => 100755 .landscape.yaml mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 constants/__init__.py mode change 100644 => 100755 constants/clientPackets.py mode change 100644 => 100755 constants/dataTypes.py mode change 100644 => 100755 constants/exceptions.py mode change 100644 => 100755 constants/fokabotCommands.py mode change 100644 => 100755 constants/matchModModes.py mode change 100644 => 100755 constants/matchScoringTypes.py mode change 100644 => 100755 constants/matchTeamTypes.py mode change 100644 => 100755 constants/matchTeams.py mode change 100644 => 100755 constants/messageTemplates.py mode change 100644 => 100755 constants/packetIDs.py mode change 100644 => 100755 constants/serverPackets.py mode change 100644 => 100755 constants/slotStatuses.py mode change 100644 => 100755 constants/userRanks.py mode change 100644 => 100755 events/__init__.py mode change 100644 => 100755 events/cantSpectateEvent.py mode change 100644 => 100755 events/changeActionEvent.py mode change 100644 => 100755 events/changeMatchModsEvent.py mode change 100644 => 100755 events/changeMatchPasswordEvent.py mode change 100644 => 100755 events/changeMatchSettingsEvent.py mode change 100644 => 100755 events/changeSlotEvent.py mode change 100644 => 100755 events/channelJoinEvent.py mode change 100644 => 100755 events/channelPartEvent.py mode change 100644 => 100755 events/createMatchEvent.py mode change 100644 => 100755 events/friendAddEvent.py mode change 100644 => 100755 events/friendRemoveEvent.py mode change 100644 => 100755 events/joinLobbyEvent.py mode change 100644 => 100755 events/joinMatchEvent.py mode change 100644 => 100755 events/loginEvent.py mode change 100644 => 100755 events/logoutEvent.py mode change 100644 => 100755 events/matchBeatmapEvent.py mode change 100644 => 100755 events/matchChangeTeamEvent.py mode change 100644 => 100755 events/matchCompleteEvent.py mode change 100644 => 100755 events/matchFailedEvent.py mode change 100644 => 100755 events/matchFramesEvent.py mode change 100644 => 100755 events/matchHasBeatmapEvent.py mode change 100644 => 100755 events/matchInviteEvent.py mode change 100644 => 100755 events/matchLockEvent.py mode change 100644 => 100755 events/matchNoBeatmapEvent.py mode change 100644 => 100755 events/matchPlayerLoadEvent.py mode change 100644 => 100755 events/matchReadyEvent.py mode change 100644 => 100755 events/matchSkipEvent.py mode change 100644 => 100755 events/matchStartEvent.py mode change 100644 => 100755 events/matchTransferHostEvent.py mode change 100644 => 100755 events/partLobbyEvent.py mode change 100644 => 100755 events/partMatchEvent.py mode change 100644 => 100755 events/requestStatusUpdateEvent.py mode change 100644 => 100755 events/sendPrivateMessageEvent.py mode change 100644 => 100755 events/sendPublicMessageEvent.py mode change 100644 => 100755 events/setAwayMessageEvent.py mode change 100644 => 100755 events/spectateFramesEvent.py mode change 100644 => 100755 events/startSpectatingEvent.py mode change 100644 => 100755 events/stopSpectatingEvent.py mode change 100644 => 100755 events/tournamentJoinMatchChannelEvent.py mode change 100644 => 100755 events/tournamentLeaveMatchChannelEvent.py mode change 100644 => 100755 events/tournamentMatchInfoRequestEvent.py mode change 100644 => 100755 events/userPanelRequestEvent.py mode change 100644 => 100755 events/userStatsRequestEvent.py mode change 100644 => 100755 filters.txt mode change 100644 => 100755 full_build.sh mode change 100644 => 100755 handlers/__init__.py mode change 100644 => 100755 handlers/apiFokabotMessageHandler.py mode change 100644 => 100755 handlers/apiGetTheFuckOuttaHere.py mode change 100644 => 100755 handlers/apiIsOnlineHandler.py mode change 100644 => 100755 handlers/apiOnlineUsersHandler.py mode change 100644 => 100755 handlers/apiServerStatusHandler.py mode change 100644 => 100755 handlers/apiVerifiedStatusHandler.py mode change 100644 => 100755 handlers/chatHelper.py mode change 100644 => 100755 handlers/ciTriggerHandler.py mode change 100644 => 100755 handlers/heavyHandler.py mode change 100644 => 100755 handlers/mainHandler.pyx mode change 100644 => 100755 helpers/__init__.py mode change 100644 => 100755 helpers/aobaHelper.py mode change 100644 => 100755 helpers/chatHelper.py mode change 100644 => 100755 helpers/configHelper.py mode change 100644 => 100755 helpers/consoleHelper.py mode change 100644 => 100755 helpers/countryHelper.py mode change 100644 => 100755 helpers/cryptHelper.py mode change 100644 => 100755 helpers/kotrikhelper.py mode change 100644 => 100755 helpers/locationHelper.py mode change 100644 => 100755 helpers/packetHelper.pyx mode change 100644 => 100755 helpers/systemHelper.py mode change 100644 => 100755 irc/__init__.py mode change 100644 => 100755 irc/ircserver.py mode change 100644 => 100755 objects/__init__.py mode change 100644 => 100755 objects/banchoConfig.py mode change 100644 => 100755 objects/channel.py mode change 100644 => 100755 objects/channelList.py mode change 100644 => 100755 objects/chatFilters.py mode change 100644 => 100755 objects/fokabot.py mode change 100644 => 100755 objects/glob.py mode change 100644 => 100755 objects/match.py mode change 100644 => 100755 objects/matchList.py mode change 100644 => 100755 objects/osuToken.py mode change 100644 => 100755 objects/stream.py mode change 100644 => 100755 objects/streamList.py mode change 100644 => 100755 objects/tokenList.py mode change 100644 => 100755 pep.py mode change 100644 => 100755 pubSubHandlers/__init__.py mode change 100644 => 100755 pubSubHandlers/banHandler.py mode change 100644 => 100755 pubSubHandlers/changeUsernameHandler.py mode change 100644 => 100755 pubSubHandlers/disconnectHandler.py mode change 100644 => 100755 pubSubHandlers/notificationHandler.py mode change 100644 => 100755 pubSubHandlers/setMainMenuIconHandler.py mode change 100644 => 100755 pubSubHandlers/updateSilenceHandler.py mode change 100644 => 100755 pubSubHandlers/updateStatsHandler.py mode change 100644 => 100755 requirements.txt mode change 100644 => 100755 setup.py mode change 100644 => 100755 version diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.gitmodules b/.gitmodules old mode 100644 new mode 100755 diff --git a/.landscape.yaml b/.landscape.yaml old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/constants/__init__.py b/constants/__init__.py old mode 100644 new mode 100755 diff --git a/constants/clientPackets.py b/constants/clientPackets.py old mode 100644 new mode 100755 diff --git a/constants/dataTypes.py b/constants/dataTypes.py old mode 100644 new mode 100755 diff --git a/constants/exceptions.py b/constants/exceptions.py old mode 100644 new mode 100755 diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py old mode 100644 new mode 100755 diff --git a/constants/matchModModes.py b/constants/matchModModes.py old mode 100644 new mode 100755 diff --git a/constants/matchScoringTypes.py b/constants/matchScoringTypes.py old mode 100644 new mode 100755 diff --git a/constants/matchTeamTypes.py b/constants/matchTeamTypes.py old mode 100644 new mode 100755 diff --git a/constants/matchTeams.py b/constants/matchTeams.py old mode 100644 new mode 100755 diff --git a/constants/messageTemplates.py b/constants/messageTemplates.py old mode 100644 new mode 100755 diff --git a/constants/packetIDs.py b/constants/packetIDs.py old mode 100644 new mode 100755 diff --git a/constants/serverPackets.py b/constants/serverPackets.py old mode 100644 new mode 100755 diff --git a/constants/slotStatuses.py b/constants/slotStatuses.py old mode 100644 new mode 100755 diff --git a/constants/userRanks.py b/constants/userRanks.py old mode 100644 new mode 100755 diff --git a/events/__init__.py b/events/__init__.py old mode 100644 new mode 100755 diff --git a/events/cantSpectateEvent.py b/events/cantSpectateEvent.py old mode 100644 new mode 100755 diff --git a/events/changeActionEvent.py b/events/changeActionEvent.py old mode 100644 new mode 100755 diff --git a/events/changeMatchModsEvent.py b/events/changeMatchModsEvent.py old mode 100644 new mode 100755 diff --git a/events/changeMatchPasswordEvent.py b/events/changeMatchPasswordEvent.py old mode 100644 new mode 100755 diff --git a/events/changeMatchSettingsEvent.py b/events/changeMatchSettingsEvent.py old mode 100644 new mode 100755 diff --git a/events/changeSlotEvent.py b/events/changeSlotEvent.py old mode 100644 new mode 100755 diff --git a/events/channelJoinEvent.py b/events/channelJoinEvent.py old mode 100644 new mode 100755 diff --git a/events/channelPartEvent.py b/events/channelPartEvent.py old mode 100644 new mode 100755 diff --git a/events/createMatchEvent.py b/events/createMatchEvent.py old mode 100644 new mode 100755 diff --git a/events/friendAddEvent.py b/events/friendAddEvent.py old mode 100644 new mode 100755 diff --git a/events/friendRemoveEvent.py b/events/friendRemoveEvent.py old mode 100644 new mode 100755 diff --git a/events/joinLobbyEvent.py b/events/joinLobbyEvent.py old mode 100644 new mode 100755 diff --git a/events/joinMatchEvent.py b/events/joinMatchEvent.py old mode 100644 new mode 100755 diff --git a/events/loginEvent.py b/events/loginEvent.py old mode 100644 new mode 100755 diff --git a/events/logoutEvent.py b/events/logoutEvent.py old mode 100644 new mode 100755 diff --git a/events/matchBeatmapEvent.py b/events/matchBeatmapEvent.py old mode 100644 new mode 100755 diff --git a/events/matchChangeTeamEvent.py b/events/matchChangeTeamEvent.py old mode 100644 new mode 100755 diff --git a/events/matchCompleteEvent.py b/events/matchCompleteEvent.py old mode 100644 new mode 100755 diff --git a/events/matchFailedEvent.py b/events/matchFailedEvent.py old mode 100644 new mode 100755 diff --git a/events/matchFramesEvent.py b/events/matchFramesEvent.py old mode 100644 new mode 100755 diff --git a/events/matchHasBeatmapEvent.py b/events/matchHasBeatmapEvent.py old mode 100644 new mode 100755 diff --git a/events/matchInviteEvent.py b/events/matchInviteEvent.py old mode 100644 new mode 100755 diff --git a/events/matchLockEvent.py b/events/matchLockEvent.py old mode 100644 new mode 100755 diff --git a/events/matchNoBeatmapEvent.py b/events/matchNoBeatmapEvent.py old mode 100644 new mode 100755 diff --git a/events/matchPlayerLoadEvent.py b/events/matchPlayerLoadEvent.py old mode 100644 new mode 100755 diff --git a/events/matchReadyEvent.py b/events/matchReadyEvent.py old mode 100644 new mode 100755 diff --git a/events/matchSkipEvent.py b/events/matchSkipEvent.py old mode 100644 new mode 100755 diff --git a/events/matchStartEvent.py b/events/matchStartEvent.py old mode 100644 new mode 100755 diff --git a/events/matchTransferHostEvent.py b/events/matchTransferHostEvent.py old mode 100644 new mode 100755 diff --git a/events/partLobbyEvent.py b/events/partLobbyEvent.py old mode 100644 new mode 100755 diff --git a/events/partMatchEvent.py b/events/partMatchEvent.py old mode 100644 new mode 100755 diff --git a/events/requestStatusUpdateEvent.py b/events/requestStatusUpdateEvent.py old mode 100644 new mode 100755 diff --git a/events/sendPrivateMessageEvent.py b/events/sendPrivateMessageEvent.py old mode 100644 new mode 100755 diff --git a/events/sendPublicMessageEvent.py b/events/sendPublicMessageEvent.py old mode 100644 new mode 100755 diff --git a/events/setAwayMessageEvent.py b/events/setAwayMessageEvent.py old mode 100644 new mode 100755 diff --git a/events/spectateFramesEvent.py b/events/spectateFramesEvent.py old mode 100644 new mode 100755 diff --git a/events/startSpectatingEvent.py b/events/startSpectatingEvent.py old mode 100644 new mode 100755 diff --git a/events/stopSpectatingEvent.py b/events/stopSpectatingEvent.py old mode 100644 new mode 100755 diff --git a/events/tournamentJoinMatchChannelEvent.py b/events/tournamentJoinMatchChannelEvent.py old mode 100644 new mode 100755 diff --git a/events/tournamentLeaveMatchChannelEvent.py b/events/tournamentLeaveMatchChannelEvent.py old mode 100644 new mode 100755 diff --git a/events/tournamentMatchInfoRequestEvent.py b/events/tournamentMatchInfoRequestEvent.py old mode 100644 new mode 100755 diff --git a/events/userPanelRequestEvent.py b/events/userPanelRequestEvent.py old mode 100644 new mode 100755 diff --git a/events/userStatsRequestEvent.py b/events/userStatsRequestEvent.py old mode 100644 new mode 100755 diff --git a/filters.txt b/filters.txt old mode 100644 new mode 100755 diff --git a/full_build.sh b/full_build.sh old mode 100644 new mode 100755 diff --git a/handlers/__init__.py b/handlers/__init__.py old mode 100644 new mode 100755 diff --git a/handlers/apiFokabotMessageHandler.py b/handlers/apiFokabotMessageHandler.py old mode 100644 new mode 100755 diff --git a/handlers/apiGetTheFuckOuttaHere.py b/handlers/apiGetTheFuckOuttaHere.py old mode 100644 new mode 100755 diff --git a/handlers/apiIsOnlineHandler.py b/handlers/apiIsOnlineHandler.py old mode 100644 new mode 100755 diff --git a/handlers/apiOnlineUsersHandler.py b/handlers/apiOnlineUsersHandler.py old mode 100644 new mode 100755 diff --git a/handlers/apiServerStatusHandler.py b/handlers/apiServerStatusHandler.py old mode 100644 new mode 100755 diff --git a/handlers/apiVerifiedStatusHandler.py b/handlers/apiVerifiedStatusHandler.py old mode 100644 new mode 100755 diff --git a/handlers/chatHelper.py b/handlers/chatHelper.py old mode 100644 new mode 100755 diff --git a/handlers/ciTriggerHandler.py b/handlers/ciTriggerHandler.py old mode 100644 new mode 100755 diff --git a/handlers/heavyHandler.py b/handlers/heavyHandler.py old mode 100644 new mode 100755 diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx old mode 100644 new mode 100755 diff --git a/helpers/__init__.py b/helpers/__init__.py old mode 100644 new mode 100755 diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py old mode 100644 new mode 100755 diff --git a/helpers/chatHelper.py b/helpers/chatHelper.py old mode 100644 new mode 100755 diff --git a/helpers/configHelper.py b/helpers/configHelper.py old mode 100644 new mode 100755 diff --git a/helpers/consoleHelper.py b/helpers/consoleHelper.py old mode 100644 new mode 100755 diff --git a/helpers/countryHelper.py b/helpers/countryHelper.py old mode 100644 new mode 100755 diff --git a/helpers/cryptHelper.py b/helpers/cryptHelper.py old mode 100644 new mode 100755 diff --git a/helpers/kotrikhelper.py b/helpers/kotrikhelper.py old mode 100644 new mode 100755 diff --git a/helpers/locationHelper.py b/helpers/locationHelper.py old mode 100644 new mode 100755 diff --git a/helpers/packetHelper.pyx b/helpers/packetHelper.pyx old mode 100644 new mode 100755 diff --git a/helpers/systemHelper.py b/helpers/systemHelper.py old mode 100644 new mode 100755 diff --git a/irc/__init__.py b/irc/__init__.py old mode 100644 new mode 100755 diff --git a/irc/ircserver.py b/irc/ircserver.py old mode 100644 new mode 100755 diff --git a/objects/__init__.py b/objects/__init__.py old mode 100644 new mode 100755 diff --git a/objects/banchoConfig.py b/objects/banchoConfig.py old mode 100644 new mode 100755 diff --git a/objects/channel.py b/objects/channel.py old mode 100644 new mode 100755 diff --git a/objects/channelList.py b/objects/channelList.py old mode 100644 new mode 100755 diff --git a/objects/chatFilters.py b/objects/chatFilters.py old mode 100644 new mode 100755 diff --git a/objects/fokabot.py b/objects/fokabot.py old mode 100644 new mode 100755 diff --git a/objects/glob.py b/objects/glob.py old mode 100644 new mode 100755 diff --git a/objects/match.py b/objects/match.py old mode 100644 new mode 100755 diff --git a/objects/matchList.py b/objects/matchList.py old mode 100644 new mode 100755 diff --git a/objects/osuToken.py b/objects/osuToken.py old mode 100644 new mode 100755 diff --git a/objects/stream.py b/objects/stream.py old mode 100644 new mode 100755 diff --git a/objects/streamList.py b/objects/streamList.py old mode 100644 new mode 100755 diff --git a/objects/tokenList.py b/objects/tokenList.py old mode 100644 new mode 100755 diff --git a/pep.py b/pep.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/__init__.py b/pubSubHandlers/__init__.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/banHandler.py b/pubSubHandlers/banHandler.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/changeUsernameHandler.py b/pubSubHandlers/changeUsernameHandler.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/disconnectHandler.py b/pubSubHandlers/disconnectHandler.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/notificationHandler.py b/pubSubHandlers/notificationHandler.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/setMainMenuIconHandler.py b/pubSubHandlers/setMainMenuIconHandler.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/updateSilenceHandler.py b/pubSubHandlers/updateSilenceHandler.py old mode 100644 new mode 100755 diff --git a/pubSubHandlers/updateStatsHandler.py b/pubSubHandlers/updateStatsHandler.py old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 diff --git a/version b/version old mode 100644 new mode 100755 -- 2.20.1 From a2aca48e5f8cc6cc9dc860ded478795e093d1c2b Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 05:12:54 +0000 Subject: [PATCH 74/88] Where is the submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a6e28da..a6fbeb4 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a6e28daf8f85b152b0fb5264ed59ee45dedf0b0c +Subproject commit a6fbeb4533938afbd3db2efc7f792c229e417ed7 -- 2.20.1 From cd751cccde3f7b811945046d04628d75b5d65b1e Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 05:16:09 +0000 Subject: [PATCH 75/88] Wrong build --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a6fbeb4..8c621cb 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a6fbeb4533938afbd3db2efc7f792c229e417ed7 +Subproject commit 8c621cbdee75bcf4603b8ad3bccdc61ad0813646 -- 2.20.1 From 25d8896a37c39f534ba4732a1ce2f74de74a81cf Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 7 Jan 2020 10:48:18 +0000 Subject: [PATCH 76/88] RTX player when login with cheats client --- constants/serverPackets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 8d45084..0e80d8f 100755 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -28,7 +28,9 @@ def loginError(): return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]]) def loginCheats(): + message = "You better quit cheating! >_< ~Aoba" packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]]) + packets += packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]]) packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.") return packets -- 2.20.1 From 67d1690e358a9c4088d9a23fcf7379f8e4773265 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Thu, 9 Jan 2020 13:59:46 +0700 Subject: [PATCH 77/88] Change Bancho page --- handlers/mainHandler.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/mainHandler.pyx b/handlers/mainHandler.pyx index de6cc62..977e90d 100755 --- a/handlers/mainHandler.pyx +++ b/handlers/mainHandler.pyx @@ -244,6 +244,6 @@ class handler(requestsManager.asyncRequestHandler): @tornado.gen.engine def asyncGet(self): html = "<html><head><title>Aoba's a cutie?</title>" - html += "<iframe src='https://ghostbin.co/paste/8j2ft' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>" + html += "<iframe src='https://ghostbin.co/paste/bwe8z' style='position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;'></iframe>" #Yes. I just wrote the credit... in it. self.write(html) -- 2.20.1 From ed21cc1ee84e467502b98d271a4487c9cf267f7d Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Fri, 17 Jan 2020 00:24:07 +0000 Subject: [PATCH 78/88] Updated submodule --- common | 2 +- constants/exceptions.py | 2 +- constants/serverPackets.py | 5 +++++ events/loginEvent.py | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/common b/common index 8c621cb..345e62a 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 8c621cbdee75bcf4603b8ad3bccdc61ad0813646 +Subproject commit 345e62a7c5ec00e4698220574ebf6e7a1bc1ace7 diff --git a/constants/exceptions.py b/constants/exceptions.py index 525106b..2543404 100755 --- a/constants/exceptions.py +++ b/constants/exceptions.py @@ -110,4 +110,4 @@ class wrongChannelException(Exception): pass class periodicLoopException(Exception): - pass + pass \ No newline at end of file diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 0e80d8f..10e4f7d 100755 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -24,6 +24,11 @@ def loginLocked(): packets += notification("Well... Your account is locked but everything still in the website ya know? and uh... You can appeal us at Discord! (You can go to our website for the link!)") return packets +def loginCheats(): + packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]]) + packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.") + return packets + def loginError(): return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]]) diff --git a/events/loginEvent.py b/events/loginEvent.py index 2eeada3..47418b8 100755 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -309,4 +309,4 @@ def handle(tornadoRequest): log.info("Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP), "bunker") # Return token string and data - return responseTokenString, responseData + return responseTokenString, responseData \ No newline at end of file -- 2.20.1 From af02c7fd5fafa37901d50d2b586012300829cedf Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Fri, 17 Jan 2020 08:34:59 +0700 Subject: [PATCH 79/88] That ain't my fault! It's GitHub fault! --- constants/serverPackets.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/constants/serverPackets.py b/constants/serverPackets.py index 10e4f7d..0e80d8f 100755 --- a/constants/serverPackets.py +++ b/constants/serverPackets.py @@ -24,11 +24,6 @@ def loginLocked(): packets += notification("Well... Your account is locked but everything still in the website ya know? and uh... You can appeal us at Discord! (You can go to our website for the link!)") return packets -def loginCheats(): - packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]]) - packets += notification("Please... don't login with cheats client... Play on cheating server instead of cheating on our server. Thank you.") - return packets - def loginError(): return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]]) -- 2.20.1 From f20622749fb1c93057de4f5851f95fc76233a303 Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Wed, 29 Jan 2020 16:24:31 +0000 Subject: [PATCH 80/88] Add ability to use multiplayer commands for host and their refers --- constants/fokabotCommands.py | 33 +++++++++++++++++++++++++++++++++ objects/fokabot.py | 14 +++++++++++--- objects/match.py | 13 +++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 6eda3f1..6a08834 100755 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -828,6 +828,36 @@ def getSpectatorHostUserIDFromChannel(chan): return userID def multiplayer(fro, chan, message): + def mpListRefer(): + _match = glob.matches.matches[getMatchIDFromChannel(chan)] + return str(_match.refers) + + def mpAddRefer(): + if len(message) < 2: + raise exceptions.invalidArgumentsException("Wrong syntax: !mp addref <user>") + _match = glob.matches.matches[getMatchIDFromChannel(chan)] + username = message[1].strip() + if not username: + raise exceptions.invalidArgumentsException("Please provide a username") + userID = userUtils.getIDSafe(username) + if userID is None: + raise exceptions.userNotFoundException("No such user") + _match.addRefer(userID) + return "Added {} to refers".format(username) + + def mpRemoveRefer(): + if len(message) < 2: + raise exceptions.invalidArgumentsException("Wrong syntax: !mp rmref <user>") + _match = glob.matches.matches[getMatchIDFromChannel(chan)] + username = message[1].strip() + if not username: + raise exceptions.invalidArgumentsException("Please provide a username") + userID = userUtils.getIDSafe(username) + if userID is None: + raise exceptions.userNotFoundException("No such user") + _match.removeRefer(userID) + return "Removed {} from refers".format(username) + def mpMake(): if len(message) < 2: raise exceptions.invalidArgumentsException("Wrong syntax: !mp make <name>") @@ -1153,6 +1183,9 @@ def multiplayer(fro, chan, message): try: subcommands = { + "listref": mpListRefer, + "addref": mpAddRefer, + "rmref": mpRemoveRefer, "make": mpMake, "close": mpClose, "join": mpJoin, diff --git a/objects/fokabot.py b/objects/fokabot.py index 3ab179d..278577b 100755 --- a/objects/fokabot.py +++ b/objects/fokabot.py @@ -54,10 +54,18 @@ def fokabotResponse(fro, chan, message): # message has triggered a command # Make sure the user has right permissions + _userId = userUtils.getID(fro) if i["privileges"] is not None: - # Rank = x - if userUtils.getPrivileges(userUtils.getID(fro)) & i["privileges"] == 0: - return False + if userUtils.getPrivileges(_userId) & i["privileges"] == 0: + if i["trigger"] == "!mp": + try: + refers = glob.matches.matches[fokabotCommands.getMatchIDFromChannel(chan)].refers + if not _userId in refers: + return False + except: + return False + else: + return False # Check argument number message = message.split(" ") diff --git a/objects/match.py b/objects/match.py index c0bed58..63d75d8 100755 --- a/objects/match.py +++ b/objects/match.py @@ -82,6 +82,19 @@ class match: glob.channels.addHiddenChannel("#multi_{}".format(self.matchID)) log.info("MPROOM{}: {} match created!".format(self.matchID, "Tourney" if self.isTourney else "Normal")) + # Create referrs array that couls use !mp command from the bot. + self.refers = [hostUserID] + + def addRefer(self, referUserId): + self.refers.append(referUserId) + + def removeRefer(self, referUserId): + if self.userRefersToMatch(referUserId): + self.refers.remove(referUserId) + + def userRefersToMatch(self, referUserId): + return referUserId in self.refers + def getMatchData(self, censored = False): """ Return binary match data structure for packetHelper -- 2.20.1 From 733d1eb5da944537b311a732ec4641e1ad36d1ea Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 11 Feb 2020 19:37:14 +0000 Subject: [PATCH 81/88] Improved Commands --- constants/fokabotCommands.py | 181 +++++++++++++++++++++++------------ helpers/aobaHelper.py | 133 ++++++++++++++++++++++++- helpers/configHelper.py | 4 + 3 files changed, 254 insertions(+), 64 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 6a08834..8bdc7f9 100755 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -14,6 +14,7 @@ from constants import exceptions, slotStatuses, matchModModes, matchTeams, match from common.constants import gameModes from common.constants import privileges from constants import serverPackets +from helpers import aobaHelper from helpers import systemHelper from objects import fokabot from objects import glob @@ -688,6 +689,22 @@ def tillerinoLast(fro, chan, message): log.error(a) return False + +def getBeatmapRequest(fro, chan, message): # Grab a random beatmap request. TODO: Add gamemode handling to this and !request + + request = glob.db.fetch("SELECT * FROM rank_requests LIMIT 1;") + if request is not None: + username = userUtils.getUsername(request['userid']) + mapData = glob.db.fetch("SELECT song_name, ranked FROM beatmaps WHERE beatmap_id = {} ORDER BY difficulty_std DESC LIMIT 1;".format(request['bid'])) + glob.db.execute("DELETE FROM rank_requests WHERE id = {};".format(request['id'])) + return "[https://ainu.pw/u/{userID} {username}] nominated beatmap: [https://osu.ppy.sh/b/{beatmapID} {songName}] for status change. {AinuBeatmapLink}The request has been deleted, so please decide it's status.".format(userID=request['userid'], username=username, beatmapID=request['bid'], songName=mapData['song_name'], AinuBeatmapLink='[https://ainu.pw/b/{} Ainu beatmap Link]. '.format(request['bid'])) + else: + return "All nominations have been checked. Thank you for your hard work! :)" + + return "The beatmap ranking system has been reworked." + + + def mm00(fro, chan, message): random.seed() return random.choice(["meme", "MA MAURO ESISTE?"]) @@ -1255,86 +1272,124 @@ def rtx(fro, chan, message): userToken.enqueue(serverPackets.rtx(message)) return ":ok_hand:" -def editMap(fro, chan, message): # Edit maps ranking status ingame. // Added by cmyui and edited by Aoba - messages = [m.lower() for m in message] +def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit + # Put the gathered values into variables to be used later + messages = [m.lower() for m in message] #!map rank set [something] rankType = message[0] mapType = message[1] mapID = message[2] - - # Get persons username & ID + gameMode = message[3] + + # Get persons userID, privileges, and token userID = userUtils.getID(fro) + privileges = userUtils.getPrivileges(userID) + token = glob.tokens.getTokenFromUserID(userID) name = userUtils.getUsername(userID) - - # What do I do here? - if rankType == 'rank': - rankTypeID = 2 - freezeStatus = 1 - elif rankType == 'unrank': - rankTypeID = 0 - freezeStatus = 0 - + + # Only allow users to request maps in #admin channel or PMs with AC. Heavily reduced spam smh + if chan.startswith('#') and chan != '#admin' and not privileges & 8388608: + return "Map ranking is not permitted in regular channels, please do so in PMs with Mirai (or #admin if administrator)." + # Grab beatmapData from db try: - beatmapData = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) + beatmapData = glob.db.fetch("SELECT beatmapset_id, song_name, ranked FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(mapID)) except: return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly." - - if mapType == 'set': - glob.db.execute( - "UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format( - rankTypeID, freezeStatus, beatmapData["beatmapset_id"])) - if freezeStatus == 1: - glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps - WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format( - beatmapData["beatmapset_id"])) - typeBM = 'set' - elif mapType == 'map': - glob.db.execute( - "UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format( - rankTypeID, freezeStatus, mapID)) - if freezeStatus == 1: - glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps - WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format( - beatmapData["beatmap_id"])) - typeBM = 'beatmap' + + # Handle gameMode + if 's' in gameMode.lower() or ('o' in gameMode.lower() and not 'm' in gameMode.lower() and not 'c' in gameMode.lower() and not 't' in gameMode.lower()): + gameMode = "osu!" + elif 'c' in gameMode.lower(): + gameMode = "osu!catch" + elif 'm' in gameMode.lower(): + gameMode = "osu!mania" + elif 't' in gameMode.lower(): + gameMode = "osu!taiko" else: - return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'" - - # Announce that YOOOOOOO THIS MAP IS RANKED!!! - if rankType == "rank": - log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), - True) - if mapType == 'set': - msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, - beatmapData["beatmapset_id"], - beatmapData["song_name"]) - else: - msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, - beatmapData["song_name"]) - glob.db.execute( - "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( - beatmapData["beatmap_id"])) + return "Please enter a valid gamemode (std, ctb, taiko, mania)." + + if 's' in mapType.lower(): + mapType = 'set' + elif 'd' in mapType.lower() or 'm' in mapType.lower(): + mapType = 'map' else: - log.rap(userID, "has {}ed beatmap ({}): {} ({}).".format(rankType, mapType, beatmapData["song_name"], mapID), - True) + return "Please specify whether your request is a single difficulty, or a full set (map/set). Example: '!map unrank/rank/love set/map 256123 mania'." + + # User has AdminManageBeatmaps perm + if privileges & 256: + + # Figure out which ranked status we're requesting to + if 'r' in rankType.lower() and 'u' not in rankType.lower(): + rankType = 'rank' + rankTypeID = 2 + freezeStatus = 1 + elif 'l' in rankType.lower(): + rankType = 'love' + rankTypeID = 5 + freezeStatus = 2 + elif 'u' in rankType.lower() or 'g' in rankType.lower(): + rankType = 'unrank' + rankTypeID = 0 + freezeStatus = 0 + else: + return "Please enter a valid ranked status (rank, love, unrank)." + + if beatmapData['ranked'] == rankTypeID: + return "This map is already {}ed".format(rankType) + + if mapType == 'set': - msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rankType, - beatmapData["beatmapset_id"], - beatmapData["song_name"]) + numDiffs = glob.db.fetch("SELECT COUNT(id) FROM beatmaps WHERE beatmapset_id = {}".format(beatmapData["beatmapset_id"])) + glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmapset_id = {} LIMIT {}".format(rankTypeID, freezeStatus, userID, beatmapData["beatmapset_id"], numDiffs["COUNT(id)"])) else: - msg = "{} has {}ed beatmap: [https://osu.ppy.sh/b/{} {}]".format(name, rankType, mapID, - beatmapData["song_name"]) + glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, userID, mapID )) - glob.db.execute( - "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( - beatmapData["beatmap_id"])) - - chat.sendMessage(glob.BOT_NAME, "#ranked", msg) - return msg + # Announce / Log to admin panel logs when ranked status is changed + log.rap(userID, "has {}ed beatmap ({}): {} ({}), on gamemode {}.".format(rankType, mapType, beatmapData["song_name"], mapID, gameMode), True) + if mapType.lower() == 'set': + msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"], gameMode) + else: + msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, mapID, beatmapData["song_name"], gameMode) + + chat.sendMessage(glob.BOT_NAME, "#announce", msg) + if rankType == "love": + status = "Loved" + elif rankType == "rank": + status = "Ranked" + else: + status = "Unranked" + + if rankType == "love": + if mapType == "set": + webhookdesp = "{} (set) has been loved by {}".format(beatmapData["song_name"], name) + else: + webhookdesp = "{} has been loved by {}".format(beatmapData["song_name"], name) + else: + if mapType == "set": + webhookdesp = "{} (set) has been {}ed by {}".format(beatmapData["song_name"], rankType, name) + else: + webhookdesp = "{} has been {}ed by {}".format(beatmapData["song_name"], rankType, name) + + webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was ranked on osu!Ainu") + webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID))) + webhook.set_title(title="New {} Map!".format(status), url='https://osu.ppy.sh/s/{}'.format(str(beatmapData["beatmapset_id"]))) + webhook.set_desc(webhookdesp) + webhook.set_image("https://assets.ppy.sh/beatmaps/{}/covers/cover.jpg".format(str(beatmapData["beatmapset_id"]))) + webhook.post() + return msg def postAnnouncement(fro, chan, message): # Post to #announce ingame announcement = ' '.join(message[0:]) chat.sendMessage(glob.BOT_NAME, "#announce", announcement) + userID = userUtils.getID(fro) + name = userUtils.getUsername(userID) + + webhook = aobaHelper.Webhook(glob.conf.config["discord"]["announcement"], color=0xadd8e6, footer="This announcement was posted in-game") + webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID))) + webhook.set_title(title="=-= ANNOUNCEMENT =-=") + webhook.set_desc(announcement) + webhook.post() + return "Announcement successfully sent." def usePPBoard(fro, chan, message): @@ -1384,7 +1439,7 @@ def whitelistUserPPLimit(fro, chan, message): userUtils.whitelistUserPPLimit(userID, rx) return "{user} has been whitelisted from autorestrictions on {rx}.".format(user=target, rx='relax' if rx else 'vanilla') - + def bloodcat(fro, chan, message): try: matchID = getMatchIDFromChannel(chan) diff --git a/helpers/aobaHelper.py b/helpers/aobaHelper.py index c333425..296044a 100755 --- a/helpers/aobaHelper.py +++ b/helpers/aobaHelper.py @@ -1,3 +1,9 @@ +import json +import requests +import datetime + +from collections import defaultdict + from objects import glob def getOsuVer(userID): @@ -7,4 +13,129 @@ def getOsuVer(userID): :param userID: user id :return: osu! version """ - return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"] \ No newline at end of file + return glob.db.fetch("SELECT osuver FROM users WHERE id = %s LIMIT 1", [userID])["osuver"] + +class Webhook: + def __init__(self, url, **kwargs): + + """ + Initialise a Webhook Embed Object + Thanks to osu!fx's secret + Commit ID: 77a9136422ceaa792452556c5218a050207aa76a + """ + + self.url = url + self.msg = kwargs.get('msg') + self.color = kwargs.get('color') + self.title = kwargs.get('title') + self.title_url = kwargs.get('title_url') + self.author = kwargs.get('author') + self.author_icon = kwargs.get('author_icon') + self.author_url = kwargs.get('author_url') + self.desc = kwargs.get('desc') + self.fields = kwargs.get('fields', []) + self.image = kwargs.get('image') + self.thumbnail = kwargs.get('thumbnail') + self.footer = kwargs.get('footer') + self.footer_icon = kwargs.get('footer_icon') + self.ts = kwargs.get('ts') + + + def add_field(self,**kwargs): + '''Adds a field to `self.fields`''' + name = kwargs.get('name') + value = kwargs.get('value') + inline = kwargs.get('inline', True) + + field = { + + 'name' : name, + 'value' : value, + 'inline' : inline + + } + + self.fields.append(field) + + def set_desc(self,desc): + self.desc = desc + + def set_author(self, **kwargs): + self.author = kwargs.get('name') + self.author_icon = kwargs.get('icon') + self.author_url = kwargs.get('url') + + def set_title(self, **kwargs): + self.title = kwargs.get('title') + self.title_url = kwargs.get('url') + + def set_thumbnail(self, url): + self.thumbnail = url + + def set_image(self, url): + self.image = url + + def set_footer(self,**kwargs): + self.footer = kwargs.get('text') + self.footer_icon = kwargs.get('icon') + ts = kwargs.get('ts') + if ts == True: + self.ts = str(datetime.datetime.utcfromtimestamp(time.time())) + else: + self.ts = str(datetime.datetime.utcfromtimestamp(ts)) + + + def del_field(self, index): + self.fields.pop(index) + + @property + def json(self,*arg): + ''' + Formats the data into a payload + ''' + + data = {} + + data["embeds"] = [] + embed = defaultdict(dict) + if self.msg: data["content"] = self.msg + if self.author: embed["author"]["name"] = self.author + if self.author_icon: embed["author"]["icon_url"] = self.author_icon + if self.author_url: embed["author"]["url"] = self.author_url + if self.color: embed["color"] = self.color + if self.desc: embed["description"] = self.desc + if self.title: embed["title"] = self.title + if self.title_url: embed["url"] = self.title_url + if self.image: embed["image"]['url'] = self.image + if self.thumbnail: embed["thumbnail"]['url'] = self.thumbnail + if self.footer: embed["footer"]['text'] = self.footer + if self.footer_icon: embed['footer']['icon_url'] = self.footer_icon + if self.ts: embed["timestamp"] = self.ts + + if self.fields: + embed["fields"] = [] + for field in self.fields: + f = {} + f["name"] = field['name'] + f["value"] = field['value'] + f["inline"] = field['inline'] + embed["fields"].append(f) + + data["embeds"].append(dict(embed)) + + empty = all(not d for d in data["embeds"]) + + if empty and 'content' not in data: + print('You cant post an empty payload.') + if empty: data['embeds'] = [] + + return json.dumps(data, indent=4) + + def post(self): + """ + Send the JSON formated object to the specified `self.url`. + """ + + headers = {'Content-Type': 'application/json'} + + requests.post(self.url, data=self.json, headers=headers) diff --git a/helpers/configHelper.py b/helpers/configHelper.py index 5501000..81821b3 100755 --- a/helpers/configHelper.py +++ b/helpers/configHelper.py @@ -75,6 +75,8 @@ class config: parsedConfig.get("discord", "enable") parsedConfig.get("discord", "boturl") parsedConfig.get("discord", "devgroup") + parsedConfig.get("discord", "ranked") + parsedConfig.get("discord", "announcement") parsedConfig.get("datadog", "enable") parsedConfig.get("datadog", "apikey") @@ -143,6 +145,8 @@ class config: self.config.set("discord", "enable", "0") self.config.set("discord", "boturl", "") self.config.set("discord", "devgroup", "") + self.config.set("discord", "ranked", "") + self.config.set("discord", "announcement", "") self.config.add_section("datadog") self.config.set("datadog", "enable", "0") -- 2.20.1 From bae1dabb5a73eeaa652221a31dc6cafd7a773396 Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 11 Feb 2020 19:47:27 +0000 Subject: [PATCH 82/88] Removed gamemode from editMap func --- constants/fokabotCommands.py | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 8bdc7f9..e29ff1c 100755 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1278,7 +1278,6 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit rankType = message[0] mapType = message[1] mapID = message[2] - gameMode = message[3] # Get persons userID, privileges, and token userID = userUtils.getID(fro) @@ -1296,18 +1295,6 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit except: return "We could not find that beatmap. Perhaps check you are using the BeatmapID (not BeatmapSetID), and typed it correctly." - # Handle gameMode - if 's' in gameMode.lower() or ('o' in gameMode.lower() and not 'm' in gameMode.lower() and not 'c' in gameMode.lower() and not 't' in gameMode.lower()): - gameMode = "osu!" - elif 'c' in gameMode.lower(): - gameMode = "osu!catch" - elif 'm' in gameMode.lower(): - gameMode = "osu!mania" - elif 't' in gameMode.lower(): - gameMode = "osu!taiko" - else: - return "Please enter a valid gamemode (std, ctb, taiko, mania)." - if 's' in mapType.lower(): mapType = 'set' elif 'd' in mapType.lower() or 'm' in mapType.lower(): @@ -1345,19 +1332,19 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, userID, mapID )) # Announce / Log to admin panel logs when ranked status is changed - log.rap(userID, "has {}ed beatmap ({}): {} ({}), on gamemode {}.".format(rankType, mapType, beatmapData["song_name"], mapID, gameMode), True) + log.rap(userID, "has {}ed beatmap ({}): {} ({})".format(rankType, mapType, beatmapData["song_name"], mapID), True) if mapType.lower() == 'set': - msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"], gameMode) + msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"]) else: - msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}] on gamemode {}".format(fro, rankType, mapID, beatmapData["song_name"], gameMode) + msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, mapID, beatmapData["song_name"]) chat.sendMessage(glob.BOT_NAME, "#announce", msg) if rankType == "love": - status = "Loved" + status = "loved" elif rankType == "rank": - status = "Ranked" + status = "ranked" else: - status = "Unranked" + status = "unranked" if rankType == "love": if mapType == "set": @@ -1370,9 +1357,9 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit else: webhookdesp = "{} has been {}ed by {}".format(beatmapData["song_name"], rankType, name) - webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was ranked on osu!Ainu") + webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was {} on osu!Ainu".format(status)) webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID))) - webhook.set_title(title="New {} Map!".format(status), url='https://osu.ppy.sh/s/{}'.format(str(beatmapData["beatmapset_id"]))) + webhook.set_title(title="New {} map!".format(status), url='https://osu.ppy.sh/s/{}'.format(str(beatmapData["beatmapset_id"]))) webhook.set_desc(webhookdesp) webhook.set_image("https://assets.ppy.sh/beatmaps/{}/covers/cover.jpg".format(str(beatmapData["beatmapset_id"]))) webhook.post() -- 2.20.1 From 721e599a44fb1fe667fd5c9cfe49a9254c7fa5fc Mon Sep 17 00:00:00 2001 From: Ainu Server <neko.aoba.suzukaze@gmail.com> Date: Tue, 11 Feb 2020 19:51:46 +0000 Subject: [PATCH 83/88] loveed --- constants/fokabotCommands.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index e29ff1c..f5fa090 100755 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1346,16 +1346,10 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit else: status = "unranked" - if rankType == "love": - if mapType == "set": - webhookdesp = "{} (set) has been loved by {}".format(beatmapData["song_name"], name) - else: - webhookdesp = "{} has been loved by {}".format(beatmapData["song_name"], name) + if mapType == "set": + webhookdesp = "{} (set) has been {} by {}".format(beatmapData["song_name"], status, name) else: - if mapType == "set": - webhookdesp = "{} (set) has been {}ed by {}".format(beatmapData["song_name"], rankType, name) - else: - webhookdesp = "{} has been {}ed by {}".format(beatmapData["song_name"], rankType, name) + webhookdesp = "{} has been {} by {}".format(beatmapData["song_name"], status, name) webhook = aobaHelper.Webhook(glob.conf.config["discord"]["ranked"], color=0xadd8e6, footer="This beatmap was {} on osu!Ainu".format(status)) webhook.set_author(name=name, icon='https://a.ainu.pw/{}'.format(str(userID)), url="https://ainu.pw/u/{}".format(str(userID))) -- 2.20.1 From fa7bcddea461e7133e8c15ff4a0c397f9fc9877c Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Wed, 12 Feb 2020 08:20:50 +0700 Subject: [PATCH 84/88] Whoops Atoka --- constants/fokabotCommands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index f5fa090..e69b64b 100755 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1287,7 +1287,7 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit # Only allow users to request maps in #admin channel or PMs with AC. Heavily reduced spam smh if chan.startswith('#') and chan != '#admin' and not privileges & 8388608: - return "Map ranking is not permitted in regular channels, please do so in PMs with Mirai (or #admin if administrator)." + return "Map ranking is not permitted in regular channels, please do so in PMs with AC (or #admin if administrator)." # Grab beatmapData from db try: -- 2.20.1 From 00e46c01fe39eaec326eed42e4f2f18eea3a80e0 Mon Sep 17 00:00:00 2001 From: Hazuki Onohara <neko.aoba.suzukaze@gmail.com> Date: Fri, 14 Feb 2020 21:17:03 +0700 Subject: [PATCH 85/88] loveed... again!? --- constants/fokabotCommands.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index e69b64b..c45e5da 100755 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -1320,10 +1320,16 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit freezeStatus = 0 else: return "Please enter a valid ranked status (rank, love, unrank)." - + + if rankType == "love": + status = "loved" + elif rankType == "rank": + status = "ranked" + else: + status = "unranked" + if beatmapData['ranked'] == rankTypeID: - return "This map is already {}ed".format(rankType) - + return "This map is already {}".format(status) if mapType == 'set': numDiffs = glob.db.fetch("SELECT COUNT(id) FROM beatmaps WHERE beatmapset_id = {}".format(beatmapData["beatmapset_id"])) @@ -1332,20 +1338,14 @@ def editMap(fro, chan, message): # Using Atoka's editMap with Aoba's edit glob.db.execute("UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {}, rankedby = {} WHERE beatmap_id = {} LIMIT 1".format(rankTypeID, freezeStatus, userID, mapID )) # Announce / Log to admin panel logs when ranked status is changed - log.rap(userID, "has {}ed beatmap ({}): {} ({})".format(rankType, mapType, beatmapData["song_name"], mapID), True) + log.rap(userID, "has {} beatmap ({}): {} ({})".format(status, mapType, beatmapData["song_name"], mapID), True) if mapType.lower() == 'set': - msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, beatmapData["beatmapset_id"], beatmapData["song_name"]) + msg = "{} has {} beatmap set: [https://osu.ppy.sh/s/{} {}]".format(fro, status, beatmapData["beatmapset_id"], beatmapData["song_name"]) else: - msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(fro, rankType, mapID, beatmapData["song_name"]) + msg = "{} has {} beatmap: [https://osu.ppy.sh/s/{} {}]".format(fro, status, mapID, beatmapData["song_name"]) chat.sendMessage(glob.BOT_NAME, "#announce", msg) - if rankType == "love": - status = "loved" - elif rankType == "rank": - status = "ranked" - else: - status = "unranked" - + if mapType == "set": webhookdesp = "{} (set) has been {} by {}".format(beatmapData["song_name"], status, name) else: -- 2.20.1 From 3063dfb6e6910750b22ca06cfc48118f224d1b20 Mon Sep 17 00:00:00 2001 From: Chitsanupong C <lumilous@noreply.example.org> Date: Sat, 7 Mar 2020 14:27:23 +0000 Subject: [PATCH 86/88] Update 'README.md' --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) mode change 100755 => 100644 README.md diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 9a03d58..8c5685d --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ +# Old Ripple bAncho? + ## pep.py -This is Ripple's bancho server but osu!thailand forked edition. It handles: +This is Ripple's bancho server but osu!lumilous forked edition. It handles: - Client login - Online users listing and statuses - Public and private chat -- 2.20.1 From 0d3b8f2ec57cad747bdbf3d496d7de5f4a333959 Mon Sep 17 00:00:00 2001 From: Chitsanupong C <lumilous@noreply.example.org> Date: Sun, 8 Mar 2020 08:21:28 +0000 Subject: [PATCH 87/88] Delete '.gitignore' --- .gitignore | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100755 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100755 index 808cd02..0000000 --- a/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -**/__pycache__ -**/build -config.ini -filters.txt -.data -.idea -redistest.py -*.c -*.so -.pyenv \ No newline at end of file -- 2.20.1 From 646a2b2378815a6554d9907b67a9509b33c5bae1 Mon Sep 17 00:00:00 2001 From: Chitsanupong C <lumilous@noreply.example.org> Date: Sun, 8 Mar 2020 08:21:42 +0000 Subject: [PATCH 88/88] Delete '.gitmodules' --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) delete mode 100755 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100755 index a4ead52..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "common"] - path = common - url = https://github.com/osuthailand/common.git -- 2.20.1 ```
Elnzburn (Migrated from zxq.co) closed this issue 2020-11-26 16:54:03 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ripple/pep.py#28
No description provided.