653303831b
BATs with Donor have bright yellow username in chat General performance improvements Code cleaning Multiplayer improvements and fixes Fixed some spectator bugs
340 lines
11 KiB
Python
340 lines
11 KiB
Python
from objects import glob
|
|
from helpers import logHelper as log
|
|
from constants import exceptions
|
|
from constants import serverPackets
|
|
from objects import fokabot
|
|
from helpers import discordBotHelper
|
|
from helpers import userHelper
|
|
from events import logoutEvent
|
|
from constants import messageTemplates
|
|
|
|
def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
|
"""
|
|
Join a channel
|
|
|
|
userID -- user ID of the user that joins the channel. Optional.
|
|
token can be used instead.
|
|
token -- user token object of user that joins the channel. Optional.
|
|
userID can be used instead.
|
|
channel -- name of channe
|
|
toIRC -- if True, send this channel join event to IRC. Must be true if joining from bancho.
|
|
Optional. Defaukt: True
|
|
return -- returns 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
|
"""
|
|
try:
|
|
# Get token if not defined
|
|
if token == None:
|
|
token = glob.tokens.getTokenFromUserID(userID)
|
|
# Make sure the token exists
|
|
if token == None:
|
|
raise exceptions.userNotFoundException
|
|
else:
|
|
token = token
|
|
userID = token.userID
|
|
|
|
# Get usertoken data
|
|
username = token.username
|
|
|
|
# Normal channel, do check stuff
|
|
# Make sure the channel exists
|
|
if channel not in glob.channels.channels:
|
|
raise exceptions.channelUnknownException
|
|
|
|
# Check channel permissions
|
|
channelObject = glob.channels.channels[channel]
|
|
if channelObject.publicRead == False and token.admin == False:
|
|
raise exceptions.channelNoPermissionsException
|
|
|
|
# Add our userID to users in that channel
|
|
channelObject.userJoin(userID)
|
|
|
|
# Add the channel to our joined channel
|
|
token.joinChannel(channel)
|
|
|
|
# Send channel joined (bancho). We use clientName here because of #multiplayer and #spectator channels
|
|
token.enqueue(serverPackets.channelJoinSuccess(userID, channelObject.clientName))
|
|
|
|
# Send channel joined (IRC)
|
|
if glob.irc == True and toIRC == True:
|
|
glob.ircServer.banchoJoinChannel(username, channel)
|
|
|
|
# Console output
|
|
log.info("{} joined channel {}".format(username, channel))
|
|
|
|
# IRC code return
|
|
return 0
|
|
except exceptions.channelNoPermissionsException:
|
|
log.warning("{} attempted to join channel {}, but they have no read permissions".format(username, channel))
|
|
return 403
|
|
except exceptions.channelUnknownException:
|
|
log.warning("{} attempted to join an unknown channel ({})".format(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):
|
|
"""
|
|
Part a channel
|
|
|
|
userID -- user ID of the user that parts the channel. Optional.
|
|
token can be used instead.
|
|
token -- user token object of user that parts the channel. Optional.
|
|
userID can be used instead.
|
|
channel -- name of channel
|
|
toIRC -- if True, send this channel join event to IRC. Must be true if joining from bancho.
|
|
Optional. Defaukt: True
|
|
kick -- if True, channel tab will be closed on client. Used when leaving lobby. Optional. Default: False
|
|
return -- returns 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
|
"""
|
|
try:
|
|
# Get token if not defined
|
|
if token == None:
|
|
token = glob.tokens.getTokenFromUserID(userID)
|
|
# Make sure the token exists
|
|
if token == None:
|
|
raise exceptions.userNotFoundException
|
|
else:
|
|
token = token
|
|
userID = token.userID
|
|
|
|
# Get usertoken data
|
|
username = token.username
|
|
|
|
# Determine internal/client name if needed
|
|
# (toclient is used clientwise for #multiplayer and #spectator channels)
|
|
channelClient = channel
|
|
if channel == "#spectator":
|
|
if token.spectating == 0:
|
|
s = userID
|
|
else:
|
|
s = token.spectating
|
|
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
|
|
|
|
# Part channel (token-side and channel-side)
|
|
channelObject = glob.channels.channels[channel]
|
|
token.partChannel(channel)
|
|
channelObject.userPart(userID)
|
|
|
|
# Force close tab if needed
|
|
# NOTE: Maybe always needed, will check later
|
|
if kick == True:
|
|
token.enqueue(serverPackets.channelKicked(channelClient))
|
|
|
|
# IRC part
|
|
if glob.irc == True and toIRC == True:
|
|
glob.ircServer.banchoPartChannel(username, channel)
|
|
|
|
# Console output
|
|
log.info("{} parted channel {} ({})".format(username, channel, channelClient))
|
|
|
|
# Return IRC code
|
|
return 0
|
|
except exceptions.channelUnknownException:
|
|
log.warning("{} attempted to part an unknown channel ({})".format(username, channel))
|
|
return 403
|
|
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
|
|
|
|
fro -- sender username. Optional.
|
|
You can use token instead of this if you wish.
|
|
to -- receiver channel (if starts with #) or username
|
|
message -- text of the message
|
|
token -- sender token object.
|
|
You can use this instead of fro if you are sending messages from bancho.
|
|
Optional.
|
|
toIRC -- if True, send the message to IRC. If False, send it to Bancho only.
|
|
Optional. Default: True
|
|
"""
|
|
try:
|
|
tokenString = ""
|
|
# Get token object if not passed
|
|
if token == None:
|
|
token = glob.tokens.getTokenFromUsername(fro)
|
|
if token == None:
|
|
raise exceptions.userNotFoundException
|
|
else:
|
|
# token object alredy passed, get its string and its username (fro)
|
|
fro = token.username
|
|
tokenString = token.token
|
|
|
|
# Set some variables
|
|
userID = token.userID
|
|
username = token.username
|
|
recipients = []
|
|
|
|
# Make sure the user is not in restricted mode
|
|
if token.restricted == True:
|
|
raise exceptions.userRestrictedException
|
|
|
|
# Make sure the user is not silenced
|
|
if token.isSilenced() == True:
|
|
raise exceptions.userSilencedException
|
|
|
|
# Determine internal name if needed
|
|
# (toclient is used clientwise for #multiplayer and #spectator channels)
|
|
toClient = to
|
|
if to == "#spectator":
|
|
if token.spectating == 0:
|
|
s = userID
|
|
else:
|
|
s = token.spectating
|
|
to = "#spect_{}".format(s)
|
|
elif to == "#multiplayer":
|
|
to = "#multi_{}".format(token.matchID)
|
|
elif to.startswith("#spect_"):
|
|
toClient = "#spectator"
|
|
elif to.startswith("#multi_"):
|
|
toClient = "#multiplayer"
|
|
|
|
# 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(username, toClient, message)
|
|
|
|
# Send the message
|
|
isChannel = to.startswith("#")
|
|
if isChannel == True:
|
|
# 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 == True and token.admin == False:
|
|
raise exceptions.channelModeratedException
|
|
|
|
# Make sure we have write permissions
|
|
if glob.channels.channels[to].publicWrite == False and token.admin == False:
|
|
raise exceptions.channelNoPermissionsException
|
|
|
|
# Everything seems fine, build recipients list and send packet
|
|
recipients = glob.channels.channels[to].getConnectedUsers()[:]
|
|
for key, value in glob.tokens.tokens.items():
|
|
# Skip our client and irc clients
|
|
if key == tokenString or value.irc == True:
|
|
continue
|
|
# Send to this client if it's connected to the channel
|
|
if value.userID in recipients:
|
|
value.enqueue(packet)
|
|
else:
|
|
# USER
|
|
# Make sure recipient user is connected
|
|
recipientToken = glob.tokens.getTokenFromUsername(to)
|
|
if recipientToken == None:
|
|
raise exceptions.userNotFoundException
|
|
|
|
# Make sure the recipient is not restricted or we are FokaBot
|
|
if recipientToken.restricted == True and fro.lower() != "fokabot":
|
|
raise exceptions.userRestrictedException
|
|
|
|
# TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend
|
|
|
|
# Check message templates (mods/admins only)
|
|
if message in messageTemplates.templates and token.admin == True:
|
|
sendMessage(fro, to, messageTemplates.templates[message])
|
|
|
|
# Everything seems fine, send packet
|
|
recipientToken.enqueue(packet)
|
|
|
|
# Send the message to IRC
|
|
if glob.irc == True and toIRC == True:
|
|
glob.ircServer.banchoMessage(fro, to, message)
|
|
|
|
# Spam protection (ignore FokaBot)
|
|
if userID > 999:
|
|
token.spamProtection()
|
|
|
|
# Fokabot message
|
|
if isChannel == True or to.lower() == "fokabot":
|
|
fokaMessage = fokabot.fokabotResponse(username, to, message)
|
|
if fokaMessage != False:
|
|
sendMessage("FokaBot", to if isChannel else fro, fokaMessage)
|
|
|
|
# File and discord logs (public chat only)
|
|
if to.startswith("#") == True:
|
|
log.chat("{fro} @ {to}: {message}".format(fro=username, to=to, message=str(message.encode("utf-8"))))
|
|
discordBotHelper.sendChatlog("**{fro} @ {to}:** {message}".format(fro=username, to=to, message=str(message.encode("utf-8"))[2:-1]))
|
|
return 0
|
|
except exceptions.userSilencedException:
|
|
token.enqueue(serverPackets.silenceEndTime(token.getSilenceSecondsLeft()))
|
|
log.warning("{} tried to send a message during silence".format(username))
|
|
return 404
|
|
except exceptions.channelModeratedException:
|
|
log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(username, to))
|
|
return 404
|
|
except exceptions.channelUnknownException:
|
|
log.warning("{} tried to send a message to an unknown channel ({})".format(username, to))
|
|
return 403
|
|
except exceptions.channelNoPermissionsException:
|
|
log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(username, to))
|
|
return 404
|
|
except exceptions.userRestrictedException:
|
|
log.warning("{} tried to send a message {}, but the recipient is in restricted mode".format(username, to))
|
|
return 404
|
|
except exceptions.userNotFoundException:
|
|
log.warning("User not connected to IRC/Bancho")
|
|
return 401
|
|
|
|
|
|
""" IRC-Bancho Connect/Disconnect/Join/Part interfaces"""
|
|
def fixUsernameForBancho(username):
|
|
return username.replace("_", " ")
|
|
|
|
def fixUsernameForIRC(username):
|
|
return username.replace(" ", "_")
|
|
|
|
def IRCConnect(username):
|
|
userID = userHelper.getID(username)
|
|
if userID == False:
|
|
log.warning("{} doesn't exist".format(username))
|
|
return
|
|
glob.tokens.deleteOldTokens(userID)
|
|
glob.tokens.addToken(userID, irc=True)
|
|
glob.tokens.enqueueAll(serverPackets.userPanel(userID))
|
|
log.info("{} logged in from IRC".format(username))
|
|
|
|
def IRCDisconnect(username):
|
|
token = glob.tokens.getTokenFromUsername(username)
|
|
if token == None:
|
|
log.warning("{} doesn't exist".format(username))
|
|
return
|
|
logoutEvent.handle(token)
|
|
log.info("{} disconnected from IRC".format(username))
|
|
|
|
def IRCJoinChannel(username, channel):
|
|
userID = userHelper.getID(username)
|
|
if userID == False:
|
|
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):
|
|
userID = userHelper.getID(username)
|
|
if userID == False:
|
|
log.warning("{} doesn't exist".format(username))
|
|
return
|
|
return partChannel(userID, channel)
|