.BANCHO. Use streams for public chat

This commit is contained in:
Nyo 2016-12-11 11:07:35 +01:00
parent c4a6c84cec
commit 44545c3bcb
11 changed files with 133 additions and 119 deletions

View File

@ -89,4 +89,10 @@ class unknownStreamException(Exception):
pass pass
class userTournamentException(Exception): class userTournamentException(Exception):
pass
class userAlreadyInChannelException(Exception):
pass
class userNotInChannelException(Exception):
pass pass

View File

@ -656,7 +656,7 @@ def pp(fro, chan, message):
pp = userUtils.getPP(token.userID, gameMode) pp = userUtils.getPP(token.userID, gameMode)
return "You have {:,} pp".format(pp) return "You have {:,} pp".format(pp)
def updateBeatmap(fro, chan, to): def updateBeatmap(fro, chan, message):
try: try:
# Run the command in PM only # Run the command in PM only
if chan.startswith("#"): if chan.startswith("#"):

View File

@ -155,11 +155,13 @@ def channelJoinSuccess(userID, chan):
return packetHelper.buildPacket(packetIDs.server_channelJoinSuccess, [[chan, dataTypes.STRING]]) return packetHelper.buildPacket(packetIDs.server_channelJoinSuccess, [[chan, dataTypes.STRING]])
def channelInfo(chan): def channelInfo(chan):
if chan not in glob.channels.channels:
return bytes()
channel = glob.channels.channels[chan] channel = glob.channels.channels[chan]
return packetHelper.buildPacket(packetIDs.server_channelInfo, [ return packetHelper.buildPacket(packetIDs.server_channelInfo, [
[chan, dataTypes.STRING], [channel.name, dataTypes.STRING],
[channel.description, dataTypes.STRING], [channel.description, dataTypes.STRING],
[len(channel.connectedUsers), dataTypes.UINT16] [len(glob.streams.streams["chat/{}".format(chan)].clients), dataTypes.UINT16]
]) ])
def channelInfoEnd(): def channelInfoEnd():

View File

@ -5,13 +5,13 @@ from objects import glob
def handle(userToken, _): def handle(userToken, _):
# Get usertoken data # Get usertoken data
userID = userToken.userID
username = userToken.username username = userToken.username
# Remove user from users in lobby # Remove user from users in lobby
userToken.leaveStream("lobby") userToken.leaveStream("lobby")
# Part lobby channel # Part lobby channel
# Done automatically by the client
chat.partChannel(channel="#lobby", token=userToken, kick=True) chat.partChannel(channel="#lobby", token=userToken, kick=True)
# Console output # Console output

View File

