import time import re import socket import os from ircthing_utils import make_files from threading import Thread, Event def connect_to_irc_server( base_path, net_name, server, port, nickname, password=None, ): print(f"Going to connect to: {server}") # Create a socket connection to the IRC server irc_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) irc_socket.connect((server, port)) # Send the server password if provided if password: irc_socket.send(bytes(f"PASS {password}\r\n", "UTF-8")) print(f"Going to use username: {nickname}") # Send user and nickname information to the server irc_socket.send(bytes(f"USER {nickname} 0 * :{nickname}\r\n", "UTF-8")) irc_socket.send(bytes(f"NICK {nickname}\r\n", "UTF-8")) # Create directories for the server and channel fifo_files, network_path = make_files(base_path, net_name) return irc_socket, fifo_files, network_path class irc_router: def __init__(self, fifo_files, irc_socket, server, nickname, network_dir): self.fifo_files = fifo_files self.irc_socket = irc_socket self.server = server self.nickname = nickname self.network_dir = network_dir self.channels = [] self.threads = [] self.disconnect_toggle = Event() def start(self): self.start_threads() for file in self.fifo_files: try: os.close(file) except: # Errors are okay at this point. pass self.irc_send("QUIT") def start_threads(self): user_listener_thread = Thread(target=self.user_listener) irc_listener_thread = Thread(target=self.irc_message_loop) self.threads.append(user_listener_thread) self.threads.append(irc_listener_thread) # start the threads for thread in self.threads: thread.start() # wait for all the threads to reach end for thread in self.threads: thread.join() def get_output_fifos(self): output_fifos = [] for fifo in self.fifo_files: if fifo.endswith("out") and fifo not in output_fifos: output_fifos.append(fifo) return output_fifos def write_to_server_out(self, message): server_out = os.path.join(self.network_dir, "out") if os.access(server_out, os.W_OK): self.write_to_out(server_out, message) else: print(f"{time.time()} | {self.server} | {message}") def irc_message_loop(self): while not self.disconnect_toggle.is_set(): try: irc_input = self.irc_socket.recv(2048) irc_input = irc_input.decode("utf-8") except Exception as e: print(f"Error reading from socket: {e}") continue if irc_input: self.handle_irc_input(irc_input) time.sleep(0.5) def handle_irc_input(self, irc_input): if "PING" in irc_input: self.irc_send(f"PONG :{self.server}") self.write_to_server_out("ping/pong") elif f":{self.server} 353" in irc_input: self.handle_channel_join(irc_input) elif "PRIVMSG" in irc_input: self.handle_privmsg(irc_input) else: self.write_to_server_out(irc_input) def handle_channel_join(self, irc_input): ## We have joined a channel! channel = re.search(r"353 \S+ = (#\S+)", irc_input).group(1) self.make_files(channel) def handle_privmsg(self, irc_input): if not self.input_belongs_to_channel(irc_input): self.write_to_server_out(irc_input) def input_belongs_to_channel(self, input): output_files = self.get_output_fifos() # We know that input includes "PRIVMSG" # But to where it shoudl go? message = re.search(r":.*:(.*)", input).group(1) input_user = re.search(r"^:(\S+)", input).group(1) input_channel = re.search(r"PRIVMSG (\S+) :", input).group(1) done = False for file in output_files: if input_channel in file: self.write_to_out(file, f"{input_user}: {message}") done = True return done def make_files(self, channel): network_path = os.path.dirname(self.fifo_files[1]) channel_dir = os.path.join(network_path, channel) os.makedirs(channel_dir, exist_ok=True) try: os.mkfifo(f"{channel_dir}/in") except FileExistsError: pass try: os.mkfifo(f"{channel_dir}/out") except FileExistsError: pass fifo_files = [] fifo_files.append(f"{channel_dir}/in") fifo_files.append(f"{channel_dir}/out") self.fifo_files.extend(fifo_files) def write_to_out(self, file, input): if file == None: print(f"{time.time()} | Most likely this should not be here: {input}") with open(file, "a") as output_sink: output_sink.write(input + "\r\n") output_sink.flush() output_sink.close() def irc_send(self, message): # Send message to the IRC server self.irc_socket.send(bytes(f"{message}\r\n", "utf-8")) print(f"{time.time()} | Send: {message}") def async_readline(self, file, queue): print(f"Trying to read from file {file}") try: while True: with open(file, mode="r") as in_file: line = in_file.readline().strip() if not line: print("There was no line!") continue queue.put(line) in_file.close() except Exception as e: print(f"ERROR IN async_readline func: {e}") def process_user_input(self, queue): print("Processing of user input may start!") while True: line = await queue.get() print(f"{time.time()} | trying to send: {line} to {self.server}") self.irc_send(line) time.sleep(0.5) async def user_listener(self): print("User Listener is HERE!") in_files = [] queue = asyncio.Queue() try: for file in self.fifo_files: if file.endswith("in"): print(f"We have input fifo {file}") in_files.append(file) tasks = [self.process_user_input(queue)] tasks.extend([self.async_readline(file, queue) for file in in_files]) # Wait for all tasks to complete await asyncio.gather(*tasks) finally: self.irc_send("QUIT")