ircthing/ircthing_core.py
2024-02-09 09:14:24 +02:00

194 lines
6.5 KiB
Python

import time
import re
import asyncio
import socket
import os
from ircthing_utils import make_files
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._loop = asyncio.new_event_loop()
def start(self):
try:
asyncio.set_event_loop(self._loop)
tasks = [self.user_listener(), self.irc_message_loop()]
self._loop.run_until_complete(asyncio.gather(*tasks))
finally:
for file in self.fifo_files:
try:
os.close(file)
except:
# Errors are okay at this point.
pass
self._loop.close()
## Main loop for reading irc inflow and routing to correct _out fifos.
## Also handle ping/pong here
async def irc_message_loop(self):
output_files=[]
loop = asyncio.get_running_loop()
while True:
for output in self.fifo_files:
if output.endswith('out'):
if output not in output_files:
print(f"{time.time()} | {self.server} has output file: {output}")
output_files.append(output)
try:
irc_input = await loop.sock_recv(self.irc_socket, 2048)
irc_input = irc_input.decode("utf-8")
except Exception as e:
print(f"Error reading from socket: {e}")
continue
if irc_input:
##
## The main logic hapens here.
## support for channel joins
## and spliting the input to channel specifig channel/[in, out] locations
##
if "PING" in irc_input:
self.irc_send(f"PONG :{self.server}")
self.write_to_out(output_files[0], f"{time.time()} | ping/pong")
continue
if f":{self.server} 353" in irc_input:
## We have joined a channel!
match = re.search(r'353 \S+ = (#\S+)', irc_input)
channel_join = match.group(1)
await self.make_files(channel_join)
continue
if "PRIVMSG" in irc_input:
if self.if_input_belongs_to_channel(irc_input):
continue
self.write_to_out(output_files[0], irc_input)
await asyncio.sleep(0.1)
def if_input_belongs_to_channel(self, input):
# 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)
done=False
for file in self.fifo_files:
if input_channel.group(1) in file:
if file.endswith("out"):
self.write_to_out(file, f"{input_user}: {message}")
done=True
return done
async 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)
await asyncio.sleep(1)
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}")
async 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
await queue.put(line)
in_file.close()
await asyncio.sleep(0.5)
except Exception as e:
print(f"ERROR IN async_readline func: {e}")
async 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)
await asyncio.sleep(0.2)
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')