Skip to content

Commit f37ca97

Browse files
ComputerTech312James Trotter
authored andcommitted
Added SSL support using QSslSocket, including added a dialog box for it to be enabled/disabled | Made irc_client.py use logger.py to log to a file and the terminal(if ran via terminal) | renamed release to 0.0.1-alpha.1
1 parent 0950079 commit f37ca97

File tree

6 files changed

+430
-18
lines changed

6 files changed

+430
-18
lines changed

__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-

logs/irc_client.log

Lines changed: 375 additions & 0 deletions
Large diffs are not rendered by default.

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
dialog = ConnectDialog()
1111
if dialog.exec() == QDialog.DialogCode.Accepted:
12-
host, port, nick, realname, channel = dialog.get_values()
13-
client = IRCClient(host, port, nick, realname, channel)
12+
host, port, nick, realname, channel, use_ssl = dialog.get_values()
13+
client = IRCClient(host, port, nick, realname, channel, use_ssl)
1414
client.connect_to_host()
1515

1616
window = MainWindow(client)

src/gui/connect_dialog.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QDialogButtonBox, QLabel, QApplication
1+
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QDialogButtonBox, QLabel, QApplication, QCheckBox
22

33
class ConnectDialog(QDialog):
44
def __init__(self, parent=None):
@@ -38,13 +38,18 @@ def __init__(self, parent=None):
3838
self.channel_input.setText("#pyechat")
3939
self.layout.addWidget(self.channel_input)
4040

41+
self.ssl_label = QLabel("Use SSL:", self)
42+
self.layout.addWidget(self.ssl_label)
43+
self.ssl_checkbox = QCheckBox(self)
44+
self.layout.addWidget(self.ssl_checkbox)
45+
4146
self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self)
4247
self.buttons.accepted.connect(self.accept)
4348
self.buttons.rejected.connect(self.reject)
4449
self.layout.addWidget(self.buttons)
4550

4651
def get_values(self):
47-
return self.host_input.text(), int(self.port_input.text()), self.nick_input.text(), self.realname_input.text(), self.channel_input.text()
48-
52+
return self.host_input.text(), int(self.port_input.text()), self.nick_input.text(), self.realname_input.text(), self.channel_input.text(), self.ssl_checkbox.isChecked()
53+
4954
def closeEvent(self, event):
5055
QApplication.quit()

src/irc/irc_client.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,46 @@
11
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
2-
from PyQt6.QtNetwork import QTcpSocket
3-
from PyQt6.QtWidgets import QApplication, QVBoxLayout, QLineEdit, QDialogButtonBox, QDialog, QLabel
4-
2+
from PyQt6.QtNetwork import QSslSocket
3+
from src.irc.logger import Logger
4+
import datetime
5+
56
class IRCClient(QObject):
67
received_message = pyqtSignal(str)
78

8-
def __init__(self, server, port, nickname, realname, channel):
9+
def __init__(self, server, port, nickname, realname, channel, use_ssl=False):
910
super().__init__()
1011

11-
self.socket = QTcpSocket()
12+
self.socket = QSslSocket()
1213
self.socket.readyRead.connect(self.on_ready_read)
1314
self.socket.errorOccurred.connect(self.on_error_occurred)
15+
self.socket.connected.connect(self.start_encryption)
1416

1517
self.server = server
1618
self.port = port
1719
self.nickname = nickname
1820
self.realname = realname
1921
self.channel = channel
22+
self.use_ssl = use_ssl
23+
24+
self.logger = Logger('logs/irc_client.log') # Create an instance of Logger
25+
self.logger.info(f'Initialized IRCClient with server={server}, port={port}, nickname={nickname}, realname={realname}, channel={channel}, use_ssl={use_ssl}')
26+
27+
@pyqtSlot()
28+
def start_encryption(self):
29+
pass
2030