@ -27,44 +27,32 @@ def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
raise exceptions.userNotFoundException raise exceptions.userNotFoundException
else: else:
token = token token = token
userID = token.userID
# Get usertoken data
username = token.username
# Normal channel, do check stuff # Normal channel, do check stuff
# Make sure the channel exists # Make sure the channel exists
if channel not in glob.channels.channels: if channel not in glob.channels.channels:
raise exceptions.channelUnknownException 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 # Add the channel to our joined channel
token.joinChannel(channel) token.joinChannel(glob.channels.channels[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) # Send channel joined (IRC)
if glob.irc == True and toIRC == True: if glob.irc == True and toIRC == True:
glob.ircServer.banchoJoinChannel(username, channel) glob.ircServer.banchoJoinChannel(token.username, channel)
# Console output # Console output
log.info("{} joined channel {}".format(username, channel)) log.info("{} joined channel {}".format(token.username, channel))
# IRC code return # IRC code return
return 0 return 0
except exceptions.channelNoPermissionsException: except exceptions.channelNoPermissionsException:
log.warning("{} attempted to join channel {}, but they have no read permissions".format(username, channel)) log.warning("{} attempted to join channel {}, but they have no read permissions".format(token.username, channel))
return 403 return 403
except exceptions.channelUnknownException: except exceptions.channelUnknownException:
log.warning("{} attempted to join an unknown channel ({})".format(username, channel)) 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 return 403
except exceptions.userNotFoundException: except exceptions.userNotFoundException:
log.warning("User not connected to IRC/Bancho") log.warning("User not connected to IRC/Bancho")
@ -82,6 +70,10 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side :return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
""" """
try: 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 # Get token if not defined
if token is None: if token is None:
token = glob.tokens.getTokenFromUserID(userID) token = glob.tokens.getTokenFromUserID(userID)
@ -90,10 +82,6 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
raise exceptions.userNotFoundException raise exceptions.userNotFoundException
else: else:
token = token token = token
userID = token.userID
# Get usertoken data
username = token.username
# Determine internal/client name if needed # Determine internal/client name if needed
# (toclient is used clientwise for #multiplayer and #spectator channels) # (toclient is used clientwise for #multiplayer and #spectator channels)
@ -113,12 +101,22 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
# Make sure the channel exists # Make sure the channel exists
if channel not in glob.channels.channels: if channel not in glob.channels.channels:
raise exceptions.channelUnknownException 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) # Part channel (token-side and channel-side)
channelObject = glob.channels.channels[channel] channelObject = glob.channels.channels[channel]
token.partChannel(channel) token.partChannel(channelObject)
channelObject.userPart(userID)
# Delete temporary channel if everyone left
if "chat/{}".format(channelObject.name) in glob.streams.streams:
sas = len(glob.streams.streams["chat/{}".format(channelObject.name)].clients)
print(str(sas - 1))
if channelObject.temp == True and sas - 1 == 0:
glob.channels.removeChannel(channelObject.name)
# Force close tab if needed # Force close tab if needed
# NOTE: Maybe always needed, will check later # NOTE: Maybe always needed, will check later
@ -127,16 +125,19 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
# IRC part # IRC part
if glob.irc == True and toIRC == True: if glob.irc == True and toIRC == True:
glob.ircServer.banchoPartChannel(username, channel) glob.ircServer.banchoPartChannel(token.username, channel)
# Console output # Console output
log.info("{} parted channel {} ({})".format(username, channel, channelClient)) log.info("{} parted channel {} ({})".format(token.username, channel, channelClient))
# Return IRC code # Return IRC code
return 0 return 0
except exceptions.channelUnknownException: except exceptions.channelUnknownException:
log.warning("{} attempted to part an unknown channel ({})".format(username, channel)) log.warning("{} attempted to part an unknown channel ({})".format(token.username, channel))
return 403 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: except exceptions.userNotFoundException:
log.warning("User not connected to IRC/Bancho") log.warning("User not connected to IRC/Bancho")
return 442 # idk return 442 # idk
@ -153,7 +154,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side :return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
""" """
try: try:
tokenString = "" #tokenString = ""
# Get token object if not passed # Get token object if not passed
if token is None: if token is None:
token = glob.tokens.getTokenFromUsername(fro) token = glob.tokens.getTokenFromUsername(fro)
@ -162,11 +163,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
else: else:
# token object alredy passed, get its string and its username (fro) # token object alredy passed, get its string and its username (fro)
fro = token.username fro = token.username
tokenString = token.token #tokenString = token.token
# Set some variables
userID = token.userID
username = token.username
# Make sure this is not a tournament client # Make sure this is not a tournament client
if token.tournament: if token.tournament:
@ -185,7 +182,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
toClient = to toClient = to
if to == "#spectator": if to == "#spectator":
if token.spectating is None: if token.spectating is None:
s = userID s = token.userID
else: else:
s = token.spectatingUserID s = token.spectatingUserID
to = "#spect_{}".format(s) to = "#spect_{}".format(s)
@ -203,7 +200,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
message = glob.chatFilters.filterMessage(message) message = glob.chatFilters.filterMessage(message)
# Build packet bytes # Build packet bytes
packet = serverPackets.sendMessage(username, toClient, message) packet = serverPackets.sendMessage(token.username, toClient, message)
# Send the message # Send the message
isChannel = to.startswith("#") isChannel = to.startswith("#")
@ -211,35 +208,28 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
# CHANNEL # CHANNEL
# Make sure the channel exists # Make sure the channel exists
if to not in glob.channels.channels: if to not in glob.channels.channels:
raise exceptions.channelUnknownException raise exceptions.channelUnknownException()
# Make sure the channel is not in moderated mode # Make sure the channel is not in moderated mode
if glob.channels.channels[to].moderated == True and token.admin == False: if glob.channels.channels[to].moderated == True and token.admin == False:
raise exceptions.channelModeratedException raise exceptions.channelModeratedException()
# Make sure we have write permissions # Make sure we have write permissions
if glob.channels.channels[to].publicWrite == False and token.admin == False: if glob.channels.channels[to].publicWrite == False and token.admin == False:
raise exceptions.channelNoPermissionsException raise exceptions.channelNoPermissionsException()
# Everything seems fine, build recipients list and send packet # Everything seems fine, build recipients list and send packet
recipients = glob.channels.channels[to].connectedUsers[:] glob.streams.broadcast("chat/{}".format(to), packet, but=[token.token])
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: else:
# USER # USER
# Make sure recipient user is connected # Make sure recipient user is connected
recipientToken = glob.tokens.getTokenFromUsername(to) recipientToken = glob.tokens.getTokenFromUsername(to)
if recipientToken is None: if recipientToken is None:
raise exceptions.userNotFoundException raise exceptions.userNotFoundException()
# Make sure the recipient is not a tournament client # Make sure the recipient is not a tournament client
if recipientToken.tournament: #if recipientToken.tournament:
raise exceptions.userTournamentException() # raise exceptions.userTournamentException()
# Make sure the recipient is not restricted or we are FokaBot # Make sure the recipient is not restricted or we are FokaBot
if recipientToken.restricted == True and fro.lower() != "fokabot": if recipientToken.restricted == True and fro.lower() != "fokabot":
@ -248,7 +238,7 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
# TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend # TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend
# Away check # Away check
if recipientToken.awayCheck(userID): if recipientToken.awayCheck(token.userID):
sendMessage(to, fro, "\x01ACTION is away: {message}\x01".format(code=chr(int(1)), message=recipientToken.awayMessage)) sendMessage(to, fro, "\x01ACTION is away: {message}\x01".format(code=chr(int(1)), message=recipientToken.awayMessage))
# Check message templates (mods/admins only) # Check message templates (mods/admins only)
@ -263,38 +253,38 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
glob.ircServer.banchoMessage(fro, to, message) glob.ircServer.banchoMessage(fro, to, message)
# Spam protection (ignore FokaBot) # Spam protection (ignore FokaBot)
if userID > 999: if token.userID > 999:
token.spamProtection() token.spamProtection()
# Fokabot message # Fokabot message
if isChannel == True or to.lower() == "fokabot": if isChannel == True or to.lower() == "fokabot":
fokaMessage = fokabot.fokabotResponse(username, to, message) fokaMessage = fokabot.fokabotResponse(token.username, to, message)
if fokaMessage: if fokaMessage:
sendMessage("FokaBot", to if isChannel else fro, fokaMessage) sendMessage("FokaBot", to if isChannel else fro, fokaMessage)
# File and discord logs (public chat only) # File and discord logs (public chat only)
if to.startswith("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")): if to.startswith("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")):
log.chat("{fro} @ {to}: {message}".format(fro=username, to=to, message=str(message.encode("utf-8")))) log.chat("{fro} @ {to}: {message}".format(fro=token.username, to=to, message=str(message.encode("utf-8"))))
glob.schiavo.sendChatlog("**{fro} @ {to}:** {message}".format(fro=username, to=to, message=str(message.encode("utf-8"))[2:-1])) glob.schiavo.sendChatlog("**{fro} @ {to}:** {message}".format(fro=token.username, to=to, message=str(message.encode("utf-8"))[2:-1]))
return 0 return 0
except exceptions.userSilencedException: except exceptions.userSilencedException:
token.enqueue(serverPackets.silenceEndTime(token.getSilenceSecondsLeft())) token.enqueue(serverPackets.silenceEndTime(token.getSilenceSecondsLeft()))
log.warning("{} tried to send a message during silence".format(username)) log.warning("{} tried to send a message during silence".format(token.username))
return 404 return 404
except exceptions.channelModeratedException: except exceptions.channelModeratedException:
log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(username, to)) log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(token.username, to))
return 404 return 404
except exceptions.channelUnknownException: except exceptions.channelUnknownException:
log.warning("{} tried to send a message to an unknown channel ({})".format(username, to)) log.warning("{} tried to send a message to an unknown channel ({})".format(token.username, to))
return 403 return 403
except exceptions.channelNoPermissionsException: except exceptions.channelNoPermissionsException:
log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(username, to)) log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(token.username, to))
return 404 return 404
except exceptions.userRestrictedException: except exceptions.userRestrictedException:
log.warning("{} tried to send a message {}, but the recipient is in restricted mode".format(username, to)) log.warning("{} tried to send a message {}, but the recipient is in restricted mode".format(token.username, to))
return 404 return 404
except exceptions.userTournamentException: except exceptions.userTournamentException:
log.warning("{} tried to send a message {}, but the recipient is a tournament client".format(username, to)) log.warning("{} tried to send a message {}, but the recipient is a tournament client".format(token.username, to))
return 404 return 404
except exceptions.userNotFoundException: except exceptions.userNotFoundException:
log.warning("User not connected to IRC/Bancho") log.warning("User not connected to IRC/Bancho")

