diff --git a/CHANGELOG.md b/CHANGELOG.md index e534ee9..81f7dee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,24 @@ # Changelog (This document uses YYYY-MM-DD) +## [v2.4.1] - 2022-07-05 + +### Added + - Support for more IRC replies/commands that imply a (forced) nick/handle change. + +### Fixed + - Connecting dialog not disappearing when theme was changed. + - Client not sending QUIT on manual reconnect or exit via tray. + - Manual reconnect occasionally failing. + +### Changed + - LoadingScreen/connecting dialog is no longer blocking. + - Console output is less verbose. + +### Deprecated + - Removed the weird metadata support timeout thingy, I think ought to just get the full 005 before we try to get moods. + - Replaced instances of socket.error excepts with OSError, since it's depreciated. + ## [v2.4] - 2022-06-30 ### Added diff --git a/irc.py b/irc.py index 7615e57..311504d 100644 --- a/irc.py +++ b/irc.py @@ -46,16 +46,14 @@ class PesterIRC(QtCore.QThread): self.NickServ = services.NickServ() self.ChanServ = services.ChanServ() def IRCConnect(self): - server = self.config.server() - port = self.config.port() - ssl = self.config.ssl() self.cli = IRCClient(PesterHandler, - host=server, - port=port, + host=self.config.server(), + port=self.config.port(), + ssl=self.config.ssl(), nick=self.mainwindow.profile().handle, - real_name='pcc31', - timeout=120, - ssl=ssl) + username='pcc31', + realname='pcc31', + timeout=120) self.cli.command_handler.parent = self self.cli.command_handler.mainwindow = self.mainwindow self.cli.connect() @@ -90,8 +88,8 @@ class PesterIRC(QtCore.QThread): self.connected.emit() def setConnectionBroken(self): PchumLog.critical("setconnection broken") - self.reconnectIRC() - #self.brokenConnection = True + self.disconnectIRC() + #self.brokenConnection = True # Unused @QtCore.pyqtSlot() def updateIRC(self): try: @@ -101,19 +99,17 @@ class PesterIRC(QtCore.QThread): return True else: raise se - except socket.error as se: + except OSError as se: raise se - except (OSError, IndexError) as se: + except (OSError, + ValueError, + IndexError) as se: raise se except StopIteration: self.conn = self.cli.conn() return True else: return res - @QtCore.pyqtSlot() - def reconnectIRC(self): - PchumLog.warning("reconnectIRC() from thread %s" % (self)) - self.cli.close() @QtCore.pyqtSlot(PesterProfile) def getMood(self, *chums): @@ -127,7 +123,7 @@ class PesterIRC(QtCore.QThread): t = str(text) try: helpers.notice(self.cli, h, t) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString, QString) @@ -174,7 +170,7 @@ class PesterIRC(QtCore.QThread): try: for t in textl: helpers.msg(self.cli, h, t) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString, QString,) @@ -184,7 +180,7 @@ class PesterIRC(QtCore.QThread): #msg = msg.replace(cmd, '') try: helpers.ctcp(self.cli, handle, text) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString, bool) @@ -194,7 +190,7 @@ class PesterIRC(QtCore.QThread): helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) if initiated: helpers.msg(self.cli, h, "PESTERCHUM:BEGIN") - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -202,7 +198,7 @@ class PesterIRC(QtCore.QThread): h = str(handle) try: helpers.msg(self.cli, h, "PESTERCHUM:CEASE") - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot() @@ -211,7 +207,7 @@ class PesterIRC(QtCore.QThread): handle = me.handle try: helpers.nick(self.cli, handle) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() self.mainwindow.closeConversations(True) @@ -225,13 +221,13 @@ class PesterIRC(QtCore.QThread): # Moods via metadata try: helpers.metadata(self.cli, '*', "set", "mood", str(me.mood.value())) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() # Backwards compatibility try: helpers.msg(self.cli, "#pesterchum", "MOOD >%d" % (me.mood.value())) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot() @@ -241,7 +237,7 @@ class PesterIRC(QtCore.QThread): for h in list(self.mainwindow.convos.keys()): try: helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -249,7 +245,7 @@ class PesterIRC(QtCore.QThread): h = str(handle) try: helpers.msg(self.cli, h, "PESTERCHUM:BLOCK") - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -257,7 +253,7 @@ class PesterIRC(QtCore.QThread): h = str(handle) try: helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK") - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -265,14 +261,14 @@ class PesterIRC(QtCore.QThread): c = str(channel) try: helpers.names(self.cli, c) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot() def requestChannelList(self): try: helpers.channel_list(self.cli) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -281,7 +277,7 @@ class PesterIRC(QtCore.QThread): try: helpers.join(self.cli, c) helpers.mode(self.cli, c, "", None) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -290,7 +286,7 @@ class PesterIRC(QtCore.QThread): try: helpers.part(self.cli, c) self.cli.command_handler.joined = False - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString, QString) @@ -307,7 +303,7 @@ class PesterIRC(QtCore.QThread): reason = "" try: helpers.kick(self.cli, h, c, reason) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString, QString, QString) @@ -320,7 +316,7 @@ class PesterIRC(QtCore.QThread): cmd = None try: helpers.mode(self.cli, c, m, cmd) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString) @@ -328,7 +324,7 @@ class PesterIRC(QtCore.QThread): c = str(channel) try: helpers.names(self.cli, c) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @QtCore.pyqtSlot(QString, QString) @@ -337,7 +333,7 @@ class PesterIRC(QtCore.QThread): c = str(channel) try: helpers.invite(self.cli, h, c) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @@ -345,7 +341,7 @@ class PesterIRC(QtCore.QThread): def pingServer(self): try: self.cli.send("PING %s" % int(time.time())) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @@ -356,7 +352,7 @@ class PesterIRC(QtCore.QThread): self.cli.send("AWAY Idle") else: self.cli.send("AWAY") - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @@ -366,12 +362,16 @@ class PesterIRC(QtCore.QThread): h = str(handle) try: helpers.ctcp(self.cli, c, "NOQUIRKS", h) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.setConnectionBroken() - - def quit_dc(self): - helpers.quit(self.cli, _pcVersion + " <3") + + @QtCore.pyqtSlot() + def disconnectIRC(self): + if hasattr(self, 'cli'): + helpers.quit(self.cli, _pcVersion + " <3") + self.cli._end = True + self.cli.close() moodUpdated = QtCore.pyqtSignal('QString', Mood) colorUpdated = QtCore.pyqtSignal('QString', QtGui.QColor) @@ -383,6 +383,7 @@ class PesterIRC(QtCore.QThread): namesReceived = QtCore.pyqtSignal('QString', PesterList) channelListReceived = QtCore.pyqtSignal(PesterList) nickCollision = QtCore.pyqtSignal('QString', 'QString') + getSvsnickedOn = QtCore.pyqtSignal('QString', 'QString') myHandleChanged = QtCore.pyqtSignal('QString') chanInviteOnly = QtCore.pyqtSignal('QString') modesUpdated = QtCore.pyqtSignal('QString', 'QString') @@ -450,8 +451,8 @@ class PesterHandler(DefaultCommandHandler): if (x != None) and (x != ''): reason += x + ' ' self.parent.stopIRC = reason.strip() - self.parent.reconnectIRC() - + self.parent.disconnectIRC() + def privmsg(self, nick, chan, msg): handle = nick[0:nick.find("!")] if len(msg) == 0: @@ -559,7 +560,7 @@ class PesterHandler(DefaultCommandHandler): if (x != None) and (x != ''): reason += x + ' ' self.parent.stopIRC = reason.strip() - self.parent.reconnectIRC() + self.parent.disconnectIRC() def keyvalue(self, target, handle_us, handle_owner, key, visibility, *value): # The format of the METADATA server notication is: @@ -577,7 +578,7 @@ class PesterHandler(DefaultCommandHandler): chumglub = "GETMOOD " try: helpers.msg(self.client, "#pesterchum", chumglub + failed_handle) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() @@ -587,7 +588,7 @@ class PesterHandler(DefaultCommandHandler): chumglub = "GETMOOD " try: helpers.msg(self.client, "#pesterchum", chumglub + failed_handle) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() @@ -597,11 +598,12 @@ class PesterHandler(DefaultCommandHandler): chumglub = "GETMOOD " try: helpers.msg(self.client, "#pesterchum", chumglub + failed_handle) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() def featurelist(self, target, handle, *params): + # Better to do this via CAP ACK/CAP NEK # RPL_ISUPPORT features = params[:-1] PchumLog.info("Server featurelist: " + str(features)) @@ -609,12 +611,23 @@ class PesterHandler(DefaultCommandHandler): if x.upper().startswith("METADATA"): PchumLog.info("Server supports metadata.") self.parent.metadata_supported = True + + def cap(self, server, nick, subcommand, tag): + PchumLog.info("CAP %s %s %s %s" % (server, nick, subcommand, tag)) + #if tag == "message-tags": + # if subcommand == "ACK": + def nicknameinuse(self, server, cmd, nick, msg): newnick = "pesterClient%d" % (random.randint(100,999)) helpers.nick(self.client, newnick) self.parent.nickCollision.emit(nick, newnick) - + + def nickcollision(self, server, cmd, nick, msg): + newnick = "pesterClient%d" % (random.randint(100,999)) + helpers.nick(self.client, newnick) + self.parent.nickCollision.emit(nick, newnick) + def quit(self, nick, reason): handle = nick[0:nick.find("!")] PchumLog.info("---> recv \"QUIT %s: %s\"" % (handle, reason)) @@ -730,7 +743,13 @@ class PesterHandler(DefaultCommandHandler): #except IndexError: #self.parent.userPresentUpdate.emit("", channel, m+":%s" % (op)) - def nick(self, oldnick, newnick): + def nick(self, oldnick, newnick, hopcount=0): + # NICK from/to us + if oldnick == self.mainwindow.profile().handle: + # Server changed our handle, svsnick? + self.parent.getSvsnickedOn.emit(oldnick, newnick) + + # NICK from/to someone else oldhandle = oldnick[0:oldnick.find("!")] if oldhandle == self.mainwindow.profile().handle: self.parent.myHandleChanged.emit(newnick) @@ -824,26 +843,8 @@ class PesterHandler(DefaultCommandHandler): self.client.send('PONG', server) def getMood(self, *chums): - # Try to get mood via metadata get. - # If it fails the old code is excecuted. + """Get mood via metadata if supported""" - # If services/bot, assume mood 18. - # * This doesn't actually seem to work. - #for c in chums: - # if c.handle.upper() in BOTNAMES: - # print("True") - # print(c.handle) - # PchumLog.info("%s is a bot, setting mood to 18." % (c.handle)) - # self.parent.moodUpdated.emit(c.handle, Mood(18)) - # Wait for server to send welcome to verify RPL_ISUPPORT has been send. - # Apparently 005 is send after 001 so nvm we gotta wait longer :"3 - timeout = 0 - while ((self.parent.registeredIRC == False) - or ((timeout < 15) - and (self.parent.metadata_supported == False))): - time.sleep(0.1) - timeout += 1 - # Get via metadata or via legacy method if self.parent.metadata_supported == True: # Metadata @@ -851,7 +852,7 @@ class PesterHandler(DefaultCommandHandler): chandle = c.handle try: helpers.metadata(self.client, chandle, "get", "mood") - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() else: @@ -863,7 +864,7 @@ class PesterHandler(DefaultCommandHandler): if len(chumglub+chandle) >= 350: try: helpers.msg(self.client, "#pesterchum", chumglub) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() chumglub = "GETMOOD " @@ -871,7 +872,7 @@ class PesterHandler(DefaultCommandHandler): if chumglub != "GETMOOD ": try: helpers.msg(self.client, "#pesterchum", chumglub) - except socket.error as e: + except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() @@ -883,12 +884,12 @@ class PesterHandler(DefaultCommandHandler): # if len(chandle) >= 200: # try: # self.client.send("ISON", ":%s" % (isonNicks)) - # except socket.error: + # except OSError: # self.parent.setConnectionBroken() # isonNicks = "" # isonNicks += " " + chandle # if isonNicks != "": # try: # self.client.send("ISON", ":%s" % (isonNicks)) - # except socket.error: + # except OSError: # self.parent.setConnectionBroken() diff --git a/menus.py b/menus.py index 9441aa8..63fd88f 100644 --- a/menus.py +++ b/menus.py @@ -791,7 +791,7 @@ class PesterChooseTheme(QtWidgets.QDialog): self.rejected.connect(parent.closeTheme) class PesterChooseProfile(QtWidgets.QDialog): - def __init__(self, userprofile, config, theme, parent, collision=None): + def __init__(self, userprofile, config, theme, parent, collision=None, svsnick=None): QtWidgets.QDialog.__init__(self, parent) self.userprofile = userprofile self.theme = theme @@ -848,6 +848,9 @@ class PesterChooseProfile(QtWidgets.QDialog): if collision: collision_warning = QtWidgets.QLabel("%s is taken already! Pick a new profile." % (collision)) layout_0.addWidget(collision_warning) + elif svsnick != None: + svsnick_warning = QtWidgets.QLabel("Your handle got changed from %s to %s! Pick a new profile." % svsnick) + layout_0.addWidget(svsnick_warning) else: layout_0.addWidget(self.currentHandle, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter) layout_0.addLayout(layout_1) @@ -1753,6 +1756,8 @@ class LoadingScreen(QtWidgets.QDialog): self.mainwindow = parent self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) + #self.setWindowModality(QtCore.Qt.WindowModality.NonModal) # useless + #self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose) # useless self.loadinglabel = QtWidgets.QLabel("CONN3CT1NG", self) self.cancel = QtWidgets.QPushButton("QU1T >:?", self) self.ok = QtWidgets.QPushButton("R3CONN3CT >:]", self) @@ -1761,6 +1766,7 @@ class LoadingScreen(QtWidgets.QDialog): self.ok.setAutoDefault(True) self.cancel.clicked.connect(self.reject) self.ok.clicked.connect(self.tryAgain) + #self.finished.connect(self.finishedEvent) self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.loadinglabel) @@ -1774,6 +1780,9 @@ class LoadingScreen(QtWidgets.QDialog): self.ok.setDefault(True) self.ok.setFocus() self.timer = None + + #def finishedEvent(self, result): + # self.close() def hideReconnect(self, safe=True): self.ok.hide() @@ -1792,8 +1801,6 @@ class LoadingScreen(QtWidgets.QDialog): def enableQuit(self): self.cancel.setEnabled(True) - tryAgain = QtCore.pyqtSignal() - class AboutPesterchum(QtWidgets.QDialog): def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) diff --git a/oyoyo/client.py b/oyoyo/client.py index a3dc528..0b78dff 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -82,7 +82,8 @@ class IRCClient: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.nick = None - self.real_name = None + self.realname = None + self.username = None self.host = None self.port = None self.connect_cb = None @@ -131,6 +132,12 @@ class IRCClient: try: tries = 1 while tries < 10: + # Check if alive + if self._end == True: + break + if self.socket.fileno() == -1: + self._end = True + break try: ready_to_read, ready_to_write, in_error = select.select([], [self.socket], []) for x in ready_to_write: @@ -156,9 +163,8 @@ class IRCClient: raise socket.timeout except (OSError, IndexError, - ValueError) as e: - # Unknown error, might as well retry? - # index/value can happen if the socket breaks. + ValueError, + Exception) as e: PchumLog.warning("Unkown error on send, " + str(e)) if tries >= 9: raise e @@ -168,7 +174,7 @@ class IRCClient: PchumLog.debug("ready_to_write (len %s): " % str(len(ready_to_write)) + str(ready_to_write)) except Exception as se: - PchumLog.warning("socket.error %s" % str(se)) + PchumLog.warning("Send Exception %s" % str(se)) try: if not self.blocking and se.errno == 11: pass @@ -190,7 +196,7 @@ class IRCClient: context.check_hostname = False context.verify_mode = ssl.CERT_NONE - bare_sock = socket.create_connection(("%s" % self.host, self.port)) + bare_sock = socket.create_connection((self.host, self.port)) self.socket = context.wrap_socket(bare_sock, server_hostname=self.host, do_handshake_on_connect=False) @@ -206,7 +212,7 @@ class IRCClient: PchumLog.info("secure sockets version is %s" % self.socket.version()) else: - self.socket = socket.create_connection(("%s" % self.host, self.port)) + self.socket = socket.create_connection((self.host, self.port)) # setblocking is a shorthand for timeout, # we shouldn't use both. @@ -218,7 +224,7 @@ class IRCClient: self.socket.setblocking(True) helpers.nick(self, self.nick) - helpers.user(self, self.nick, self.real_name) + helpers.user(self, self.username, self.realname) if self.connect_cb: self.connect_cb(self) @@ -231,6 +237,12 @@ class IRCClient: try: tries = 1 while tries < 10: + # Check if alive + if self._end == True: + break + if self.socket.fileno() == -1: + self._end = True + break try: ready_to_read, ready_to_write, in_error = select.select([self.socket], [], []) for x in ready_to_read: @@ -249,14 +261,16 @@ class IRCClient: except ssl.SSLEOFError as e: # ssl.SSLEOFError guarantees a broken connection. PchumLog.warning("ssl.SSLEOFError in on send, " + str(e)) - raise ssl.SSLEOFError + raise e except (socket.timeout, TimeoutError) as e: # socket.timeout is deprecated in 3.10 PchumLog.warning("TimeoutError in on send, " + str(e)) raise socket.timeout - except (OSError, IndexError) as e: - # Unknown error, might as well retry? - PchumLog.warning("Unkown error, " + str(e)) + except (OSError, + IndexError, + ValueError, + Exception) as e: + PchumLog.warning("Unkown error in conn, " + str(e)) if tries >= 9: raise e tries += 1 @@ -268,8 +282,8 @@ class IRCClient: if self._end: break raise e - except ssl.SSLEOFError: - raise ssl.SSLEOFError + except ssl.SSLEOFError as e: + raise e except OSError as e: PchumLog.warning("conn exception %s in %s" % (e, self)) if self._end: @@ -287,7 +301,7 @@ class IRCClient: break if len(buffer) == 0 and self.blocking: PchumLog.debug("len(buffer) = 0") - raise socket.error("Connection closed") + raise OSError("Connection closed") data = buffer.split(bytes("\n", "UTF-8")) buffer = data.pop() @@ -303,13 +317,13 @@ class IRCClient: else: self.command_handler.run(command, prefix, *args) except CommandError as e: - PchumLog.debug("CommandError %s" % str(e)) + PchumLog.warning("CommandError %s" % str(e)) yield True except socket.timeout as se: PchumLog.debug("passing timeout") raise se - except (socket.error, ssl.SSLEOFError) as se: + except (OSError, ssl.SSLEOFError) as se: PchumLog.debug("problem: %s" % (str(se))) if self.socket: PchumLog.info('error: closing socket') @@ -333,15 +347,11 @@ class IRCClient: try: self.socket.shutdown(socket.SHUT_RDWR) except OSError as e: - PchumLog.warning("Error while shutting down socket, already broken? %s" % str(e)) + PchumLog.debug("Error while shutting down socket, already broken? %s" % str(e)) try: self.socket.close() except OSError as e: - PchumLog.warning("Error while closing socket, already broken? %s" % str(e)) - - def quit(self, msg): - PchumLog.info("QUIT") - self.socket.sendall(bytes(msg + "\n", "UTF-8")) + PchumLog.debug("Error while closing socket, already broken? %s" % str(e)) class IRCApp: """ This class manages several IRCClient instances without the use of threads. diff --git a/oyoyo/cmdhandler.py b/oyoyo/cmdhandler.py index 8cc260c..cdecb4b 100644 --- a/oyoyo/cmdhandler.py +++ b/oyoyo/cmdhandler.py @@ -97,7 +97,8 @@ class CommandHandler(object): try: f = self.get(command) - except NoSuchCommandError: + except NoSuchCommandError as e: + PchumLog.info(e) self.__unhandled__(command, *args) return @@ -106,7 +107,9 @@ class CommandHandler(object): try: f(*args) except TypeError as e: - logging.exception("Failed to pass command, did the server pass an unsupported paramater?\n%s" % str(e)) + logging.warning("Failed to pass command, did the server pass an unsupported paramater? " + str(e)) + except Exception as e: + logging.warning("Failed to pass command, %s" % str(e)) @protected def __unhandled__(self, cmd, *args): diff --git a/oyoyo/helpers.py b/oyoyo/helpers.py index 1012aee..e57f2fb 100644 --- a/oyoyo/helpers.py +++ b/oyoyo/helpers.py @@ -101,14 +101,14 @@ def identify(cli, passwd, authuser="NickServ"): msg(cli, authuser, "IDENTIFY %s" % passwd) def quit(cli, msg): - msg = "QUIT :%s" % msg - cli.quit(msg) - cli.close() - cli._end = 1 + cli.send("QUIT %s" % (msg)) -def user(cli, username, realname=None): - cli.send("USER", realname or username, cli.host, cli.host, - realname or username) +def user(cli, username, realname): + cli.send("USER", + username, + '0', + '*', + ':' + realname) _simple = ( 'join', @@ -140,4 +140,3 @@ def _addNumerics(): setattr(m, name, numericcmd(num, name)) _addNumerics() - diff --git a/oyoyo/ircevents.py b/oyoyo/ircevents.py index 947684b..9041f82 100644 --- a/oyoyo/ircevents.py +++ b/oyoyo/ircevents.py @@ -223,8 +223,10 @@ protocol_events = [ "quit", "invite", "pong", - "metadata", # Metadata specification - "tagmsg", # IRCv3 message tags extension + "nick", # We can get svsnicked + "metadata", # Metadata specification + "tagmsg", # IRCv3 message tags extension + "cap" # IRCv3 Client Capability Negotiation ] all_events = (generated_events diff --git a/oyoyo/parse.py b/oyoyo/parse.py index 15525aa..b0fa6c0 100644 --- a/oyoyo/parse.py +++ b/oyoyo/parse.py @@ -26,7 +26,6 @@ logging.config.fileConfig(_datadir + "logging.ini") PchumLog = logging.getLogger('pchumLogger') def parse_raw_irc_command(element): - print(element) """ This function parses a raw irc command and returns a tuple of (prefix, command, args). diff --git a/parsetools.py b/parsetools.py index 21be784..6720540 100644 --- a/parsetools.py +++ b/parsetools.py @@ -704,14 +704,7 @@ def kxhandleInput(ctx, text=None, flavor=None): msgbox.exec() return - # Debug output. - try: - # Turns out that Windows consoles can't handle unicode, heh...who'da - # thunk. We have to repr() this, as such. - print(repr(msg)) - except Exception as err: - print("(Couldn't print processed message: {!s})".format(err)) - + PchumLog.info("--> recv \"%s\"" % msg) # karxi: We have a list...but I'm not sure if we ever get anything else, so # best to play it safe. I may remove this during later refactoring. if isinstance(msg, list): @@ -728,10 +721,10 @@ def kxhandleInput(ctx, text=None, flavor=None): msg = kxlexMsg(msg) # Debug output. - try: - print(repr(msg)) - except Exception as err: - print("(Couldn't print lexed message: {!s})".format(err)) + #try: + # print(repr(msg)) + #except Exception as err: + # print("(Couldn't print lexed message: {!s})".format(err)) # Remove coloring if this is a /me! if is_action: diff --git a/pesterchum.py b/pesterchum.py index 16cc02d..afa5725 100644 --- a/pesterchum.py +++ b/pesterchum.py @@ -1171,7 +1171,7 @@ class TrollSlumWindow(QtWidgets.QFrame): unblockChumSignal = QtCore.pyqtSignal('QString') class PesterWindow(MovingWindow): - reconnectIRC = QtCore.pyqtSignal() + disconnectIRC = QtCore.pyqtSignal() sendMessage = QtCore.pyqtSignal('QString', 'QString') def __init__(self, options, parent=None, app=None): @@ -1292,7 +1292,7 @@ class PesterWindow(MovingWindow): opts.triggered.connect(self.openOpts) exitaction = QtGui.QAction(self.theme["main/menus/client/exit"], self) self.exitaction = exitaction - exitaction.triggered.connect(self.quit) + exitaction.triggered.connect(self.killApp, QtCore.Qt.ConnectionType.QueuedConnection) userlistaction = QtGui.QAction(self.theme["main/menus/client/userlist"], self) self.userlistaction = userlistaction userlistaction.triggered.connect(self.showAllUsers) @@ -1305,7 +1305,7 @@ class PesterWindow(MovingWindow): self.idleaction.setCheckable(True) self.idleaction.toggled[bool].connect(self.toggleIdle) self.reconnectAction = QtGui.QAction(self.theme["main/menus/client/reconnect"], self) - self.reconnectAction.triggered.connect(self.reconnectIRC) + self.reconnectAction.triggered.connect(self.disconnectIRC) self.menu = QtWidgets.QMenuBar(self) self.menu.setNativeMenuBar(False) @@ -1583,7 +1583,6 @@ class PesterWindow(MovingWindow): self.hide() self.closeToTraySignal.emit() def closeEvent(self, event): - # This gets called directly if Pesterchum is closed from the taskbar, annoyingly enough. if hasattr(self, 'trollslum') and self.trollslum: self.trollslum.close() try: @@ -1597,8 +1596,7 @@ class PesterWindow(MovingWindow): self.closeToTray() elif setting == 2: # quit self.closeConversations() - self.quit() # Shut down IRC & Pesterchum fully. - self.closeSignal.emit() # <-- A close signal here on it's own shuts down QT but not the Python and the IRC connection :( + self.closeSignal.emit() event.accept() def newMessage(self, handle, msg): if handle in self.config.getBlocklist(): @@ -1863,11 +1861,16 @@ class PesterWindow(MovingWindow): self.chumList.showOnlineNumbers() - def changeProfile(self, collision=None): + def changeProfile(self, collision=None, svsnick=None): if not hasattr(self, 'chooseprofile'): self.chooseprofile = None if not self.chooseprofile: - self.chooseprofile = PesterChooseProfile(self.userprofile, self.config, self.theme, self, collision=collision) + self.chooseprofile = PesterChooseProfile(self.userprofile, + self.config, + self.theme, + self, + collision=collision, + svsnick=svsnick) self.chooseprofile.exec() def themePicker(self): @@ -2338,7 +2341,7 @@ class PesterWindow(MovingWindow): def userPresentUpdate(self, handle, channel, update): c = str(channel) n = str(handle) - print("c=%s\nn=%s\nupdate=%s\n" % (c, n, update)) + #print("c=%s\nn=%s\nupdate=%s\n" % (c, n, update)) if update == "nick": l = n.split(":") oldnick = l[0] @@ -3180,6 +3183,11 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot(QString, QString) def nickCollision(self, handle, tmphandle): + if hasattr(self, 'loadingscreen'): + if self.loadingscreen != None: + self.loadingscreen.done(QtWidgets.QDialog.DialogCode.Accepted) + self.loadingscreen = None + self.mychumhandle.setText(tmphandle) self.userprofile = userProfile(PesterProfile("pesterClient%d" % (random.randint(100,999)), QtGui.QColor("black"), Mood(0))) self.changeTheme(self.userprofile.getTheme()) @@ -3189,6 +3197,24 @@ class PesterWindow(MovingWindow): if not self.chooseprofile: h = str(handle) self.changeProfile(collision=h) + + @QtCore.pyqtSlot(QString, QString) + def getSvsnickedOn(self, oldhandle, newhandle): + if hasattr(self, 'loadingscreen'): + if self.loadingscreen != None: + self.loadingscreen.done(QtWidgets.QDialog.DialogCode.Accepted) + self.loadingscreen = None + + self.mychumhandle.setText(newhandle) + self.userprofile = userProfile(PesterProfile(newhandle, + QtGui.QColor("black"), + Mood(0))) + self.changeTheme(self.userprofile.getTheme()) + + if not hasattr(self, 'chooseprofile'): + self.chooseprofile = None + if not self.chooseprofile: + self.changeProfile(svsnick=(oldhandle, newhandle)) @QtCore.pyqtSlot(QString) def myHandleChanged(self, handle): if self.profile().handle == handle: @@ -3223,28 +3249,17 @@ class PesterWindow(MovingWindow): self.forbiddenChan.emit(channel, reason) @QtCore.pyqtSlot() - def quit(self): - try: - self.irc.quit_dc() # Actually send QUIT to server - except Exception as e: - # Not connected? - PchumLog.warning("QUIT failed: " + str(e)) - try: - self.parent.trayicon.hide() # - self.app.quit() - except AttributeError as e: - # Called from outside main Window? - PchumLog.warning("Unelegant quit: " + str(e)) - sys.exit() + def killApp(self): + self.disconnectIRC.emit() + self.parent.trayicon.hide() + self.app.quit() def passIRC(self, irc): self.irc = irc def updateServerJson(self): PchumLog.info(self.customServerPrompt_qline.text() + " chosen") - server_and_port = self.customServerPrompt_qline.text().split(':') - try: server = { "server": server_and_port[0], @@ -3539,7 +3554,7 @@ class PesterWindow(MovingWindow): # Connect self.chooseServerWidged.accepted.connect(self.setServer) - self.chooseServerWidged.rejected.connect(self.quit) + self.chooseServerWidged.rejected.connect(self.killApp, QtCore.Qt.ConnectionType.QueuedConnection) # Show self.chooseServerWidged.show() @@ -3575,7 +3590,7 @@ class PesterWindow(MovingWindow): inviteOnlyChan = QtCore.pyqtSignal('QString') forbiddenChan = QtCore.pyqtSignal('QString', 'QString') closeSignal = QtCore.pyqtSignal() - reconnectIRC = QtCore.pyqtSignal() + disconnectIRC = QtCore.pyqtSignal() gainAttention = QtCore.pyqtSignal(QtWidgets.QWidget) pingServer = QtCore.pyqtSignal() setAway = QtCore.pyqtSignal(bool) @@ -3627,7 +3642,7 @@ class MainProgram(QtCore.QObject): self.app = QtWidgets.QApplication(sys.argv) self.app.setApplicationName("Pesterchum") - self.app.setQuitOnLastWindowClosed(False) + #self.app.setQuitOnLastWindowClosed(False) options = self.oppts(sys.argv[1:]) @@ -3678,7 +3693,7 @@ class MainProgram(QtCore.QObject): miniAction = QtGui.QAction("MINIMIZE", self) miniAction.triggered.connect(self.widget.showMinimized) exitAction = QtGui.QAction("EXIT", self) - exitAction.triggered.connect(PesterWindow.quit) + exitAction.triggered.connect(self.widget.killApp, QtCore.Qt.ConnectionType.QueuedConnection) self.traymenu.addAction(miniAction) self.traymenu.addAction(exitAction) @@ -3698,8 +3713,21 @@ class MainProgram(QtCore.QObject): self.widget.passIRC(self.irc) # Maybe this is absolutely terrible in practice, but screw it. self.widget.gainAttention[QtWidgets.QWidget].connect(self.alertWindow) - + #self.app.lastWindowClosed.connect(self.lastWindow) + self.app.aboutToQuit.connect(self.death) + + def death(self): + # app murder in progress + #print("death inbound") + if hasattr(self, 'widget'): + self.widget.killApp() + + #def lastWindow(self): + # print("all windows closed") + # if hasattr(self, 'widget'): + # self.widget.killApp() + @QtCore.pyqtSlot(QtWidgets.QWidget) def alertWindow(self, widget): self.app.alert(widget) @@ -3708,9 +3736,8 @@ class MainProgram(QtCore.QObject): def trayiconShow(self): self.trayicon.show() if self.widget.config.trayMessage(): - self.trayicon.showMessage("Pesterchum", "Pesterchum is still running in the system tray.\n\ -Right click to close it.\n\ -Click this message to never see this again.") + self.trayicon.showMessage("Pesterchum", ("Pesterchum is still running in the system tray." + + '\n' + "Right click to close it.")) @QtCore.pyqtSlot() def trayMessageClick(self): @@ -3752,7 +3779,7 @@ Click this message to never see this again.") ('setAway(bool)', 'setAway(bool)'), ('killSomeQuirks(QString, QString)', 'killSomeQuirks(QString, QString)'), - ('reconnectIRC()', 'reconnectIRC()') + ('disconnectIRC()', 'disconnectIRC()') ] # IRC --> Main window irc2widget = [('connected()', 'connected()'), @@ -3770,6 +3797,8 @@ Click this message to never see this again.") 'deliverInvite(QString, QString)'), ('nickCollision(QString, QString)', 'nickCollision(QString, QString)'), + ('getSvsnickedOn(QString, QString)', + 'getSvsnickedOn(QString, QString)'), ('myHandleChanged(QString)', 'myHandleChanged(QString)'), ('namesReceived(QString, PyQt_PyObject)', @@ -3818,7 +3847,7 @@ Click this message to never see this again.") (widget.pingServer, irc.pingServer), (widget.setAway, irc.setAway), (widget.killSomeQuirks, irc.killSomeQuirks), - (widget.reconnectIRC, irc.reconnectIRC), + (widget.disconnectIRC, irc.disconnectIRC), # Main window --> IRC (irc.connected, widget.connected), (irc.moodUpdated, widget.updateMoodSlot), @@ -3828,6 +3857,7 @@ Click this message to never see this again.") (irc.noticeReceived, widget.deliverNotice), (irc.inviteReceived, widget.deliverInvite), (irc.nickCollision, widget.nickCollision), + (irc.getSvsnickedOn, widget.getSvsnickedOn), (irc.myHandleChanged, widget.myHandleChanged), (irc.namesReceived, widget.updateNames), (irc.userPresentUpdate, widget.userPresentUpdate), @@ -3887,17 +3917,7 @@ Click this message to never see this again.") widget.loadingscreen.showReconnect() else: widget.loadingscreen.hideReconnect() - status = widget.loadingscreen.exec() - if status == QtWidgets.QDialog.DialogCode.Rejected: - sys.exit(0) - else: - if self.widget.tabmemo: - for c in self.widget.tabmemo.convos: - self.irc.joinChannel(c) - else: - for c in list(self.widget.memos.values()): - self.irc.joinChannel(c.channel) - return True + widget.loadingscreen.open() @QtCore.pyqtSlot() def connected(self): @@ -3911,8 +3931,7 @@ Click this message to never see this again.") self.widget.loadingscreen = None self.attempts += 1 if hasattr(self, 'irc') and self.irc: - self.irc.reconnectIRC() - self.irc.quit() + self.irc.disconnectIRC() else: self.restartIRC() @QtCore.pyqtSlot() diff --git a/toast.py b/toast.py index a687ef1..a01f742 100644 --- a/toast.py +++ b/toast.py @@ -189,7 +189,6 @@ class ToastMachine(object): class PesterToast(QtWidgets.QWidget, DefaultToast): def __init__(self, machine, title, msg, icon, time=3000, parent=None): - PchumLog.info(isinstance(parent, QtWidgets.QWidget)) kwds = dict(machine=machine, title=title, msg=msg, icon=icon) super().__init__(parent, **kwds) diff --git a/version.py b/version.py index be98876..dff77fa 100644 --- a/version.py +++ b/version.py @@ -1,2 +1,2 @@ -_pcVersion = "Alt. v2.4" -buildVersion = "v2.4" +_pcVersion = "Alt. v2.4.1" +buildVersion = "v2.4.1"