208 lines
6.6 KiB
Python
208 lines
6.6 KiB
Python
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")
|