View File

@ -18,7 +18,6 @@ class channel:
self.publicWrite = publicWrite self.publicWrite = publicWrite
self.moderated = False self.moderated = False
self.temp = temp self.temp = temp
self.connectedUsers = [999] # Fokabot is always connected to every channels (otherwise it doesn't show up in IRC users list)
self.hidden = hidden self.hidden = hidden
# Client name (#spectator/#multiplayer) # Client name (#spectator/#multiplayer)
@ -28,27 +27,7 @@ class channel:
elif self.name.startswith("#multi_"): elif self.name.startswith("#multi_"):
self.clientName = "#multiplayer" self.clientName = "#multiplayer"
def userJoin(self, userID): # Make Foka join the channel
""" fokaToken = glob.tokens.getTokenFromUserID(999)
Add a user to connected users if fokaToken is not None:
fokaToken.joinChannel(self)
:param userID:
:return:
"""
if userID not in self.connectedUsers:
self.connectedUsers.append(userID)
def userPart(self, userID):
"""
Remove a user from connected users
:param userID:
:return:
"""
if userID in self.connectedUsers:
self.connectedUsers.remove(userID)
# Remove temp channels if empty or there's only fokabot connected
l = len(self.connectedUsers)
if self.temp == True and ((l == 0) or (l == 1 and 999 in self.connectedUsers)):
glob.channels.removeChannel(self.name)

