From aa90c78224607c3b3a1469988062d66437836665 Mon Sep 17 00:00:00 2001 From: Jordyn Date: Sun, 15 Mar 2026 17:56:58 -0500 Subject: [PATCH] Increase stability of message parser and add SSL support ZNC loves to send weird messages such as empty PRIVMSG, so I hardened the prefix parsing code accordingly. Additionally, support was added for server forced nickname changes. --- main.py | 41 +++++++++++++++++++++++++++++++++-------- skel_config.py | 1 + 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/main.py b/main.py index 42a1923..77000d6 100755 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ import re import time import queue import config +import ssl as ssllib class Server(): # Order of functions in Server(): @@ -50,9 +51,18 @@ class Server(): # This function handles the socket bring up # Sets socket paramaters, and starts the send/recv threads - def connect(self, ip, port, sock_timeout = 60, sock_sendbuf = 512, sock_recvbuf = 512): + def connect(self, ip, port, ssl = False, sock_timeout = 60, sock_sendbuf = 512, sock_recvbuf = 512): print(f"[SERVER/MAINTHREAD] Connecting to {ip}:{port}") - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + if ssl: + print(f"[SERVER/MAINTHREAD] Using SSL for connection!") + context = ssllib.create_default_context() + self.sock = context.wrap_socket(sock, server_hostname = ip) + else: + print(f"[SERVER/MAINTHREAD] Using plain text for connection!") + self.sock = sock print(f"[SERVER/MAINTHREAD] Using socket timeout of {sock_timeout} seconds!") self.sock.settimeout(sock_sendbuf) @@ -311,15 +321,21 @@ class Server(): params.append(trailing) # Extract possible user info from prefix - if command in ["PRIVMSG", "INVITE"]: + if prefix: message_source = prefix.partition("!")[0] # Technically the target is ourselfs, but I change it to be the other user # so other parts of the script can easily use it to know where to send output. - if params[0] == self.nickname: - target_channel = message_source - else: - target_channel = params[0] + try: + if params[0] == self.nickname: + target_channel = message_source + else: + target_channel = params[0] + except IndexError: + # We got an empty command? what + # ZNC loves to send these + target_channel = None + else: message_source = None target_channel = None @@ -345,6 +361,11 @@ class Server(): # Handle PRIVMSG if msg["command"] == "PRIVMSG": + # Ignore empty PRIVMSGs, ZNC loves to send these. + if not len(msg["params"]): + print(f"[RECVTHREAD] Ignoring empty PRIVMSG from server!") + return + # Check for command prefix and run it if we got one if msg["params"][-1].startswith(self.command_prefix): # Strip command prefix and spaces if any from input @@ -393,7 +414,11 @@ class Server(): print(f"[RECVTHREAD] Joining channel by opper command from {msg['message_source']}!") self._send_q.put(f"JOIN {msg['params'][-1]}\r\n".encode()) self.channels.append(msg["params"][-1]) - + elif msg["command"] == "NICK": + # Somebody changed their nick, or the server forced us to change ours. + if msg["message_source"] == self.nickname: + print(f"[RECVTHREAD] Server forced nickname change to {msg['params'][0]}!") + self.nickname = msg["params"][0] # These are where bot commands are implemented # The format is _cmd_NAMEOFCOMMAND and it gets the class instance and the triggering message as parameters. diff --git a/skel_config.py b/skel_config.py index 3a76373..ce12bf3 100644 --- a/skel_config.py +++ b/skel_config.py @@ -7,6 +7,7 @@ user = { server = { "ip": "irc.serv.net", "port": 6667, + "ssl": False "sock_timeout": 60, "sock_sendbuf": 512, "sock_recvbuf": 512