def report(fro, chan, message): msg = "" try: # TODO: Rate limit # Get username, report reason and report info target, reason, additionalInfo = message[0], message[1], message[2] target = chat.fixUsernameForBancho(target) # Make sure the target is not foka if target == glob.BOT_NAME: raise exceptions.invalidUserException() # Make sure the user exists targetID = userUtils.getID(target) if targetID == 0: raise exceptions.userNotFoundException() # Make sure that the user has specified additional info if report reason is 'Other' if reason.lower() == "other" and not additionalInfo: raise exceptions.missingReportInfoException() # Get the token if possible chatlog = "" token = glob.tokens.getTokenFromUsername(userUtils.safeUsername(target), safe=True) if token is not None: chatlog = token.getMessagesBufferString() # Everything is fine, submit report glob.db.execute( "INSERT INTO reports (id, from_uid, to_uid, reason, chatlog, time, assigned) VALUES (NULL, %s, %s, %s, %s, %s, 0)", [userUtils.getID(fro), targetID, "{reason} - ingame {info}".format(reason=reason, info="({})".format( additionalInfo) if additionalInfo is not None else ""), chatlog, int(time.time())]) msg = "You've reported {target} for {reason}{info}. A Community Manager will check your report as soon as possible. Every !report message you may see in chat wasn't sent to anyone, so nobody in chat, but admins, know about your report. Thank you for reporting!".format( target=target, reason=reason, info="" if additionalInfo is None else " (" + additionalInfo + ")") 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(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.".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: msg = "The user you've tried to report doesn't exist." except exceptions.missingReportInfoException: msg = "Please specify the reason of your report." except: raise finally: if msg != "": token = glob.tokens.getTokenFromUsername(fro) if token is not None: if token.irc: chat.sendMessage(glob.BOT_NAME, fro, msg) else: token.enqueue(serverPackets.notification(msg)) return False
def mpKick(): if len(message) < 2: raise exceptions.invalidArgumentsException( "Wrong syntax: !mp kick <username>") username = message[1] userID = userUtils.getIDSafe(username) if userID is None: raise exceptions.userNotFoundException("No such user") _match = glob.matches.matches[getMatchIDFromChannel(chan)] slotID = _match.getUserSlotID(userID) if slotID is None: raise exceptions.userNotFoundException( "The specified user is not in this match") for i in range(0, 2): _match.toggleSlotLocked(slotID) return "{} has been kicked from the match.".format(username)
def mp_invite(): userID = userUtils.getID(fro) if not can_user_touch_lobby(get_match_id_from_channel(chan), userID, True): return False if len(message) < 2: raise exceptions.invalidArgumentsException( "Wrong syntax: !mp invite <username>") 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") token = glob.tokens.getTokenFromUserID(userID, ignoreIRC=True) if token is None: raise exceptions.invalidUserException( "That user is not connected to bancho right now.") _match = glob.matches.matches[get_match_id_from_channel(chan)] _match.invite(999, userID) 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 mpHost(): if len(message) < 2: raise exceptions.invalidArgumentsException("Wrong syntax: !mp host <username>") 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 = glob.matches.matches[getMatchIDFromChannel(chan)] success = _match.setHost(userID) return "{} is now the host".format(username) if success else "Couldn't give host to {}".format(username)
def lookupUser(userStr): match = re.match(r'^#([1-9][0-9]*)$', userStr) if match: log.info(f"Special case {match.group(1)}") userID = int(match.group(1)) else: userID = lookupUserID(userStr) if userID is None: raise exceptions.userNotFoundException('No such user.') if userID < 2: raise exceptions.invalidArgumentsException("\x01ACTION gaplok") return userID
def mp_kick(): userID = userUtils.getID(fro) if not can_user_touch_lobby(get_match_id_from_channel(chan), userID, False, False): return False if len(message) < 2: raise exceptions.invalidArgumentsException( "Wrong syntax: !mp kick <username>") 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 = glob.matches.matches[get_match_id_from_channel(chan)] slotID = _match.getUserSlotID(userID) if slotID is None: raise exceptions.userNotFoundException( "The specified user is not in this match") for i in range(0, 2): _match.toggleSlotLocked(slotID) return "{} has been kicked from the match.".format(username)
def mpMove(): if len(message) < 3 or not message[2].isdigit() or int(message[2]) < 0 or int(message[2]) > 16: raise exceptions.invalidArgumentsException("Wrong syntax: !mp move <username> <slot>") username = message[1] newSlotID = int(message[2]) userID = userUtils.getIDSafe(username) if userID is None: raise exceptions.userNotFoundException("No such user") _match = glob.matches.matches[getMatchIDFromChannel(chan)] success = _match.userChangeSlot(userID, newSlotID) if success: result = "Player {} moved to slot {}".format(username, newSlotID) else: result = "You can't use that slot: it's either already occupied by someone else or locked" return result
def mpTeam(): if len(message) < 3: raise exceptions.invalidArgumentsException("Wrong syntax: !mp team <username> <colour>") username = message[1].strip() if not username: raise exceptions.invalidArgumentsException("Please provide a username") colour = message[2].lower().strip() if colour not in ["red", "blue"]: raise exceptions.invalidArgumentsException("Team colour must be red or blue") userID = userUtils.getIDSafe(username) if userID is None: raise exceptions.userNotFoundException("No such user") _match = glob.matches.matches[getMatchIDFromChannel(chan)] _match.changeTeam(userID, matchTeams.BLUE if colour == "blue" else matchTeams.RED) return "{} is now in {} team".format(username, colour)
def mpInvite(): if len(message) < 2: raise exceptions.invalidArgumentsException( "Wrong syntax: !mp invite <username>") username = message[1] userID = userUtils.getIDSafe(username) if userID is None: raise exceptions.userNotFoundException("No such user") token = glob.tokens.getTokenFromUserID(userID, ignoreIRC=True) if token is None: 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.")) return "An invite to this match has been sent to {}".format(username)
def kick(response, token, sender, channel, message): if len(message) < 1: raise exceptions.invalidArgumentsException( "Wrong syntax: !mp kick <username>") ensureMatchIdle(channel) username = message[0].strip() if not username: raise exceptions.invalidArgumentsException( "Please provide a username") userID = lookupUser(username) _match = getCurrentMatch(channel) slotID = _match.getUserSlotID(userID) if slotID is None: raise exceptions.userNotFoundException( "The specified user is not in this match") for i in range(2): _match.toggleSlotLocked(slotID) response.setContent( token, "{} has been kicked from the match.".format(username))
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 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
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
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