View File

@ -1,4 +1,5 @@
from common.log import logUtils as log from common.log import logUtils as log
from constants import serverPackets
from objects import channel from objects import channel
from objects import glob from objects import glob
@ -34,6 +35,7 @@ class channelList:
:param hidden: if True, thic channel won't be shown in channels list :param hidden: if True, thic channel won't be shown in channels list
:return: :return:
""" """
glob.streams.add("chat/{}".format(name))
self.channels[name] = channel.channel(name, description, publicRead, publicWrite, temp, hidden) self.channels[name] = channel.channel(name, description, publicRead, publicWrite, temp, hidden)
log.info("Created channel {}".format(name)) log.info("Created channel {}".format(name))
@ -47,6 +49,7 @@ class channelList:
""" """
if name in self.channels: if name in self.channels:
return False return False
glob.streams.add("chat/{}".format(name))
self.channels[name] = channel.channel(name, "Chat", True, True, True, True) self.channels[name] = channel.channel(name, "Chat", True, True, True, True)
log.info("Created temp channel {}".format(name)) log.info("Created temp channel {}".format(name))
@ -60,5 +63,8 @@ class channelList:
if name not in self.channels: if name not in self.channels:
log.debug("{} is not in channels list".format(name)) log.debug("{} is not in channels list".format(name))
return return
glob.streams.broadcast("chat/{}".format(name), serverPackets.channelKicked(name))
glob.streams.dispose("chat/{}".format(name))
glob.streams.remove("chat/{}".format(name))
self.channels.pop(name) self.channels.pop(name)
log.info("Removed channel {}".format(name)) log.info("Removed channel {}".format(name))

