Example #1
0
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
Example #2
0
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
Example #3
0
def sendMessage(sender="", target="", message="", token=None, toIRC=True):
    """
	Send a message to osu!bancho and IRC server

	:param sender: sender username. Optional. token can be used instead
	:param target: receiver channel (if starts with #) or username
	:param message: text of the message
	:param token: sender token object. Optional. sender 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
	"""
    reencode = lambda s: s.encode('latin-1').decode('utf-8')
    try:
        #tokenString = ""
        # Get token object if not passed
        if token is None:
            token = glob.tokens.getTokenFromUsername(sender)
            if token is None:
                raise exceptions.userNotFoundException()
        else:
            # token object alredy passed, get its string and its username (sender)
            sender = 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()

        if target in chatChannels.RESERVED_CHANNELS:
            sendMessage(glob.BOT_NAME, token.username,
                        "\x01ACTION gaplok kamu\x01")
            token.enqueue(serverPackets.notification("You have been gaplok'd"))
            return 403

        # Bancho style.
        isAPI = sender == glob.BOT_NAME and message.startswith(
            "\x02YOHANE ") and message.endswith("\x02")
        isSPub = target.startswith(
            f"{chatChannels.SPECTATOR_PREFIX}_") or target.startswith(
                f"{chatChannels.MULTIPLAYER_PREFIX}_") or target in (
                    chatChannels.SPECTATOR_MASK, chatChannels.MULTIPLAYER_MASK)
        forBot = target.lower() == glob.BOT_NAME.lower()
        botCommand = (target[0] == '#' and message[0] == '!') or forBot
        if isAPI:
            message = message[8:-1]
        if botCommand and not isAPI:
            redirectBot = not (token.admin or isSPub or target.lower()
                               == chatChannels.SPECIAL_CHANNEL)
            if target[0] == '#' or forBot:
                pass
            if redirectBot:
                target = glob.BOT_NAME
            log.info(
                f'Redirect {redirectBot} ({token.admin}/{isSPub}) -> {target}({forBot}) -> {message}'
            )
        if message.lower().split()[0] in ('!help', '!report'):
            target = glob.BOT_NAME

        # Determine internal name if needed
        # (toclient is used clientwise for #multiplayer and #spectator channels)
        target, toClient = channelMasking(token, target, userID=token.userID)
        if target is None and toClient is None:
            return 0

        isChannel = target.startswith("#")
        # 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 to IRC
        if glob.irc and toIRC:
            messageSplitInLines = reencode(message).split("\n")
            for line in messageSplitInLines:
                if line == messageSplitInLines[:1] and line == "":
                    continue
                glob.ircServer.banchoMessage(sender, target, line)

        # Spam protection (ignore the bot)
        if token.userID > 1 and not token.admin:
            token.spamProtection()

        # File and discord logs (public chat only)
        eligibleLogging = target.startswith("#") and not (
            message.startswith("\x01ACTION is playing")
            and target.startswith(f"{chatChannels.SPECTATOR_PREFIX}_"))
        # this one is to mark "!" usage, as those are thrown directly to the bot.
        eligibleLogging = eligibleLogging or (target == glob.BOT_NAME
                                              and not forBot)
        if eligibleLogging:
            log.chat("{sender} @ {target}: {message}".format(
                sender=token.username,
                target=target,
                message=reencode(message)))
            glob.schiavo.sendChatlog(
                "**{sender} @ {target}:** {message}".format(
                    sender=token.username,
                    target=target,
                    message=reencode(message)))

        # Send the message
        if isChannel:
            # CHANNEL
            # Make sure the channel exists
            if target not in glob.channels.channels:
                raise exceptions.channelUnknownException()

            # Make sure the channel is not in moderated mode
            if glob.channels.channels[target].moderated and not token.admin:
                raise exceptions.channelModeratedException()

            # Make sure we are in the channel
            if target 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 (token.admin or glob.channels.channels[target].publicWrite):
                raise exceptions.channelNoPermissionsException()

            # Add message in buffer
            token.addMessageInBuffer(target, message)

            # Everything seems fine, build recipients list and send packet
            glob.streams.broadcast("chat/{}".format(target),
                                   packet,
                                   but=[token.token])
        else:
            # USER
            # Make sure recipient user is connected
            recipientToken = glob.tokens.getTokenFromUsername(target)
            if recipientToken is None:
                raise exceptions.userNotFoundException()

            # Make sure the recipient is not a tournament client
            if recipientToken.tournament:
                raise exceptions.userTournamentException()

            if recipientToken.ignoreDM:
                targetFriends = userUtils.getFriendList(recipientToken.userID)
                if sender != glob.BOT_NAME and token.userID not in targetFriends and not token.admin:
                    packet = serverPackets.userDeniedMessage(
                        token.username, toClient, message)
                    token.enqueue(packet)
                    raise exceptions.userBlockedPrivateException()

            # Make sure the recipient is not restricted or we are the bot
            if recipientToken.restricted and sender.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(
                    target, sender, "\x01ACTION is away: {}\x01".format(
                        recipientToken.awayMessage))

            # Check message templates (mods/admins only)
            if message in messageTemplates.templates and token.admin:
                sendMessage(sender, target,
                            messageTemplates.templates[message])

            # Everything seems fine, send packet
            recipientToken.enqueue(packet)

        # Bot must not react to their own message.
        if sender == glob.BOT_NAME and not isAPI:
            return 0

        # Some bot message
        if (isChannel or target == glob.BOT_NAME):
            if botCommand or isAPI:
                msgOffset = 0 if forBot else 1
                da10Message = fokabot.fokabotCommands(token, token.username,
                                                      target,
                                                      message[msgOffset:])
            else:
                da10Message = fokabot.fokabotResponse(token, token.username,
                                                      target, message)
            if da10Message:
                sendMessage(glob.BOT_NAME, target if isChannel else sender,
                            da10Message)
        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, target))
        return 404
    except exceptions.channelUnknownException:
        log.warning(
            "{} tried to send a message to an unknown channel ({})".format(
                token.username, target))
        return 403
    except exceptions.channelNoPermissionsException:
        log.warning(
            "{} tried to send a message to channel {}, but they have no write permissions"
            .format(token.username, target))
        return 404
    except exceptions.userRestrictedException:
        log.warning(
            "{} tried to send a message {}, but the recipient is in restricted mode"
            .format(token.username, target))
        return 404
    except exceptions.userTournamentException:
        log.warning(
            "{} tried to send a message {}, but the recipient is a tournament client"
            .format(token.username, target))
        return 404
    except exceptions.userNotFoundException:
        log.warning("User not connected to IRC/Bancho")
        return 401
    except exceptions.userBlockedPrivateException:
        return 404
    except exceptions.invalidArgumentsException:
        log.warning("{} tried to send an invalid message to {}".format(
            token.username, target))
        return 404
