Inital commit after 3 days of coding...
This commit is contained in:
parent
02f59aaa28
commit
4d99fa7d4e
7
config.ini
Normal file
7
config.ini
Normal file
@ -0,0 +1,7 @@
|
||||
[ircnet]
|
||||
server = irc.ircnet.com
|
||||
port = 6667
|
||||
nickname = KalzuTest
|
||||
;password = server_password
|
||||
;channels = [list, of, channels]
|
||||
|
138
ircthing3.py
Normal file
138
ircthing3.py
Normal file
@ -0,0 +1,138 @@
|
||||
import threading
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import socket
|
||||
import configparser
|
||||
from ircthing_core import irc_router
|
||||
|
||||
Threads = {}
|
||||
Stop_Toggle = threading.Event()
|
||||
|
||||
def read_config(config_path):
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
|
||||
network_configs = []
|
||||
|
||||
try:
|
||||
# Collect information from each topic / network
|
||||
for topic in config.sections():
|
||||
network = config[topic]
|
||||
server = network.get("server")
|
||||
port = network.getint("port")
|
||||
channels = network.get("channels", fallback=None)
|
||||
nickname = network.get("nickname")
|
||||
password = network.get("password", fallback=None)
|
||||
|
||||
network_config = {
|
||||
"net_name": network.name,
|
||||
"server": server,
|
||||
"port": port,
|
||||
"channels": channels,
|
||||
"nickname": nickname,
|
||||
"password": password,
|
||||
}
|
||||
network_configs.append(network_config)
|
||||
return network_configs
|
||||
except Exception as e:
|
||||
print(f"Failure while reading configuration file. {e}")
|
||||
exit(1)
|
||||
|
||||
def clean_exit(fifo_files, socket):
|
||||
socket.send(bytes(f"QUIT :Bye\r\n", "UTF-8"))
|
||||
Stop_Toggle.set()
|
||||
for file in fifo_files:
|
||||
try:
|
||||
os.unlink(file)
|
||||
except FileNotFoundError:
|
||||
# We are okay if some fifo files has been removed before this.
|
||||
pass
|
||||
|
||||
def make_files(path, net_name):
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
server_dir = os.path.join(path, net_name)
|
||||
os.makedirs(server_dir, exist_ok=True)
|
||||
try:
|
||||
os.mkfifo(f"{server_dir}/in")
|
||||
except FileExistsError:
|
||||
pass
|
||||
try:
|
||||
os.mkfifo(f"{server_dir}/out")
|
||||
except FileExistsError:
|
||||
pass
|
||||
fifo_files = []
|
||||
fifo_files.append(f"{server_dir}/in")
|
||||
fifo_files.append(f"{server_dir}/out")
|
||||
return fifo_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 = make_files(base_path, net_name)
|
||||
|
||||
return irc_socket, fifo_files
|
||||
|
||||
def main():
|
||||
my_name = sys.argv[0]
|
||||
my_name_pyless, _ = os.path.splitext(my_name)
|
||||
base_path = f'/tmp/{my_name_pyless}'
|
||||
|
||||
config_path = '../ircthing3.ini'
|
||||
|
||||
# Read configurations for all topics
|
||||
network_configs = read_config(config_path)
|
||||
|
||||
## Get irc socket for each network in configuration
|
||||
## Start thread for each socket
|
||||
|
||||
for network in network_configs:
|
||||
net_name = network["net_name"]
|
||||
print(f"{time.time()} | Found configs for {net_name} network.")
|
||||
server = network["server"]
|
||||
port = network["port"]
|
||||
nickname = network["nickname"]
|
||||
password = network["password"]
|
||||
irc_socket, fifo_files = connect_to_irc_server(base_path, net_name, server, port, nickname, password)
|
||||
|
||||
router_instance = irc_router(fifo_files, irc_socket, server, nickname)
|
||||
Threads[net_name] = threading.Thread(target=router_instance.start)
|
||||
Threads[net_name].daemon = True
|
||||
Threads[net_name].start()
|
||||
|
||||
for thread in Threads.values():
|
||||
print(thread)
|
||||
thread.join()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"{time.time()} | Lets start!")
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
print(f"Got error {e}")
|
||||
finally:
|
||||
print(f"{time.time()} | Bye!")
|
||||
exit(0)
|
161
ircthing_core.py
Normal file
161
ircthing_core.py
Normal file
@ -0,0 +1,161 @@
|
||||
import time
|
||||
import re
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
class irc_router:
|
||||
def __init__(self, fifo_files, irc_socket, server, nickname):
|
||||
self.fifo_files = fifo_files
|
||||
self.irc_socket = irc_socket
|
||||
self.server = server
|
||||
self.nickname = nickname
|
||||
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')
|
||||
|
Loading…
Reference in New Issue
Block a user