View File

@ -5,6 +5,7 @@ import uuid
from common.constants import gameModes, actions from common.constants import gameModes, actions
from common.log import logUtils as log from common.log import logUtils as log
from common.ripple import userUtils from common.ripple import userUtils
from constants import exceptions
from constants import serverPackets from constants import serverPackets
from events import logoutEvent from events import logoutEvent
from helpers import chatHelper as chat from helpers import chatHelper as chat
@ -98,7 +99,12 @@ class token:
:param bytes: (packet) bytes to enqueue :param bytes: (packet) bytes to enqueue
""" """
# TODO: reduce max queue size
# Never enqueue for IRC clients or Foka
if self.irc or self.userID < 999:
return
# Avoid memory leaks
if len(bytes_) < 10 * 10 ** 6: if len(bytes_) < 10 * 10 ** 6:
self.queue += bytes_ self.queue += bytes_
else: else:
@ -108,23 +114,30 @@ class token:
"""Resets the queue. Call when enqueued packets have been sent""" """Resets the queue. Call when enqueued packets have been sent"""
self.queue = bytes() self.queue = bytes()
def joinChannel(self, channel): def joinChannel(self, channelObject):
""" """
Add channel to joined channels list Join a channel
:param channel: channel name :param channelObject: channel object
:raises: exceptions.userAlreadyInChannelException()
exceptions.channelNoPermissionsException()
""" """
if channel not in self.joinedChannels: if channelObject.name in self.joinedChannels:
self.joinedChannels.append(channel) raise exceptions.userAlreadyInChannelException()
if channelObject.publicRead == False and self.admin == False:
raise exceptions.channelNoPermissionsException()
self.joinedChannels.append(channelObject.name)
self.joinStream("chat/{}".format(channelObject.name))
self.enqueue(serverPackets.channelJoinSuccess(self.userID, channelObject.clientName))
def partChannel(self, channel): def partChannel(self, channelObject):
""" """
Remove channel from joined channels list Remove channel from joined channels list
:param channel: channel name :param channelObject: channel object
""" """
if channel in self.joinedChannels: self.joinedChannels.remove(channelObject.name)
self.joinedChannels.remove(channel) self.leaveStream("chat/{}".format(channelObject.name))
def setLocation(self, latitude, longitude): def setLocation(self, latitude, longitude):
""" """
@ -233,7 +246,7 @@ class token:
chat.partChannel(token=self, channel="#spect_{}".format(self.spectatingUserID), kick=True) chat.partChannel(token=self, channel="#spect_{}".format(self.spectatingUserID), kick=True)
# Console output # Console output
log.info("{} is no longer spectating {}".format(self.username, self.spectatingUserID)) log.info("{} is no longer spectating {}. Current spectators: {}".format(self.username, self.spectatingUserID, hostToken.spectators))
# Set our spectating user to 0 # Set our spectating user to 0
self.spectating = None self.spectating = None