Example #4
0
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 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

            # Away check
            if recipientToken.awayCheck(token.userID):
                sendMessage(
                    to, fro, "\x01ACTION is away: {}\x01".format(
                        recipientToken.awayMessage))

            # 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 the bot)
        if token.userID > 999:
            token.spamProtection()

        # Some 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
Example #5
0
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

        #here is a filter for REALLY bad words. while we will allow things such as f**k, we dont want the community to be really toxic and unwelcoming
        #TODO: unhardcode this
        MessageList = message.split(
            " ")  #makes it easier to analyse and filter
        ReallyBadWords = [  #also realistik was here
            "nazi",
            "testword1",  #so i can test without saying a really bad word!
            "nigger",  #ah the classic
            "n***a",
            "coon",
            ## GERMAN ##
            "hurensohn",
            ## RUSSIAN ##
            "хуесос",
            "пидорас",
            "пидр"
        ]
        # we EXTERMINATE THE BAD BAD WORDS!!!!
        FinalMessage = ""
        for Word in MessageList:
            #ik there is a better way of doing this but this is the only way i could come up with that ignored caps
            for BadWord in ReallyBadWords:
                if BadWord in Word:
                    Word = "#########"  #TODO: make the amount of hashes be the lenght of the word
            FinalMessage += f"{Word} "
        message = FinalMessage[:-1]  #removes the last space

        # 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