--- a/src/eric7/Cooperation/CooperationClient.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Cooperation/CooperationClient.py Wed Jul 13 14:55:47 2022 +0200 @@ -10,9 +10,7 @@ import collections from PyQt6.QtCore import QObject, pyqtSignal, QProcess -from PyQt6.QtNetwork import ( - QHostInfo, QHostAddress, QAbstractSocket, QNetworkInterface -) +from PyQt6.QtNetwork import QHostInfo, QHostAddress, QAbstractSocket, QNetworkInterface from .CooperationServer import CooperationServer from .Connection import Connection @@ -23,7 +21,7 @@ class CooperationClient(QObject): """ Class implementing the client of the cooperation package. - + @signal newMessage(user, message) emitted after a new message has arrived (string, string) @signal newParticipant(nickname) emitted after a new participant joined @@ -35,23 +33,24 @@ @signal editorCommand(hash, filename, message) emitted when an editor command has been received (string, string, string) """ + newMessage = pyqtSignal(str, str) newParticipant = pyqtSignal(str) participantLeft = pyqtSignal(str) connectionError = pyqtSignal(str) cannotConnect = pyqtSignal() editorCommand = pyqtSignal(str, str, str) - + def __init__(self, parent=None): """ Constructor - + @param parent reference to the parent object (QObject) """ super().__init__(parent) - + self.__chatWidget = parent - + self.__servers = [] for networkInterface in QNetworkInterface.allInterfaces(): for addressEntry in networkInterface.addressEntries(): @@ -62,13 +61,12 @@ server = CooperationServer(address, self) server.newConnection.connect(self.__newConnection) self.__servers.append(server) - + self.__peers = collections.defaultdict(list) - + self.__initialConnection = None - - envVariables = ["USERNAME", "USERDOMAIN", "USER", - "HOSTNAME", "DOMAINNAME"] + + envVariables = ["USERNAME", "USERDOMAIN", "USER", "HOSTNAME", "DOMAINNAME"] environment = QProcess.systemEnvironment() found = False for envVariable in envVariables: @@ -79,92 +77,92 @@ self.__username = envList[1].strip() found = True break - + if found: break - + if self.__username == "": self.__username = self.tr("unknown") - + self.__listening = False self.__serversErrorString = "" - + def chatWidget(self): """ Public method to get a reference to the chat widget. - + @return reference to the chat widget (ChatWidget) """ return self.__chatWidget - + def sendMessage(self, message): """ Public method to send a message. - + @param message message to be sent (string) """ if message == "": return - + for connectionList in self.__peers.values(): for connection in connectionList: connection.sendMessage(message) - + def nickName(self): """ Public method to get the nick name. - + @return nick name (string) """ return "{0}@{1}@{2}".format( - self.__username, - QHostInfo.localHostName(), - self.__servers[0].serverPort() + self.__username, QHostInfo.localHostName(), self.__servers[0].serverPort() ) - + def hasConnection(self, senderIp, senderPort=-1): """ Public method to check for an existing connection. - + @param senderIp address of the sender (QHostAddress) @param senderPort port of the sender (integer) @return flag indicating an existing connection (boolean) """ if senderPort == -1: return senderIp in self.__peers - + if senderIp not in self.__peers: return False - - return any(connection.peerPort() == senderPort - for connection in self.__peers[senderIp]) - + + return any( + connection.peerPort() == senderPort for connection in self.__peers[senderIp] + ) + def hasConnections(self): """ Public method to check, if there are any connections established. - + @return flag indicating the presence of connections (boolean) """ - return any(bool(connectionList) - for connectionList in self.__peers.values()) - + return any(bool(connectionList) for connectionList in self.__peers.values()) + def removeConnection(self, connection): """ Public method to remove a connection. - + @param connection reference to the connection to be removed (Connection) """ - if (connection.peerAddress() in self.__peers and - connection in self.__peers[connection.peerAddress()]): + if ( + connection.peerAddress() in self.__peers + and connection in self.__peers[connection.peerAddress()] + ): self.__peers[connection.peerAddress()].remove(connection) nick = connection.name() if nick != "": self.participantLeft.emit(nick) - + if connection.isValid(): connection.abort() - + def disconnectConnections(self): """ Public slot to disconnect from the chat network. @@ -172,38 +170,34 @@ for connectionList in self.__peers.values(): while connectionList: self.removeConnection(connectionList[0]) - + def __newConnection(self, connection): """ Private slot to handle a new connection. - + @param connection reference to the new connection (Connection) """ connection.setParent(self) connection.setClient(self) - connection.setGreetingMessage(self.__username, - self.__servers[0].serverPort()) - - connection.error.connect( - lambda err: self.__connectionError(err, connection)) - connection.disconnected.connect( - lambda: self.__disconnected(connection)) - connection.readyForUse.connect( - lambda: self.__readyForUse(connection)) + connection.setGreetingMessage(self.__username, self.__servers[0].serverPort()) + + connection.error.connect(lambda err: self.__connectionError(err, connection)) + connection.disconnected.connect(lambda: self.__disconnected(connection)) + connection.readyForUse.connect(lambda: self.__readyForUse(connection)) connection.rejected.connect(self.__connectionRejected) - + def __connectionRejected(self, msg): """ Private slot to handle the rejection of a connection. - + @param msg error message (string) """ self.connectionError.emit(msg) - + def __connectionError(self, socketError, connection): """ Private slot to handle a connection error. - + @param socketError reference to the error object @type QAbstractSocket.SocketError @param connection connection that caused the error @@ -214,7 +208,7 @@ msg = "* {0}:{1}\n{2}\n".format( connection.peerAddress().toString(), connection.peerPort(), - connection.errorString() + connection.errorString(), ) else: msg = "* {0}\n".format(connection.errorString()) @@ -222,57 +216,55 @@ if connection == self.__initialConnection: self.cannotConnect.emit() self.removeConnection(connection) - + def __disconnected(self, connection): """ Private slot to handle the disconnection of a chat client. - + @param connection connection that was disconnected @type Connection """ self.removeConnection(connection) - + def __readyForUse(self, connection): """ Private slot to handle a connection getting ready for use. - + @param connection connection that got ready for use @type Connection """ if self.hasConnection(connection.peerAddress(), connection.peerPort()): return - + connection.newMessage.connect(self.newMessage) - connection.getParticipants.connect( - lambda: self.__getParticipants(connection)) + connection.getParticipants.connect(lambda: self.__getParticipants(connection)) connection.editorCommand.connect(self.editorCommand) - + self.__peers[connection.peerAddress()].append(connection) nick = connection.name() if nick != "": self.newParticipant.emit(nick) - + if connection == self.__initialConnection: connection.sendGetParticipants() self.__initialConnection = None - + def connectToHost(self, host, port): """ Public method to connect to a host. - + @param host host to connect to (string) @param port port to connect to (integer) """ self.__initialConnection = Connection(self) self.__newConnection(self.__initialConnection) - self.__initialConnection.participants.connect( - self.__processParticipants) + self.__initialConnection.participants.connect(self.__processParticipants) self.__initialConnection.connectToHost(host, port) - + def __getParticipants(self, reqConnection): """ Private slot to handle the request for a list of participants. - + @param reqConnection reference to the connection to get participants for @type Connection @@ -281,36 +273,37 @@ for connectionList in self.__peers.values(): for connection in connectionList: if connection != reqConnection: - participants.append("{0}@{1}".format( - connection.peerAddress().toString(), - connection.serverPort())) + participants.append( + "{0}@{1}".format( + connection.peerAddress().toString(), connection.serverPort() + ) + ) reqConnection.sendParticipants(participants) - + def __processParticipants(self, participants): """ Private slot to handle the receipt of a list of participants. - + @param participants list of participants (list of strings of "host:port") """ for participant in participants: host, port = participant.split("@") port = int(port) - + if port == 0: - msg = self.tr("Illegal address: {0}@{1}\n").format( - host, port) + msg = self.tr("Illegal address: {0}@{1}\n").format(host, port) self.connectionError.emit(msg) else: if not self.hasConnection(QHostAddress(host), port): connection = Connection(self) self.__newConnection(connection) connection.connectToHost(host, port) - + def sendEditorCommand(self, projectHash, filename, message): """ Public method to send an editor command. - + @param projectHash hash of the project (string) @param filename project relative universal file name of the sending editor (string) @@ -319,11 +312,11 @@ for connectionList in self.__peers.values(): for connection in connectionList: connection.sendEditorCommand(projectHash, filename, message) - + def __findConnections(self, nick): """ Private method to get a list of connection given a nick name. - + @param nick nick name in the format of self.nickName() (string) @return list of references to the connection objects (list of Connection) @@ -331,28 +324,28 @@ if "@" not in nick: # nick given in wrong format return [] - + user, host, port = nick.split("@") senderIp = QHostAddress(host) - + if senderIp not in self.__peers: return [] - + return self.__peers[senderIp][:] - + def kickUser(self, nick): """ Public method to kick a user by its nick name. - + @param nick nick name in the format of self.nickName() (string) """ for connection in self.__findConnections(nick): connection.abort() - + def banUser(self, nick): """ Public method to ban a user by its nick name. - + @param nick nick name in the format of self.nickName() (string) """ Preferences.syncPreferences() @@ -361,20 +354,20 @@ if user not in bannedUsers: bannedUsers.append(user) Preferences.setCooperation("BannedUsers", bannedUsers) - + def banKickUser(self, nick): """ Public method to ban and kick a user by its nick name. - + @param nick nick name in the format of self.nickName() (string) """ self.banUser(nick) self.kickUser(nick) - + def startListening(self, port=-1): """ Public method to start listening for new connections. - + @param port port to listen on (integer) @return tuple giving a flag indicating success (boolean) and the port the server listens on @@ -392,20 +385,20 @@ else: res = False self.__serversErrorString = self.tr("No servers present.") - + if res: self.__serversErrorString = "" self.__listening = res return res, port - + def isListening(self): """ Public method to check, if the client is listening for connections. - + @return flag indicating the listening state (boolean) """ return self.__listening - + def close(self): """ Public method to close all connections and stop listening. @@ -413,12 +406,12 @@ for server in self.__servers: server.close() self.__listening = False - + def errorString(self): """ Public method to get a human readable error message about the last server error. - + @return human readable error message about the last server error (string) """