View File

@ -43,15 +43,29 @@ class stream:
log.info("{} has left stream {}".format(token, self.name)) log.info("{} has left stream {}".format(token, self.name))
self.clients.remove(token) self.clients.remove(token)
def broadcast(self, data): def broadcast(self, data, but=None):
""" """
Send some data to all clients connected to this stream Send some data to all (or some) clients connected to this stream
:param data: data to send :param data: data to send
:param but: array of tokens to ignore. Default: None (send to everyone)
:return:
"""
if but is None:
but = []
for i in self.clients:
if i in glob.tokens.tokens:
if i not in but:
glob.tokens.tokens[i].enqueue(data)
else:
self.removeClient(token=i)
def dispose(self):
"""
Tell every client in this stream to leave the stream
:return: :return:
""" """
for i in self.clients: for i in self.clients:
if i in glob.tokens.tokens: if i in glob.tokens.tokens:
glob.tokens.tokens[i].enqueue(data) glob.tokens.tokens[i].leaveStream(self.name)
else:
self.removeClient(token=i)

View File

@ -1,6 +1,7 @@
from objects import stream from objects import stream
from objects import glob from objects import glob
# TODO: use *args and **kwargs
class streamList: class streamList:
def __init__(self): def __init__(self):
self.streams = {} self.streams = {}
@ -55,25 +56,28 @@ class streamList:
return return
self.streams[streamName].removeClient(client=client, token=token) self.streams[streamName].removeClient(client=client, token=token)
def broadcast(self, streamName, data): def broadcast(self, streamName, data, but=None):
""" """
Send some data to all clients in a stream Send some data to all clients in a stream
:param streamName: stream name :param streamName: stream name
:param data: data to send :param data: data to send
:param but: array of tokens to ignore. Default: None (send to everyone)
:return: :return:
""" """
if streamName not in self.streams: if streamName not in self.streams:
return return
self.streams[streamName].broadcast(data) self.streams[streamName].broadcast(data, but)
'''def getClients(self, streamName): def dispose(self, streamName, *args, **kwargs):
""" """
Get all clients in a stream Call `dispose` on `streamName`
:param streamName: name of the stream :param streamName: name of the stream
:param args:
:param kwargs:
:return: :return:
""" """
if streamName not in self.streams: if streamName not in self.streams:
return return
return self.streams[streamName].clients''' self.streams[streamName].dispose(*args, **kwargs)

10
pep.py
View File

@ -154,6 +154,11 @@ if __name__ == "__main__":
consoleHelper.printColored("[!] Error while loading chat filters. Make sure there is a filters.txt file present", bcolors.RED) consoleHelper.printColored("[!] Error while loading chat filters. Make sure there is a filters.txt file present", bcolors.RED)
raise raise
# Start fokabot
consoleHelper.printNoNl("> Connecting FokaBot... ")
fokabot.connect()
consoleHelper.printDone()
# Initialize chat channels # Initialize chat channels
print("> Initializing chat channels... ") print("> Initializing chat channels... ")
glob.channels.loadChannels() glob.channels.loadChannels()
@ -165,11 +170,6 @@ if __name__ == "__main__":
glob.streams.add("lobby") glob.streams.add("lobby")
consoleHelper.printDone() consoleHelper.printDone()
# Start fokabot
consoleHelper.printNoNl("> Connecting FokaBot... ")
fokabot.connect()
consoleHelper.printDone()
# Initialize user timeout check loop # Initialize user timeout check loop
consoleHelper.printNoNl("> Initializing user timeout check loop... ") consoleHelper.printNoNl("> Initializing user timeout check loop... ")
glob.tokens.usersTimeoutCheckLoop() glob.tokens.usersTimeoutCheckLoop()