2131
def connect_to_host(self):
22-
self.socket.connectToHost(self.server, self.port)
32+
if self.use_ssl and not QSslSocket.supportsSsl():
33+
self.logger.error("This system does not support SSL.")
34+
return
35+
36+
self.logger.info(f'Connecting to host {self.server}:{self.port}')
37+
if self.use_ssl:
38+
self.socket.connectToHostEncrypted(self.server, self.port)
39+
else:
40+
self.socket.connectToHost(self.server, self.port)
2341
self.send_command(f'NICK {self.nickname}')
2442
self.send_command(f'USER {self.nickname} 0 * :{self.realname}')
43+
self.logger.info(f'Connected to host {self.server}:{self.port} with SSL={self.use_ssl}')
2544

2645
def decode(self, bytes):
2746
encodings = ['utf-8', 'latin1', 'iso-8859-1', 'cp1252']
@@ -30,12 +49,22 @@ def decode(self, bytes):
3049
return bytes.decode(encoding)
3150
except UnicodeDecodeError:
3251
continue
52+
self.logger.error('None of the encodings could decode the bytes.')
3353
raise UnicodeDecodeError("None of the encodings could decode the bytes.")
3454

3555
def parse_message(self, message):
56+
57+
tags = {}
58+
server_time = None
59+
if message.startswith('@'):
60+
tags_str, message = message[1:].split(' ', 1)
61+
tags = dict(tag.split('=') for tag in tags_str.split(';'))
62+
if 'time' in tags:
63+
server_time = datetime.datetime.fromisoformat(tags['time'].replace('Z', '+00:00'))
64+
3665
parts = message.split()
3766
if len(parts) < 2:
38-
return None, None, []
67+
return None, None, [], tags, server_time
3968
source = parts[0][1:] if parts[0].startswith(':') else None
4069
command = parts[1] if source else parts[0]
4170
args_start = 2 if source else 1
@@ -49,16 +78,17 @@ def parse_message(self, message):
4978
args.append(part)
5079
if trailing_arg_start is not None:
5180
args.append(' '.join(parts[trailing_arg_start:])[1:])
52-
return source, command, args
81+
return source, command, args, tags, server_time
5382

5483
@pyqtSlot()
5584
def on_ready_read(self):
5685
while self.socket.canReadLine():
5786
line = self.socket.readLine().data()
5887
line = self.decode(line).strip()
5988
self.received_message.emit(line)
89+
self.logger.debug(f'Received line: {line}')
6090

61-
source, command, args = self.parse_message(line)
91+
source, command, args, _, _ = self.parse_message(line)
6292

6393
if command == 'NICK' and source.split('!')[0] == self.nickname:
6494
self.nickname = args[0]
@@ -70,15 +100,18 @@ def on_ready_read(self):
70100
elif 'End of /MOTD command.' in line:
71101
self.send_command(f'JOIN {self.channel}')
72102

73-
@pyqtSlot(QTcpSocket.SocketError)
103+
@pyqtSlot(QSslSocket.SocketError)
74104
def on_error_occurred(self, socket_error):
75-
print(f'Error occurred: {self.socket.errorString()}')
105+
self.logger.error(f'Error occurred on the socket: {self.socket.errorString()}')
76106

77107
def send_command(self, command):
108+
self.logger.info(f'Sending command: {command}')
78109
if ' ' in command:
79110
cmd, args = command.split(' ', 1)
80111
if cmd in ['JOIN', 'PART']:
81112
args = args.split(' ', 1)[0]
82113
self.socket.write(f'{cmd} {args}\r\n'.encode())
114+
self.logger.info(f'Sent command: {cmd} {args}') # Log the command sent
83115
else:
84-
self.socket.write(f'{command}\r\n'.encode())
116+
self.socket.write(f'{command}\r\n'.encode())
117+
self.logger.info(f'Sent command: {command}') # Log the command sent
File renamed without changes.

0 commit comments

Comments
 (0)