From 40264ceffe2efec18cdec19e8513db34d9d6fbaa Mon Sep 17 00:00:00 2001 From: Nyo Date: Sat, 1 Oct 2016 21:19:03 +0200 Subject: [PATCH] .BANCHO. Add streams and streamList object, add 'main' stream --- constants/exceptions.py | 3 ++ constants/fokabotCommands.py | 12 +++---- events/loginEvent.py | 2 +- events/logoutEvent.py | 5 ++- helpers/chatHelper.py | 2 +- helpers/systemHelper.py | 4 +-- irc/ircserver.py | 15 +++++---- objects/fokabot.py | 4 +-- objects/glob.py | 4 +++ objects/osuToken.py | 20 +++++++++++- objects/stream.py | 39 ++++++++++++++++++++++ objects/streamList.py | 63 ++++++++++++++++++++++++++++++++++++ objects/tokenList.py | 8 +++-- pep.py | 5 +++ 14 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 objects/stream.py create mode 100644 objects/streamList.py diff --git a/constants/exceptions.py b/constants/exceptions.py index 4e769ac..0b41f28 100644 --- a/constants/exceptions.py +++ b/constants/exceptions.py @@ -86,3 +86,6 @@ class forceUpdateException(Exception): class loginLockedException(Exception): pass + +class unknownStreamException(Exception): + pass \ No newline at end of file diff --git a/constants/fokabotCommands.py b/constants/fokabotCommands.py index 97e0f62..fc9b033 100644 --- a/constants/fokabotCommands.py +++ b/constants/fokabotCommands.py @@ -28,7 +28,7 @@ return the message or **False** if there's no response by the bot TODO: Change False to None, because False doesn't make any sense """ def instantRestart(fro, chan, message): - glob.tokens.enqueueAll(serverPackets.notification("We are restarting Bancho. Be right back!")) + glob.streams.broadcast("main", serverPackets.notification("We are restarting Bancho. Be right back!")) systemHelper.scheduleShutdown(0, True, delay=1) return False @@ -69,7 +69,7 @@ def roll(fro, chan, message): # return random.choice(["yes", "no", "maybe"]) def alert(fro, chan, message): - glob.tokens.enqueueAll(serverPackets.notification(' '.join(message[:]))) + glob.streams.broadcast("main", serverPackets.notification(' '.join(message[:]))) return False def alertUser(fro, chan, message): @@ -311,11 +311,11 @@ def systemReload(fro, chan, message): glob.chatFilters.loadFilters() # Send new channels and new bottom icon to everyone - glob.tokens.enqueueAll(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"])) - glob.tokens.enqueueAll(serverPackets.channelInfoEnd()) + glob.streams.broadcast("main", serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"])) + glob.streams.broadcast("main", serverPackets.channelInfoEnd()) for key, value in glob.channels.channels.items(): if value.publicRead == True and value.hidden == False: - glob.tokens.enqueueAll(serverPackets.channelInfo(key)) + glob.streams.broadcast("main", serverPackets.channelInfo(key)) return "Bancho settings reloaded!" @@ -341,7 +341,7 @@ def systemMaintenance(fro, chan, message): if not value.admin: who.append(value.userID) - glob.tokens.enqueueAll(serverPackets.notification("Our bancho server is in maintenance mode. Please try to login again later.")) + glob.streams.broadcast("main", serverPackets.notification("Our bancho server is in maintenance mode. Please try to login again later.")) glob.tokens.multipleEnqueue(serverPackets.loginError(), who) msg = "The server is now in maintenance mode!" else: diff --git a/events/loginEvent.py b/events/loginEvent.py index 64574c6..b4ec689 100644 --- a/events/loginEvent.py +++ b/events/loginEvent.py @@ -205,7 +205,7 @@ def handle(tornadoRequest): # Send to everyone our userpanel if we are not restricted if not responseToken.restricted: - glob.tokens.enqueueAll(serverPackets.userPanel(userID)) + glob.streams.broadcast("main", serverPackets.userPanel(userID)) # Set reponse data to right value and reset our queue responseData = responseToken.queue diff --git a/events/logoutEvent.py b/events/logoutEvent.py index 362c7be..1a8753b 100644 --- a/events/logoutEvent.py +++ b/events/logoutEvent.py @@ -25,8 +25,11 @@ def handle(userToken, _=None): for i in userToken.joinedChannels: chat.partChannel(token=userToken, channel=i) + # Leave all joined streams + userToken.leaveAllStreams() + # Enqueue our disconnection to everyone else - glob.tokens.enqueueAll(serverPackets.userLogout(userID)) + glob.streams.broadcast("main", serverPackets.userLogout(userID)) # Disconnect from IRC if needed if userToken.irc == True and glob.irc == True: diff --git a/helpers/chatHelper.py b/helpers/chatHelper.py index 2324d94..7d9997d 100644 --- a/helpers/chatHelper.py +++ b/helpers/chatHelper.py @@ -320,7 +320,7 @@ def IRCConnect(username): return glob.tokens.deleteOldTokens(userID) glob.tokens.addToken(userID, irc=True) - glob.tokens.enqueueAll(serverPackets.userPanel(userID)) + glob.streams.broadcast("main", serverPackets.userPanel(userID)) log.info("{} logged in from IRC".format(username)) def IRCDisconnect(username): diff --git a/helpers/systemHelper.py b/helpers/systemHelper.py index 8c3445e..f570c8a 100644 --- a/helpers/systemHelper.py +++ b/helpers/systemHelper.py @@ -42,10 +42,10 @@ def scheduleShutdown(sendRestartTime, restart, message = "", delay=20): # Send notification if set if message != "": - glob.tokens.enqueueAll(serverPackets.notification(message)) + glob.streams.broadcast("main", serverPackets.notification(message)) # Schedule server restart packet - threading.Timer(sendRestartTime, glob.tokens.enqueueAll, [serverPackets.banchoRestart(delay*2*1000)]).start() + threading.Timer(sendRestartTime, glob.streams.broadcast, ["main", serverPackets.banchoRestart(delay*2*1000)]).start() glob.restarting = True # Restart/shutdown diff --git a/irc/ircserver.py b/irc/ircserver.py index a6fe050..1412ad6 100644 --- a/irc/ircserver.py +++ b/irc/ircserver.py @@ -282,20 +282,21 @@ class Client: return # Make sure we are not connected to Bancho - token = glob.tokens.getTokenFromUsername(chat.fixUsernameForBancho(nick)) + token = glob.tokens.getTokenFromUsername(chat.fixUsernameForBancho(nick), True) if token is not None: self.reply("433 * {} :Nickname is already in use".format(nick)) return - # Make sure we are not already connected from IRC with that name - for _, value in self.server.clients.items(): - if value.IRCUsername == self.IRCUsername and value != self: - self.reply("433 * {} :Nickname is already in use".format(nick)) - return - # Everything seems fine, set username (nickname) self.IRCUsername = nick # username for IRC self.banchoUsername = chat.fixUsernameForBancho(self.IRCUsername) # username for bancho + + # Disconnect other IRC clients from the same user + for _, value in self.server.clients.items(): + if value.IRCUsername.lower() == self.IRCUsername.lower() and value != self: + print("DISCONNECTERINOOOOOOOOOOOOOOOOOOOOO") + value.disconnect(quitmsg="Connected from another client") + return elif command == "USER": # Ignore USER command, we use nickname only return diff --git a/objects/fokabot.py b/objects/fokabot.py index e71fc75..73054ef 100644 --- a/objects/fokabot.py +++ b/objects/fokabot.py @@ -14,8 +14,8 @@ def connect(): """Add FokaBot to connected users and send userpanel/stats packet to everyone""" token = glob.tokens.addToken(999) token.actionID = actions.IDLE - glob.tokens.enqueueAll(serverPackets.userPanel(999)) - glob.tokens.enqueueAll(serverPackets.userStats(999)) + glob.streams.broadcast("main", serverPackets.userPanel(999)) + glob.streams.broadcast("main", serverPackets.userStats(999)) def disconnect(): """Remove FokaBot from connected users""" diff --git a/objects/glob.py b/objects/glob.py index a22b6d7..3bb7abb 100644 --- a/objects/glob.py +++ b/objects/glob.py @@ -5,6 +5,7 @@ from objects import channelList from objects import matchList from objects import fileLocks from objects import fileBuffer +from objects import streamList import time try: @@ -42,3 +43,6 @@ sentry = False irc = False startTime = int(time.time()) + + +streams = streamList.streamList() diff --git a/objects/osuToken.py b/objects/osuToken.py index 5bc7852..ee1607d 100644 --- a/objects/osuToken.py +++ b/objects/osuToken.py @@ -34,6 +34,7 @@ class token: self.pingTime = self.loginTime self.timeOffset = timeOffset self.lock = threading.Lock() # Sync primitive + self.streams = [] # Default variables self.spectators = [] @@ -79,6 +80,9 @@ class token: if ip != "": userHelper.saveBanchoSession(self.userID, self.ip) + # Join main stream + self.joinStream("main") + def enqueue(self, bytes_): """ Add bytes (packets) to queue @@ -277,7 +281,7 @@ class token: self.enqueue(serverPackets.silenceEndTime(seconds)) # Send silenced packet to everyone else - glob.tokens.enqueueAll(serverPackets.userSilenced(self.userID)) + glob.streams.broadcast("main", serverPackets.userSilenced(self.userID)) def spamProtection(self, increaseSpamRate = True): """ @@ -343,3 +347,17 @@ class token: """ self.restricted = True chat.sendMessage("FokaBot",self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.") + + def joinStream(self, name): + glob.streams.join(name, self) + if name not in self.streams: + self.streams.append(name) + + def leaveStream(self, name): + glob.streams.leave(name, self) + if name in self.streams: + self.streams.remove(name) + + def leaveAllStreams(self): + for i in self.streams: + self.leaveStream(i) \ No newline at end of file diff --git a/objects/stream.py b/objects/stream.py new file mode 100644 index 0000000..d1145c5 --- /dev/null +++ b/objects/stream.py @@ -0,0 +1,39 @@ +class stream(): + def __init__(self, name): + """ + Initialize a stream object + + :param name: stream name + """ + self.name = name + self.clients = [] + + def addClient(self, client): + """ + Add a client to this stream if not already in + + :param client: client (osuToken) object + :return: + """ + if client not in self.clients: + self.clients.append(client) + + def removeClient(self, client): + """ + Remove a client from this stream if in + + :param client: client (osuToken) object + :return: + """ + if client in self.clients: + self.clients.remove(client) + + def broadcast(self, data): + """ + Send some data to all clients connected to this stream + + :param data: data to send + :return: + """ + for i in self.clients: + i.enqueue(data) \ No newline at end of file diff --git a/objects/streamList.py b/objects/streamList.py new file mode 100644 index 0000000..3cf5ab7 --- /dev/null +++ b/objects/streamList.py @@ -0,0 +1,63 @@ +from objects import stream + +class streamList(): + def __init__(self): + self.streams = {} + + def add(self, name): + """ + Create a new stream list if it doesn't already exist + + :param name: stream name + :return: + """ + if name not in self.streams: + self.streams[name] = stream.stream(name) + + def remove(self, name): + """ + Removes an existing stream and kick every user in it + + :param name: stream name + :return: + """ + if name in self.streams: + for i in self.streams[name].clients: + i.leaveStream(name) + self.streams.pop(name) + + def broadcast(self, streamName, data): + """ + Send some data to all clients in a stream + + :param streamName: stream name + :param data: data to send + :return: + """ + if streamName not in self.streams: + return + self.streams[streamName].broadcast(data) + + def join(self, streamName, client): + """ + Add a client to a stream + + :param streamName: stream name + :param client: client (osuToken) object + :return: + """ + if streamName not in self.streams: + return + self.streams[streamName].addClient(client) + + def leave(self, streamName, client): + """ + Remove a client from a stream + + :param streamName: stream name + :param client: client (osuToken) object + :return: + """ + if streamName not in self.streams: + return + self.streams[streamName].removeClient(client) \ No newline at end of file diff --git a/objects/tokenList.py b/objects/tokenList.py index 69259ce..45ecaa8 100644 --- a/objects/tokenList.py +++ b/objects/tokenList.py @@ -58,7 +58,7 @@ class tokenList: # Get userID associated to that token return self.tokens[token].userID - def getTokenFromUserID(self, userID): + def getTokenFromUserID(self, userID, ignoreIRC=False): """ Get token from a user ID @@ -68,12 +68,14 @@ class tokenList: # Make sure the token exists for _, value in self.tokens.items(): if value.userID == userID: + if ignoreIRC and value.irc: + continue return value # Return none if not found return None - def getTokenFromUsername(self, username): + def getTokenFromUsername(self, username, ignoreIRC=False): """ Get token from a username @@ -86,6 +88,8 @@ class tokenList: # Make sure the token exists for _, value in self.tokens.items(): if value.username.lower() == who: + if ignoreIRC and value.irc: + continue return value # Return none if not found diff --git a/pep.py b/pep.py index 783d341..47155ba 100644 --- a/pep.py +++ b/pep.py @@ -130,6 +130,11 @@ if __name__ == "__main__": glob.channels.loadChannels() consoleHelper.printDone() + # Initialize stremas + consoleHelper.printNoNl("> Creating main stream... ") + glob.streams.add("main") + consoleHelper.printDone() + # Start fokabot consoleHelper.printNoNl("> Connecting FokaBot... ") fokabot.connect()