I am looking for a better way to minimize nested if statements in my MessageHandler.handleMessage method. I am looking to adhere to SRP and a function should do one thing and one thing well!
Any input would be greatly appreciated.
class MessageHandler():
""" MessageHandler - Handles twitch IRC messages and emits events based on Commands and msgid tags """
def __init__(self):
self.COMMANDS: COMMANDS = COMMANDS()
def handleMessage(self, IrcMessage: str)->tuple:
""" MessageHandler.handleMessage - breaks down data string from irc server returns tuple (event: str, message: Message) or
returns tuple of (None, None)
:param IrcMessage: a irc recieved message for server
:type IrcMessage: str
:raises TypeError: IrcMessage needs to be of type str
:return: a tuple with event string and Message data type populasted with all parsed message data
:rtype: tuple
"""
try:
if not isinstance(IrcMessage, str):
raise TypeError("MessageHandler.handleMessage requires input of type str")
message = self._parse(IrcMessage)
# Populate message values
message.channel: str = message.params[0] if len(message.params) > 0 else None
message.text: str = message.params[1] if len(message.params) > 1 else None
message.id: str = message.tags.get("msg-id")
message.raw: str = IrcMessage
message.username: str = message.tags.get("display-name")
# Parse badges and emotes
message.tags = self._badges(self._emotes(message.tags))
# Transform IRCv3 Tags
message = self._TransformIRCv3Tags(message)
except AttributeError:
return(None, None)
# Handle message with prefix "tmi.twitch.tv"
if message.prefix == self.COMMANDS.TMI_TWITCH_TV:
# Handle command bot Username
if message.command == self.COMMANDS.USERNAME:
botUsername = message.params[0]
# Chatroom NOTICE check msgid tag
elif message.command == self.COMMANDS.NOTICE:
if message.id in self.COMMANDS.MESSAGEIDS.__dict__.values():
return (self.COMMANDS.NOTICE, message)
else:
if message.raw.replace(":tmi.twitch.tv NOTICE * :",'') in ("Login unsuccessful", "Login authentication failed", "Error logging in", "Invalid NICK"):
return (self.COMMANDS.LOGIN_UNSUCCESSFUL, \
message.raw.replace(":tmi.twitch.tv NOTICE * :",''))
else:
return (message.command, message)
# Handle message with prefix jtv ??????? unsure it is still required
elif message.prefix == "jtv":
print(message.params)#still testing
else:
if message.command == self.COMMANDS.MESSAGE:
message.username: str = message.prefix[:message.prefix.find("!")]
return (self.COMMANDS.MESSAGE, message)
elif message.command == self.COMMANDS.WHISPER:
return (self.COMMANDS.WHISPER, message)
elif message.command == self.COMMANDS.NAMES:
return (self.COMMANDS.NAMES, message)
return (None, None) # invalid message
@staticmethod
def _TransformIRCv3Tags(message: Message)->Message:
""" MessageHandler._TransformIRCv3Tags reformats message tags
:param message: message object
:type message: Message
:return: message with updated tags
:rtype: Message
"""
if message.tags:
for key in message.tags:
if key not in ("emote-sets", "ban-duration", "bits"):
if isinstance(message.tags[key], bool):
message.tags[key] = None
elif message.tags[key] in ('0', '1'):
message.tags[key] = bool(int(message.tags[key]))
return message
@staticmethod
def _badges(tags: dict)->dict:
""" MessageHandler._badges - Parse tags['badges'] from str to dict and update tags['badges']
:param tags: tags from parsed IRC message
:type event: dict
:return: tags
:rtype: dict
"""
if ("badges" in tags and isinstance(tags.get("badges"), str)):
badges = tags.get("badges").split(",")
tags["badges"]: dict = {}
for badge in badges:
key, value = badge.split("/")
if value is None:
return tags
tags["badges"][key] = value
return tags
@staticmethod
def _emotes(tags: dict)->dict:
""" MessageHandler._emotes - Parse tags['emotes'] from str to list and update tags['emotes']
:param tags: tags from parsed IRC message
:type event: dict
:return: tags
:rtype: dict
"""
if ("emotes" in tags and isinstance(tags.get("emotes"), str)):
emotes: dict = {}
emoticons = tags.get("emotes").split("/")
for emoticon in emoticons:
key, value = emoticon.split(":")
if value is None:
return tags
emotes[key] = value.split(",")
tags["emotes"] = emotes
return tags
@staticmethod
def _parse(data: str)->Message:
""" MessageHandler._parse - Parses IRC messages to Message type
:param data: string from IRC server
:type event: str
:return: message
:rtype: Message
"""
message = Message()
if not isinstance(data, str):
raise TypeError("MessageHandler._parse requires input of type str")
position: int = 0
nextspace: int = 0
if len(data) < 1:
return None
if data.startswith("@"):
nextspace = data.find(" ")
if nextspace == -1:
return None # invalid message form
tags = data[1:nextspace].split(";")
for tag in tags:
key, value = tag.split("=")
message.tags[key] = value or True
position = nextspace + 1
while data[position] == " ":
position += 1
if data[position] == ":":
nextspace = data.find(" ", position)
if nextspace == -1:
return None # invalid message form
message.prefix = data[position + 1:nextspace]
position = nextspace + 1
while data[position] == " ":
position += 1
nextspace = data.find(" ", position)
if nextspace == -1:
if len(data) > position:
message.command = data[position:]
return message
return None # invalid message form
message.command = data[position:nextspace]
position = nextspace + 1
while data[position] == " ":
position += 1
dataLen = len(data)
while position < dataLen:
nextspace = data.find(" ", position)
if data[position] == ":":
message.params.append(data[position + 1:])
break
if nextspace != -1:
message.params.append(data[position:nextspace])
position = nextspace + 1
while data[position] == " ":
position += 1
continue
if nextspace == -1:
message.params.append(data[position:])
break
return message