diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..a9be522 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,10 @@ +name: Black + +on: [push, pull_request] + +jobs: + black: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: psf/black@stable diff --git a/README.md b/README.md index 72d17dc..c5157aa 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@
GitHub commit activity Lines of code + Code style: black PESTERCHUM diff --git a/__main__.py b/__main__.py index 21f8383..2fee9c7 100644 --- a/__main__.py +++ b/__main__.py @@ -5,6 +5,6 @@ import sys try: from .run_as_subprocess import main except (ImportError, ModuleNotFoundError): - from run_as_subprocess import main - + from run_as_subprocess import main + main(sys.argv) diff --git a/console.py b/console.py index 4a8fd4e..b9f8d1d 100644 --- a/console.py +++ b/console.py @@ -1,7 +1,7 @@ # vim: set autoindent ts=4 sts=4 sw=4 textwidth=79 expandtab: # -*- coding=UTF-8; tab-width: 4 -*- -#import os -#from os import remove +# import os +# from os import remove import sys import traceback import time @@ -17,17 +17,20 @@ except ImportError: from PyQt5.QtWidgets import QAction import dataobjs -#import generic -#import memos -#import parsetools + +# import generic +# import memos +# import parsetools import ostools -#from version import _pcVersion + +# from version import _pcVersion from pnc.dep.attrdict import AttrDict -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + class ConsoleWindow(QtWidgets.QDialog): -#~class ConsoleWindow(styler.PesterBaseWindow): + # ~class ConsoleWindow(styler.PesterBaseWindow): # A simple console class, cobbled together from the corpse of another. stylesheet_path = "main/defaultwindow/style" @@ -127,7 +130,7 @@ class ConsoleWindow(QtWidgets.QDialog): def initTheme(self, theme): # Set up our style/window specifics self.changeTheme(theme) - self.resize(400,600) + self.resize(400, 600) def changeTheme(self, theme): self.setStyleSheet(theme[self.stylesheet_path]) @@ -150,18 +153,23 @@ class ConsoleWindow(QtWidgets.QDialog): wgt = QtWidgets.QApplication.widgetAt(pos) if wgt is None: # Don't set None, for now. May change this later. - self.addMessage("You need to have your cursor over something " + \ - "in Pesterchum to use that.", - direction=direction) + self.addMessage( + "You need to have your cursor over something " + + "in Pesterchum to use that.", + direction=direction, + ) return self.selected_widget = wgt nchild = len(wgt.children()) output = [] output.append("CONSOLE.selected_widget = {0!r}".format(wgt)) - output.append("{0: <4}Parent: {1!r}".format('', wgt.parent())) - output.append("{0: <4}{1:4d} child{2}".format('', - nchild, ("ren" if abs(nchild) != 1 else "") )) + output.append("{0: <4}Parent: {1!r}".format("", wgt.parent())) + output.append( + "{0: <4}{1:4d} child{2}".format( + "", nchild, ("ren" if abs(nchild) != 1 else "") + ) + ) if self.show_info_on_select: qtss = None uses_ss = None @@ -211,12 +219,13 @@ class ConsoleWindow(QtWidgets.QDialog): else: # We got a stylesheet out of this! uses_ss, ss_msg = True, "Yes" - #~ss_par_msg = "{0: <4}...on parent ↑{1:d}: {2!r}".format('', - ss_par_msg = "{0: <4}...on parent #{1:d}: {2!r}".format('', - i, ss_par) + # ~ss_par_msg = "{0: <4}...on parent ↑{1:d}: {2!r}".format('', + ss_par_msg = "{0: <4}...on parent #{1:d}: {2!r}".format( + "", i, ss_par + ) msg = [] - msg.append("{0: <4}QtSS?: {1}".format('', ss_msg)) + msg.append("{0: <4}QtSS?: {1}".format("", ss_msg)) # A stylesheet analyzer would be wonderful here. Perhaps something # that tells us how many parent classes define stylesheets? if uses_ss: @@ -224,16 +233,15 @@ class ConsoleWindow(QtWidgets.QDialog): # We got this stylesheet from a parent object somewhere. msg.append(ss_par_msg) msg.append("{0: <4}".format("Stylesheet:")) - for ln in qtss.split('\n'): + for ln in qtss.split("\n"): msg.append("{0: <8}".format(ln)) # Actually add this stuff to the result we're constructing output.extend(msg) - output = '\n'.join(output) + output = "\n".join(output) self.addMessage(output, direction=direction) - # Actual console stuff. def execInConsole(self, scriptstr, env=None): # Since that's what imports *us*, this should be okay @@ -247,16 +255,16 @@ class ConsoleWindow(QtWidgets.QDialog): # Fetch from the class/instance first. _CUSTOM_ENV = self._CUSTOM_ENV.copy() # Modify with some hard-coded environmental additions. - _CUSTOM_ENV.update({ + _CUSTOM_ENV.update( + { "CONSOLE": self, "MAINWIN": self.mainwindow, "PCONFIG": self.mainwindow.config, - "exit": lambda: self.mainwindow.exitaction.trigger() - }) + "exit": lambda: self.mainwindow.exitaction.trigger(), + } + ) # Aliases. - _CUSTOM_ENV.update({ - "quit": _CUSTOM_ENV["exit"] - }) + _CUSTOM_ENV.update({"quit": _CUSTOM_ENV["exit"]}) # Add whatever additions were set in the main pesterchum file. _CUSTOM_ENV.update(pchum._CONSOLE_ENV) @@ -284,7 +292,7 @@ class ConsoleWindow(QtWidgets.QDialog): # Replace the old writer (for now) sysout, sys.stdout = sys.stdout, self try: - code = compile(scriptstr + '\n', "", "single") + code = compile(scriptstr + "\n", "", "single") # Will using cenv instead of env cause problems?... result = eval(code, cenv) except: @@ -309,7 +317,7 @@ class ConsoleWindow(QtWidgets.QDialog): # We only ever use this for receiving, so it's safe to assume the # direction is always -1. if not isinstance(data, list): - data = data.split('\n') + data = data.split("\n") for line in data: if len(line): self.addMessage(line, -1) @@ -332,7 +340,7 @@ class ConsoleText(QtWidgets.QTextEdit): def __init__(self, theme, parent=None): super(ConsoleText, self).__init__(parent) - if hasattr(self.window(), 'mainwindow'): + if hasattr(self.window(), "mainwindow"): self.mainwindow = self.window().mainwindow else: self.mainwindow = self.window() @@ -362,7 +370,7 @@ class ConsoleText(QtWidgets.QTextEdit): # be too hard - it's what dicts are for. # Add the rest. - stylesheet += '\n' + self.stylesheet_template + stylesheet += "\n" + self.stylesheet_template stylesheet = stylesheet.format(style=theme) self.setStyleSheet(stylesheet) @@ -371,12 +379,12 @@ class ConsoleText(QtWidgets.QTextEdit): # Direction > 0 == out (sent by us); < 0 == in (sent by script). if len(msg) == 0: return - #~color = chum.colorcmd() - #~initials = chum.initials() + # ~color = chum.colorcmd() + # ~initials = chum.initials() parent = self.window() mwindow = parent.mainwindow - #systemColor = QtGui.QColor(mwindow.theme["convo/systemMsgColor"]) + # systemColor = QtGui.QColor(mwindow.theme["convo/systemMsgColor"]) if mwindow.config.showTimeStamps(): if mwindow.config.time12Format(): @@ -406,7 +414,7 @@ class ConsoleText(QtWidgets.QTextEdit): # Later, this will have to escape things so we don't parse them, # likely...hm. - #~result = "{} {} {!r}" + # ~result = "{} {} {!r}" # The input we get is already repr'd...we pass it via print, and thus # do that there. result = "{}{} {}\n" @@ -442,8 +450,12 @@ class ConsoleText(QtWidgets.QTextEdit): # should. # karxi: Test for tab changing? if self.window().text.input: - if event.key() not in (QtCore.Qt.Key.Key_PageUp, QtCore.Qt.Key.Key_PageDown, - QtCore.Qt.Key.Key_Up, QtCore.Qt.Key.Key_Down): + if event.key() not in ( + QtCore.Qt.Key.Key_PageUp, + QtCore.Qt.Key.Key_PageDown, + QtCore.Qt.Key.Key_Up, + QtCore.Qt.Key.Key_Down, + ): self.window().text.input.keyPressEvent(event) super(ConsoleText, self).keyPressEvent(event) @@ -463,7 +475,9 @@ class ConsoleText(QtWidgets.QTextEdit): QtWidgets.QApplication.clipboard().setText(url) else: # This'll probably be removed. May change the lexer out. - QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl(url, QtCore.QUrl.ParsingMode.TolerantMode) + ) super(ConsoleText, self).mousePressEvent(event) @@ -477,8 +491,13 @@ class ConsoleText(QtWidgets.QTextEdit): # PyQt5 pos = event.pos() if self.anchorAt(pos): - if self.viewport().cursor().shape != QtCore.Qt.CursorShape.PointingHandCursor: - self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) + if ( + self.viewport().cursor().shape + != QtCore.Qt.CursorShape.PointingHandCursor + ): + self.viewport().setCursor( + QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor) + ) else: self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor)) @@ -489,6 +508,7 @@ class ConsoleText(QtWidgets.QTextEdit): class ConsoleInput(QtWidgets.QLineEdit): """The actual text entry box on a ConsoleWindow.""" + # I honestly feel like this could just be made a private class of # ConsoleWindow, but...best not to overcomplicate things. stylesheet_path = "convo/input/style" @@ -516,7 +536,7 @@ class ConsoleInput(QtWidgets.QLineEdit): # NOTE: Do we really want everyone knowing we're around if we're # messing around in the console? Hm. parent.mainwindow.idler.time = 0 - + if evtkey == QtCore.Qt.Key.Key_Up: text = str(self.text()) next = parent.text.history.next(text) diff --git a/convo.py b/convo.py index e356c9a..c7c0f84 100644 --- a/convo.py +++ b/convo.py @@ -14,16 +14,12 @@ except ImportError: import ostools from dataobjs import PesterHistory -from parsetools import (convertTags, - lexMessage, - mecmd, - colorBegin, - colorEnd, - smiledict) +from parsetools import convertTags, lexMessage, mecmd, colorBegin, colorEnd, smiledict import parsetools from pnc.dep.attrdict import AttrDict -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + class PesterTabWindow(QtWidgets.QFrame): def __init__(self, mainwindow, parent=None, convo="convo"): @@ -41,18 +37,26 @@ class PesterTabWindow(QtWidgets.QFrame): self.shortcuts = AttrDict() self.shortcuts.tabNext = QShortcut( - QtGui.QKeySequence('Ctrl+j'), self, - context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut) + QtGui.QKeySequence("Ctrl+j"), + self, + context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut, + ) self.shortcuts.tabLast = QShortcut( - QtGui.QKeySequence('Ctrl+k'), self, - context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut) + QtGui.QKeySequence("Ctrl+k"), + self, + context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut, + ) # Note that we use reversed keys here. self.shortcuts.tabUp = QShortcut( - QtGui.QKeySequence('Ctrl+PgDown'), self, - context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut) + QtGui.QKeySequence("Ctrl+PgDown"), + self, + context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut, + ) self.shortcuts.tabDn = QShortcut( - QtGui.QKeySequence('Ctrl+PgUp'), self, - context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut) + QtGui.QKeySequence("Ctrl+PgUp"), + self, + context=QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut, + ) self.shortcuts.tabNext.activated.connect(self.nudgeTabNext) self.shortcuts.tabUp.activated.connect(self.nudgeTabNext) @@ -61,7 +65,7 @@ class PesterTabWindow(QtWidgets.QFrame): self.initTheme(self.mainwindow.theme) self.layout = QtWidgets.QVBoxLayout() - self.layout.setContentsMargins(0,0,0,0) + self.layout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(self.tabs) self.setLayout(self.layout) self.convos = {} @@ -83,6 +87,7 @@ class PesterTabWindow(QtWidgets.QFrame): self.tabs.removeTab(i) self.changedTab = False return c + def addChat(self, convo): self.convos[convo.title()] = convo # either addTab or setCurrentIndex will trigger changed() @@ -90,6 +95,7 @@ class PesterTabWindow(QtWidgets.QFrame): self.tabIndices[convo.title()] = newindex self.tabs.setCurrentIndex(newindex) self.tabs.setTabIcon(newindex, convo.icon()) + def showChat(self, handle): tabi = self.tabIndices[handle] if self.tabs.currentIndex() == tabi: @@ -98,6 +104,7 @@ class PesterTabWindow(QtWidgets.QFrame): self.convos[handle].raiseChat() else: self.tabs.setCurrentIndex(tabi) + """ There are two instances of "convoHasFocus" for some reason? This one seems to just get redefined. @@ -107,7 +114,7 @@ class PesterTabWindow(QtWidgets.QFrame): self.tabs.tabText(self.tabs.currentIndex()) == convo.title()): return True """ - + def isBot(self, *args, **kwargs): return self.mainwindow.isBot(*args, **kwargs) @@ -115,29 +122,35 @@ class PesterTabWindow(QtWidgets.QFrame): # TODO: Clean this up. Our text areas now call this. keypress = event.key() mods = event.modifiers() - if ((mods & QtCore.Qt.KeyboardModifier.ControlModifier) and - keypress == QtCore.Qt.Key.Key_Tab): + if ( + mods & QtCore.Qt.KeyboardModifier.ControlModifier + ) and keypress == QtCore.Qt.Key.Key_Tab: handles = list(self.convos.keys()) waiting = self.mainwindow.waitingMessages.waitingHandles() waitinghandles = list(set(handles) & set(waiting)) if len(waitinghandles) > 0: nexti = self.tabIndices[waitinghandles[0]] else: - nexti = (self.tabIndices[self.currentConvo.title()] + 1) % self.tabs.count() + nexti = ( + self.tabIndices[self.currentConvo.title()] + 1 + ) % self.tabs.count() self.tabs.setCurrentIndex(nexti) @QtCore.pyqtSlot() - def nudgeTabNext(self): return self.nudgeTabIndex(+1) + def nudgeTabNext(self): + return self.nudgeTabIndex(+1) + @QtCore.pyqtSlot() - def nudgeTabLast(self): return self.nudgeTabIndex(-1) + def nudgeTabLast(self): + return self.nudgeTabIndex(-1) def nudgeTabIndex(self, direction): # Inverted controls. Might add an option for this if people want # it. - #~if keypress == QtCore.Qt.Key.Key_PageDown: - #~ direction = 1 - #~elif keypress == QtCore.Qt.Key.Key_PageUp: - #~ direction = -1 + # ~if keypress == QtCore.Qt.Key.Key_PageDown: + # ~ direction = 1 + # ~elif keypress == QtCore.Qt.Key.Key_PageUp: + # ~ direction = -1 # ...Processing... tabs = self.tabs # Pick our new index by sliding up or down the tab range. @@ -161,7 +174,7 @@ class PesterTabWindow(QtWidgets.QFrame): tabs.setCurrentIndex(nind) def contextMenuEvent(self, event): - #~if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse: + # ~if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse: tabi = self.tabs.tabAt(event.pos()) if tabi < 0: tabi = self.tabs.currentIndex() @@ -179,12 +192,14 @@ class PesterTabWindow(QtWidgets.QFrame): def closeSoft(self): self.softclose = True self.close() + def updateBlocked(self, handle): i = self.tabIndices[handle] icon = QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) self.tabs.setTabIcon(i, icon) if self.tabs.currentIndex() == i: self.setWindowIcon(icon) + def updateMood(self, handle, mood, unblocked=False): i = self.tabIndices[handle] if handle in self.mainwindow.config.getBlocklist() and not unblocked: @@ -194,37 +209,42 @@ class PesterTabWindow(QtWidgets.QFrame): self.tabs.setTabIcon(i, icon) if self.tabs.currentIndex() == i: self.setWindowIcon(icon) + def closeEvent(self, event): if not self.softclose: while self.tabs.count() > 0: self.tabClose(0) self.windowClosed.emit() + def focusInEvent(self, event): # make sure we're not switching tabs! i = self.tabs.tabAt(self.mapFromGlobal(QtGui.QCursor.pos())) if i == -1: - i = self.tabs.currentIndex() + i = self.tabs.currentIndex() handle = str(self.tabs.tabText(i)) self.clearNewMessage(handle) + def convoHasFocus(self, handle): i = self.tabIndices[handle] - if (self.tabs.currentIndex() == i and - (self.hasFocus() or self.tabs.hasFocus())): + if self.tabs.currentIndex() == i and (self.hasFocus() or self.tabs.hasFocus()): return True else: return False + def notifyNewMessage(self, handle): i = self.tabIndices[handle] - self.tabs.setTabTextColor(i, - QtGui.QColor(self.mainwindow.theme["%s/tabs/newmsgcolor" - % (self.type)])) + self.tabs.setTabTextColor( + i, QtGui.QColor(self.mainwindow.theme["%s/tabs/newmsgcolor" % (self.type)]) + ) convo = self.convos[handle] # Create a function for the icon to use # TODO: Let us disable this. def func(): convo.showChat() + self.mainwindow.waitingMessages.addMessage(handle, func) # set system tray + def clearNewMessage(self, handle): try: i = self.tabIndices[handle] @@ -232,13 +252,15 @@ class PesterTabWindow(QtWidgets.QFrame): except KeyError: pass self.mainwindow.waitingMessages.messageAnswered(handle) + def initTheme(self, theme): self.resize(*theme["convo/size"]) self.setStyleSheet(theme["convo/tabwindow/style"]) self.tabs.setShape(QtWidgets.QTabBar.Shape(theme["convo/tabs/tabstyle"])) - self.tabs.setStyleSheet("QTabBar::tab{ %s } QTabBar::tab:selected { %s }" - % (theme["convo/tabs/style"], - theme["convo/tabs/selectedstyle"])) + self.tabs.setStyleSheet( + "QTabBar::tab{ %s } QTabBar::tab:selected { %s }" + % (theme["convo/tabs/style"], theme["convo/tabs/selectedstyle"]) + ) def changeTheme(self, theme): self.initTheme(theme) @@ -255,20 +277,20 @@ class PesterTabWindow(QtWidgets.QFrame): def tabClose(self, i): handle = str(self.tabs.tabText(i)) self.mainwindow.waitingMessages.messageAnswered(handle) - #print(self.convos.keys()) + # print(self.convos.keys()) # I, legit don' t know why this is an issue, but, uh, yeah- try: convo = self.convos[handle] except: - #handle = handle.replace("&","") - handle = ''.join(handle.split('&', 1)) + # handle = handle.replace("&","") + handle = "".join(handle.split("&", 1)) convo = self.convos[handle] del self.convos[handle] del self.tabIndices[handle] self.tabs.removeTab(i) for (h, j) in self.tabIndices.items(): if j > i: - self.tabIndices[h] = j-1 + self.tabIndices[h] = j - 1 self.layout.removeWidget(convo) convo.close() if self.tabs.count() == 0: @@ -312,10 +334,12 @@ class PesterTabWindow(QtWidgets.QFrame): windowClosed = QtCore.pyqtSignal() + class PesterMovie(QtGui.QMovie): def __init__(self, parent): super(PesterMovie, self).__init__(parent) self.textwindow = parent + @QtCore.pyqtSlot(int) def animate(self, frame): text = self.textwindow @@ -327,20 +351,25 @@ class PesterMovie(QtGui.QMovie): if text.hasTabs: i = text.tabobject.tabIndices[text.parent().title()] if text.tabobject.tabs.currentIndex() == i: - text.document().addResource(QtGui.QTextDocument.ResourceType.ImageResource.value, - text.urls[movie], movie.currentPixmap()) + text.document().addResource( + QtGui.QTextDocument.ResourceType.ImageResource.value, + text.urls[movie], + movie.currentPixmap(), + ) text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth()) else: - text.document().addResource(QtGui.QTextDocument.ResourceType.ImageResource.value, - text.urls[movie], - movie.currentPixmap()) + text.document().addResource( + QtGui.QTextDocument.ResourceType.ImageResource.value, + text.urls[movie], + movie.currentPixmap(), + ) text.setLineWrapColumnOrWidth(text.lineWrapColumnOrWidth()) - + class PesterText(QtWidgets.QTextEdit): def __init__(self, theme, parent=None): super(PesterText, self).__init__(parent) - if hasattr(self.parent(), 'mainwindow'): + if hasattr(self.parent(), "mainwindow"): self.mainwindow = self.parent().mainwindow else: self.mainwindow = self.parent() @@ -356,8 +385,12 @@ class PesterText(QtWidgets.QTextEdit): self.copyAvailable[bool].connect(self.textReady) self.urls = {} for k in smiledict: - self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), "smilies/%s" % (smiledict[k])) - #self.mainwindow.animationSetting[bool].connect(self.animateChanged) + self.addAnimation( + QtCore.QUrl("smilies/%s" % (smiledict[k])), + "smilies/%s" % (smiledict[k]), + ) + # self.mainwindow.animationSetting[bool].connect(self.animateChanged) + def addAnimation(self, url, fileName): # We don't need to treat images formats like .png as animation, # this always opens a file handler otherwise, "movie.frameCount() > 1" isn't sufficient. @@ -365,12 +398,12 @@ class PesterText(QtWidgets.QTextEdit): # As long as we're only using gifs there's no advantage to that though. if not fileName.endswith(".gif"): return - + movie = PesterMovie(self) movie.setFileName(fileName) self.urls[movie] = url movie.frameChanged[int].connect(movie.animate) - + """ @QtCore.pyqtSlot(bool) def animateChanged(self, animate): @@ -393,29 +426,37 @@ class PesterText(QtWidgets.QTextEdit): @QtCore.pyqtSlot(bool) def textReady(self, ready): self.textSelected = ready + def initTheme(self, theme): if "convo/scrollbar" in theme: - self.setStyleSheet("QTextEdit { %s }" - "QScrollBar:vertical { %s }" - "QScrollBar::handle:vertical { %s }" - "QScrollBar::add-line:vertical { %s }" - "QScrollBar::sub-line:vertical { %s }" - "QScrollBar:up-arrow:vertical { %s }" - "QScrollBar:down-arrow:vertical { %s }" - % (theme["convo/textarea/style"], - theme["convo/scrollbar/style"], - theme["convo/scrollbar/handle"], - theme["convo/scrollbar/downarrow"], - theme["convo/scrollbar/uparrow"], - theme["convo/scrollbar/uarrowstyle"], - theme["convo/scrollbar/darrowstyle"])) + self.setStyleSheet( + "QTextEdit { %s }" + "QScrollBar:vertical { %s }" + "QScrollBar::handle:vertical { %s }" + "QScrollBar::add-line:vertical { %s }" + "QScrollBar::sub-line:vertical { %s }" + "QScrollBar:up-arrow:vertical { %s }" + "QScrollBar:down-arrow:vertical { %s }" + % ( + theme["convo/textarea/style"], + theme["convo/scrollbar/style"], + theme["convo/scrollbar/handle"], + theme["convo/scrollbar/downarrow"], + theme["convo/scrollbar/uparrow"], + theme["convo/scrollbar/uarrowstyle"], + theme["convo/scrollbar/darrowstyle"], + ) + ) else: self.setStyleSheet("QTextEdit { %s }" % (theme["convo/textarea/style"])) + def addMessage(self, lexmsg, chum): if len(lexmsg) == 0: return color = chum.colorcmd() - systemColor = QtGui.QColor(self.parent().mainwindow.theme["convo/systemMsgColor"]) + systemColor = QtGui.QColor( + self.parent().mainwindow.theme["convo/systemMsgColor"] + ) initials = chum.initials() parent = self.parent() window = parent.mainwindow @@ -438,28 +479,34 @@ class PesterText(QtWidgets.QTextEdit): time = "" if lexmsg[0] == "PESTERCHUM:BEGIN": parent.setChumOpen(True) - pmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) + pmsg = chum.pestermsg( + me, systemColor, window.theme["convo/text/beganpester"] + ) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:CEASE": parent.setChumOpen(False) - pmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/ceasepester"]) + pmsg = chum.pestermsg( + me, systemColor, window.theme["convo/text/ceasepester"] + ) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:BLOCK": - pmsg = chum.pestermsg(me, systemColor, window.theme['convo/text/blocked']) + pmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/blocked"]) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:UNBLOCK": - pmsg = chum.pestermsg(me, systemColor, window.theme['convo/text/unblocked']) + pmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/unblocked"]) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:BLOCKED": - pmsg = chum.pestermsg(me, systemColor, window.theme['convo/text/blockedmsg']) + pmsg = chum.pestermsg( + me, systemColor, window.theme["convo/text/blockedmsg"] + ) window.chatlog.log(chum.handle, pmsg) self.append(convertTags(pmsg)) elif lexmsg[0] == "PESTERCHUM:IDLE": - imsg = chum.idlemsg(systemColor, window.theme['convo/text/idle']) + imsg = chum.idlemsg(systemColor, window.theme["convo/text/idle"]) window.chatlog.log(chum.handle, imsg) self.append(convertTags(imsg)) elif type(lexmsg[0]) is mecmd: @@ -471,30 +518,34 @@ class PesterText(QtWidgets.QTextEdit): self.append(time + convertTags(memsg)) else: if not parent.chumopen and chum is not me: - beginmsg = chum.pestermsg(me, systemColor, window.theme["convo/text/beganpester"]) + beginmsg = chum.pestermsg( + me, systemColor, window.theme["convo/text/beganpester"] + ) parent.setChumOpen(True) window.chatlog.log(chum.handle, beginmsg) self.append(convertTags(beginmsg)) - lexmsg[0:0] = [colorBegin("" % (color), color), - "%s: " % (initials)] + lexmsg[0:0] = [colorBegin("" % (color), color), "%s: " % (initials)] lexmsg.append(colorEnd("")) - self.append("" - + time - + convertTags(lexmsg) - + "") - #self.append('' + self.append( + '' + time + convertTags(lexmsg) + "" + ) + # self.append('' # + '' # + '' # + ''); if chum is me: window.chatlog.log(parent.chum.handle, lexmsg) else: - if ((window.idler.auto or window.idler.manual) and parent.chumopen - and not parent.isBot(chum.handle)): + if ( + (window.idler.auto or window.idler.manual) + and parent.chumopen + and not parent.isBot(chum.handle) + ): idlethreshhold = 60 - if (not hasattr(self, 'lastmsg')) or \ - datetime.now() - self.lastmsg > timedelta(0,idlethreshhold): + if ( + not hasattr(self, "lastmsg") + ) or datetime.now() - self.lastmsg > timedelta(0, idlethreshhold): verb = window.theme["convo/text/idle"] idlemsg = me.idlemsg(systemColor, verb) parent.textArea.append(convertTags(idlemsg)) @@ -502,10 +553,12 @@ class PesterText(QtWidgets.QTextEdit): parent.messageSent.emit("PESTERCHUM:IDLE", parent.title()) self.lastmsg = datetime.now() window.chatlog.log(chum.handle, lexmsg) + def changeTheme(self, theme): self.initTheme(theme) sb = self.verticalScrollBar() sb.setValue(sb.maximum()) + def focusInEvent(self, event): self.parent().clearNewMessage() QtWidgets.QTextEdit.focusInEvent(self, event) @@ -516,12 +569,16 @@ class PesterText(QtWidgets.QTextEdit): def keyPressEvent(self, event): # First parent is the PesterConvo containing this. # Second parent is the PesterTabWindow containing *it*. - pass_to_super = (QtCore.Qt.Key.Key_PageUp, QtCore.Qt.Key.Key_PageDown, - QtCore.Qt.Key.Key_Up, QtCore.Qt.Key.Key_Down) + pass_to_super = ( + QtCore.Qt.Key.Key_PageUp, + QtCore.Qt.Key.Key_PageDown, + QtCore.Qt.Key.Key_Up, + QtCore.Qt.Key.Key_Down, + ) parent = self.parent() key = event.key() - #keymods = event.modifiers() - if hasattr(parent, 'textInput') and key not in pass_to_super: + # keymods = event.modifiers() + if hasattr(parent, "textInput") and key not in pass_to_super: # TODO: Shift focus here on bare (no modifiers) alphanumerics. parent.textInput.keyPressEvent(event) @@ -546,9 +603,11 @@ class PesterText(QtWidgets.QTextEdit): if event.modifiers() == QtCore.Qt.KeyboardModifier.ControlModifier: QtWidgets.QApplication.clipboard().setText(url) else: - QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, - QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl(url, QtCore.QUrl.ParsingMode.TolerantMode) + ) QtWidgets.QTextEdit.mousePressEvent(self, event) + def mouseMoveEvent(self, event): QtWidgets.QTextEdit.mouseMoveEvent(self, event) try: @@ -558,8 +617,13 @@ class PesterText(QtWidgets.QTextEdit): # PyQt5 pos = event.pos() if self.anchorAt(pos): - if self.viewport().cursor().shape != QtCore.Qt.CursorShape.PointingHandCursor: - self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) + if ( + self.viewport().cursor().shape + != QtCore.Qt.CursorShape.PointingHandCursor + ): + self.viewport().setCursor( + QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor) + ) else: self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor)) @@ -567,24 +631,28 @@ class PesterText(QtWidgets.QTextEdit): textMenu = self.createStandardContextMenu() textMenu.exec(event.globalPos()) + class PesterInput(QtWidgets.QLineEdit): stylesheet_path = "convo/input/style" + def __init__(self, theme, parent=None): super(PesterInput, self).__init__(parent) self.changeTheme(theme) + def changeTheme(self, theme): # Explicitly set color if not already set. # (Some platforms seem to default to white instead of black.) - - StyleSheet = theme[self.stylesheet_path] - if "color:" not in theme[self.stylesheet_path].replace(' ', ''): + + StyleSheet = theme[self.stylesheet_path] + if "color:" not in theme[self.stylesheet_path].replace(" ", ""): StyleSheet = "color: black; " + StyleSheet self.setStyleSheet(StyleSheet) - + def focusInEvent(self, event): self.parent().clearNewMessage() self.parent().textArea.textCursor().clearSelection() super(PesterInput, self).focusInEvent(event) + def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key.Key_Up: text = str(self.text()) @@ -600,6 +668,7 @@ class PesterInput(QtWidgets.QLineEdit): self.parent().mainwindow.idler.time = 0 super(PesterInput, self).keyPressEvent(event) + class PesterConvo(QtWidgets.QFrame): def __init__(self, chum, initiated, mainwindow, parent=None): super(PesterConvo, self).__init__(parent) @@ -610,9 +679,9 @@ class PesterConvo(QtWidgets.QFrame): self.mainwindow = mainwindow theme = self.mainwindow.theme self.resize(*theme["convo/size"]) - self.setStyleSheet("QtWidgets.QFrame#%s { %s }" - % (chum.handle, - theme["convo/style"])) + self.setStyleSheet( + "QtWidgets.QFrame#%s { %s }" % (chum.handle, theme["convo/style"]) + ) self.setWindowIcon(self.icon()) self.setWindowTitle(self.title()) @@ -620,12 +689,22 @@ class PesterConvo(QtWidgets.QFrame): self.chumLabel = QtWidgets.QLabel(t.safe_substitute(handle=chum.handle), self) self.chumLabel.setStyleSheet(self.mainwindow.theme["convo/chumlabel/style"]) - self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] - | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]]) - self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"]) - self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"]) - self.chumLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, - QtWidgets.QSizePolicy.Policy.MinimumExpanding)) + self.chumLabel.setAlignment( + self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] + | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]] + ) + self.chumLabel.setMaximumHeight( + self.mainwindow.theme["convo/chumlabel/maxheight"] + ) + self.chumLabel.setMinimumHeight( + self.mainwindow.theme["convo/chumlabel/minheight"] + ) + self.chumLabel.setSizePolicy( + QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) + ) self.textArea = PesterText(self.mainwindow.theme, self) self.textInput = PesterInput(self.mainwindow.theme, self) self.textInput.setFocus() @@ -638,28 +717,45 @@ class PesterConvo(QtWidgets.QFrame): self.layout.addWidget(self.textInput) self.layout.setSpacing(0) margins = self.mainwindow.theme["convo/margins"] - self.layout.setContentsMargins(margins["left"], margins["top"], - margins["right"], margins["bottom"]) + self.layout.setContentsMargins( + margins["left"], margins["top"], margins["right"], margins["bottom"] + ) self.setLayout(self.layout) self.optionsMenu = QtWidgets.QMenu(self) - self.optionsMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) - self.addChumAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self) + self.optionsMenu.setStyleSheet( + self.mainwindow.theme["main/defaultwindow/style"] + ) + self.addChumAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self + ) self.addChumAction.triggered.connect(self.addThisChum) - self.blockAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self) + self.blockAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self + ) self.blockAction.triggered.connect(self.blockThisChum) - self.quirksOff = QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self) + self.quirksOff = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self + ) self.quirksOff.setCheckable(True) self.quirksOff.toggled[bool].connect(self.toggleQuirks) - self.oocToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self) + self.oocToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self + ) self.oocToggle.setCheckable(True) self.oocToggle.toggled[bool].connect(self.toggleOOC) - self.unblockchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self) + self.unblockchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self + ) self.unblockchum.triggered.connect(self.unblockChumSlot) - self.reportchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/report"], self) + self.reportchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/report"], self + ) self.reportchum.triggered.connect(self.reportThisChum) - self.logchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self) + self.logchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self + ) self.logchum.triggered.connect(self.openChumLogs) # For this, we'll want to use setChecked to toggle these so they match @@ -670,25 +766,32 @@ class PesterConvo(QtWidgets.QFrame): # TODO: Look into setting up theme support here. # Theme support :3c - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): try: - self._beepToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"], self) + self._beepToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"], self + ) except: self._beepToggle = QAction("BEEP ON MESSAGE", self) self._beepToggle.setCheckable(True) self._beepToggle.toggled[bool].connect(self.toggleBeep) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): try: - self._flashToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"], self) + self._flashToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"], self + ) except: self._flashToggle = QAction("FLASH ON MESSAGE", self) self._flashToggle.setCheckable(True) self._flashToggle.toggled[bool].connect(self.toggleFlash) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): try: - self._muteToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"], self) + self._muteToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"], + self, + ) except: self._muteToggle = QAction("MUTE NOTIFICATIONS", self) self._muteToggle.setCheckable(True) @@ -717,7 +820,11 @@ class PesterConvo(QtWidgets.QFrame): if parent: parent.addChat(self) if initiated: - msg = self.mainwindow.profile().pestermsg(self.chum, QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), self.mainwindow.theme["convo/text/beganpester"]) + msg = self.mainwindow.profile().pestermsg( + self.chum, + QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]), + self.mainwindow.theme["convo/text/beganpester"], + ) self.setChumOpen(True) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.title(), msg) @@ -726,8 +833,10 @@ class PesterConvo(QtWidgets.QFrame): def title(self): return self.chum.handle + def icon(self): return self.chum.mood.icon(self.mainwindow.theme) + def myUpdateMood(self, mood): chum = self.mainwindow.profile() syscolor = QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]) @@ -740,12 +849,12 @@ class PesterConvo(QtWidgets.QFrame): def updateMood(self, mood, unblocked=False, old=None): syscolor = QtGui.QColor(self.mainwindow.theme["convo/systemMsgColor"]) - #~ if mood.name() == "offline" and self.chumopen == True and not unblocked: - #~ self.mainwindow.ceasesound.play() - #~ msg = self.chum.pestermsg(self.mainwindow.profile(), syscolor, self.mainwindow.theme["convo/text/ceasepester"]) - #~ self.textArea.append(convertTags(msg)) - #~ self.mainwindow.chatlog.log(self.title(), msg) - #~ self.chumopen = False + # ~ if mood.name() == "offline" and self.chumopen == True and not unblocked: + # ~ self.mainwindow.ceasesound.play() + # ~ msg = self.chum.pestermsg(self.mainwindow.profile(), syscolor, self.mainwindow.theme["convo/text/ceasepester"]) + # ~ self.textArea.append(convertTags(msg)) + # ~ self.mainwindow.chatlog.log(self.title(), msg) + # ~ self.chumopen = False if old and old.name() != mood.name(): msg = self.chum.moodmsg(mood, syscolor, self.mainwindow.theme) self.textArea.append(convertTags(msg)) @@ -754,7 +863,9 @@ class PesterConvo(QtWidgets.QFrame): self.parent().updateMood(self.title(), mood, unblocked) else: if self.chum.blocked(self.mainwindow.config) and not unblocked: - self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) + self.setWindowIcon( + QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) + ) self.optionsMenu.addAction(self.unblockchum) self.optionsMenu.removeAction(self.blockAction) else: @@ -762,17 +873,21 @@ class PesterConvo(QtWidgets.QFrame): self.optionsMenu.removeAction(self.unblockchum) self.optionsMenu.addAction(self.blockAction) # print mood update? + def updateBlocked(self): if self.parent(): self.parent().updateBlocked(self.title()) else: - self.setWindowIcon(QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"])) + self.setWindowIcon( + QtGui.QIcon(self.mainwindow.theme["main/chums/moods/blocked/icon"]) + ) self.optionsMenu.addAction(self.unblockchum) self.optionsMenu.removeAction(self.blockAction) def updateColor(self, color): PchumLog.debug("convo updateColor: " + str(color)) self.chum.color = color + def addMessage(self, msg, me=True): if type(msg) in [str, str]: lexmsg = lexMessage(msg) @@ -796,16 +911,19 @@ class PesterConvo(QtWidgets.QFrame): memoblink &= self.mainwindow.config.MBLINK pesterblink &= self.mainwindow.config.PBLINK mutednots = self.notifications_muted - #mtsrc = self + # mtsrc = self if parent: try: mutednots = parent.notifications_muted - #mtsrc = parent + # mtsrc = parent except: pass - if not (self.hasFocus() or self.textArea.hasFocus() or - self.textInput.hasFocus() or - (parent and parent.convoHasFocus(title))): + if not ( + self.hasFocus() + or self.textArea.hasFocus() + or self.textInput.hasFocus() + or (parent and parent.convoHasFocus(title)) + ): # ok if it has a tabconvo parent, send that the notify. if parent: # Just let the icon highlight normally. @@ -818,11 +936,11 @@ class PesterConvo(QtWidgets.QFrame): # the checks there. # PesterTabWindow -> MemoTabWindow if isinstance(parent, MemoTabWindow): - if self.always_flash or memoblink: - self.mainwindow.gainAttention.emit(parent) + if self.always_flash or memoblink: + self.mainwindow.gainAttention.emit(parent) elif isinstance(parent, PesterTabWindow): - if self.always_flash or pesterblink: - self.mainwindow.gainAttention.emit(parent) + if self.always_flash or pesterblink: + self.mainwindow.gainAttention.emit(parent) # if not change the window title and update system tray else: self.newmessage = True @@ -831,15 +949,16 @@ class PesterConvo(QtWidgets.QFrame): # entirely sure how much of this directly affects what we see. def func(): self.showChat() + self.mainwindow.waitingMessages.addMessage(title, func) if not mutednots: # Once again, PesterMemo inherits from PesterConvo. if isinstance(self, PesterMemo): - if self.always_flash or memoblink: - self.mainwindow.gainAttention.emit(self) + if self.always_flash or memoblink: + self.mainwindow.gainAttention.emit(self) elif isinstance(self, PesterConvo): - if self.always_flash or pesterblink: - self.mainwindow.gainAttention.emit(self) + if self.always_flash or pesterblink: + self.mainwindow.gainAttention.emit(self) def clearNewMessage(self): if self.parent(): @@ -849,6 +968,7 @@ class PesterConvo(QtWidgets.QFrame): self.setWindowTitle(self.title()) self.mainwindow.waitingMessages.messageAnswered(self.title()) # reset system tray + def focusInEvent(self, event): self.clearNewMessage() self.textInput.setFocus() @@ -862,68 +982,101 @@ class PesterConvo(QtWidgets.QFrame): if self.parent(): self.parent().showChat(self.title()) self.raiseChat() + def contextMenuEvent(self, event): if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse: self.optionsMenu.popup(event.globalPos()) + def closeEvent(self, event): self.mainwindow.waitingMessages.messageAnswered(self.title()) for movie in self.textArea.urls: - movie.setFileName("") # Required, sometimes, for some reason. . . + movie.setFileName("") # Required, sometimes, for some reason. . . movie.stop() del movie self.windowClosed.emit(self.title()) def setChumOpen(self, o): self.chumopen = o + def changeTheme(self, theme): self.resize(*theme["convo/size"]) - self.setStyleSheet("QtWidgets.QFrame#%s { %s }" % (self.chum.handle, theme["convo/style"])) + self.setStyleSheet( + "QtWidgets.QFrame#%s { %s }" % (self.chum.handle, theme["convo/style"]) + ) margins = theme["convo/margins"] - self.layout.setContentsMargins(margins["left"], margins["top"], - margins["right"], margins["bottom"]) + self.layout.setContentsMargins( + margins["left"], margins["top"], margins["right"], margins["bottom"] + ) self.setWindowIcon(self.icon()) t = Template(self.mainwindow.theme["convo/chumlabel/text"]) self.chumLabel.setText(t.safe_substitute(handle=self.title())) self.chumLabel.setStyleSheet(theme["convo/chumlabel/style"]) - self.chumLabel.setAlignment(self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]]) - self.chumLabel.setMaximumHeight(self.mainwindow.theme["convo/chumlabel/maxheight"]) - self.chumLabel.setMinimumHeight(self.mainwindow.theme["convo/chumlabel/minheight"]) - self.chumLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding)) - self.quirksOff.setText(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"]) - self.addChumAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/addchum"]) - self.blockAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"]) - self.unblockchum.setText(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"]) + self.chumLabel.setAlignment( + self.aligndict["h"][self.mainwindow.theme["convo/chumlabel/align/h"]] + | self.aligndict["v"][self.mainwindow.theme["convo/chumlabel/align/v"]] + ) + self.chumLabel.setMaximumHeight( + self.mainwindow.theme["convo/chumlabel/maxheight"] + ) + self.chumLabel.setMinimumHeight( + self.mainwindow.theme["convo/chumlabel/minheight"] + ) + self.chumLabel.setSizePolicy( + QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + ) + self.quirksOff.setText( + self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"] + ) + self.addChumAction.setText( + self.mainwindow.theme["main/menus/rclickchumlist/addchum"] + ) + self.blockAction.setText( + self.mainwindow.theme["main/menus/rclickchumlist/blockchum"] + ) + self.unblockchum.setText( + self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"] + ) self.logchum.setText(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"]) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): try: - self._beepToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"]) + self._beepToggle.setText( + self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"] + ) except: self._beepToggle.setText("BEEP ON MESSAGE") - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): try: - self._flashToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"]) + self._flashToggle.setText( + self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"] + ) except: self._flashToggle.setText("FLASH ON MESSAGE", self) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): try: - self._muteToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"]) + self._muteToggle.setText( + self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"] + ) except: self._muteToggle.setText("MUTE NOTIFICATIONS") - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/report"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/report"): try: - self.reportchum.setText(self.mainwindow.theme["main/menus/rclickchumlist/report"]) + self.reportchum.setText( + self.mainwindow.theme["main/menus/rclickchumlist/report"] + ) except: pass self.textArea.changeTheme(theme) self.textInput.changeTheme(theme) - @QtCore.pyqtSlot() def sentMessage(self): # Offloaded to another function, like its sisters. @@ -936,29 +1089,36 @@ class PesterConvo(QtWidgets.QFrame): @QtCore.pyqtSlot() def addThisChum(self): self.mainwindow.addChum(self.chum) + @QtCore.pyqtSlot() def blockThisChum(self): self.mainwindow.blockChum(self.chum.handle) + @QtCore.pyqtSlot() def reportThisChum(self): self.mainwindow.reportChum(self.chum.handle) + @QtCore.pyqtSlot() def unblockChumSlot(self): self.mainwindow.unblockChum(self.chum.handle) + @QtCore.pyqtSlot(bool) def toggleQuirks(self, toggled): self.applyquirks = not toggled + @QtCore.pyqtSlot(bool) def toggleOOC(self, toggled): self.ooc = toggled + @QtCore.pyqtSlot() def openChumLogs(self): currentChum = self.chum.handle - self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, - self.mainwindow.config, - self.mainwindow.theme, - self.mainwindow) - self.mainwindow.chumList.pesterlogviewer.rejected.connect(self.mainwindow.chumList.closeActiveLog) + self.mainwindow.chumList.pesterlogviewer = PesterLogViewer( + currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow + ) + self.mainwindow.chumList.pesterlogviewer.rejected.connect( + self.mainwindow.chumList.closeActiveLog + ) self.mainwindow.chumList.pesterlogviewer.show() self.mainwindow.chumList.pesterlogviewer.raise_() self.mainwindow.chumList.pesterlogviewer.activateWindow() @@ -975,15 +1135,22 @@ class PesterConvo(QtWidgets.QFrame): def toggleMute(self, toggled): self.notifications_muted = toggled - messageSent = QtCore.pyqtSignal('QString', 'QString') - windowClosed = QtCore.pyqtSignal('QString') + messageSent = QtCore.pyqtSignal("QString", "QString") + windowClosed = QtCore.pyqtSignal("QString") + + aligndict = { + "h": { + "center": QtCore.Qt.AlignmentFlag.AlignHCenter, + "left": QtCore.Qt.AlignmentFlag.AlignLeft, + "right": QtCore.Qt.AlignmentFlag.AlignRight, + }, + "v": { + "center": QtCore.Qt.AlignmentFlag.AlignVCenter, + "top": QtCore.Qt.AlignmentFlag.AlignTop, + "bottom": QtCore.Qt.AlignmentFlag.AlignBottom, + }, + } - aligndict = {"h": {"center": QtCore.Qt.AlignmentFlag.AlignHCenter, - "left": QtCore.Qt.AlignmentFlag.AlignLeft, - "right": QtCore.Qt.AlignmentFlag.AlignRight }, - "v": {"center": QtCore.Qt.AlignmentFlag.AlignVCenter, - "top": QtCore.Qt.AlignmentFlag.AlignTop, - "bottom": QtCore.Qt.AlignmentFlag.AlignBottom } } # the import is way down here to avoid recursive imports from logviewer import PesterLogViewer diff --git a/dataobjs.py b/dataobjs.py index 112acee..b9b485a 100644 --- a/dataobjs.py +++ b/dataobjs.py @@ -1,6 +1,7 @@ import logging import ostools -PchumLog = logging.getLogger('pchumLogger') + +PchumLog = logging.getLogger("pchumLogger") try: from PyQt6 import QtGui except ImportError: @@ -11,15 +12,17 @@ import re import random from mood import Mood -from parsetools import (timeDifference, - convertTags, - lexMessage, - parseRegexpFunctions, - smiledict) +from parsetools import ( + timeDifference, + convertTags, + lexMessage, + parseRegexpFunctions, + smiledict, +) from mispeller import mispeller _urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+") -#_url2re = re.compile(r"(?i)(?\\]+)\)") _lowerre = re.compile(r"lower\(([\w<>\\]+)\)") @@ -30,6 +33,7 @@ _smilere = re.compile("|".join(list(smiledict.keys()))) _memore = re.compile(r"(\s|^)(#[A-Za-z0-9_]+)") _handlere = re.compile(r"(\s|^)(@[A-Za-z0-9_]+)") + class pesterQuirk(object): def __init__(self, quirk): if type(quirk) != dict: @@ -46,7 +50,7 @@ class pesterQuirk(object): self.checkstate = self.quirk["checkstate"] except KeyError: pass - + def apply(self, string, first=False, last=False): if not self.on: return string @@ -60,7 +64,7 @@ class pesterQuirk(object): fr = self.quirk["from"] if not first and len(fr) > 0 and fr[0] == "^": return string - if not last and len(fr) > 0 and fr[len(fr)-1] == "$": + if not last and len(fr) > 0 and fr[len(fr) - 1] == "$": return string to = self.quirk["to"] pt = parseRegexpFunctions(to) @@ -71,15 +75,17 @@ class pesterQuirk(object): fr = self.quirk["from"] if not first and len(fr) > 0 and fr[0] == "^": return string - if not last and len(fr) > 0 and fr[len(fr)-1] == "$": + if not last and len(fr) > 0 and fr[len(fr) - 1] == "$": return string + def randomrep(mo): choice = random.choice(self.quirk["randomlist"]) pt = parseRegexpFunctions(choice) return pt.expand(mo) + return re.sub(self.quirk["from"], randomrep, string) elif self.type == "spelling": - percentage = self.quirk["percentage"]/100.0 + percentage = self.quirk["percentage"] / 100.0 words = string.split(" ") newl = [] ctag = re.compile("()", re.I) @@ -99,7 +105,7 @@ class pesterQuirk(object): else: newl.append(w) return " ".join(newl) - + def __str__(self): if self.type == "prefix": return "BEGIN WITH: %s" % (self.quirk["value"]) @@ -108,28 +114,38 @@ class pesterQuirk(object): elif self.type == "replace": return "REPLACE %s WITH %s" % (self.quirk["from"], self.quirk["to"]) elif self.type == "regexp": - return "REGEXP: %s REPLACED WITH %s" % (self.quirk["from"], self.quirk["to"]) + return "REGEXP: %s REPLACED WITH %s" % ( + self.quirk["from"], + self.quirk["to"], + ) elif self.type == "random": - return "REGEXP: %s RANDOMLY REPLACED WITH %s" % (self.quirk["from"], [r for r in self.quirk["randomlist"]]) + return "REGEXP: %s RANDOMLY REPLACED WITH %s" % ( + self.quirk["from"], + [r for r in self.quirk["randomlist"]], + ) elif self.type == "spelling": return "MISPELLER: %d%%" % (self.quirk["percentage"]) + class pesterQuirks(object): def __init__(self, quirklist): self.quirklist = [] for q in quirklist: self.addQuirk(q) + def plainList(self): return [q.quirk for q in self.quirklist] + def addQuirk(self, q): if type(q) == dict: self.quirklist.append(pesterQuirk(q)) elif type(q) == pesterQuirk: self.quirklist.append(q) + def apply(self, lexed, first=False, last=False): - prefix = [q for q in self.quirklist if q.type=='prefix'] - suffix = [q for q in self.quirklist if q.type=='suffix'] - + prefix = [q for q in self.quirklist if q.type == "prefix"] + suffix = [q for q in self.quirklist if q.type == "suffix"] + newlist = [] for (i, o) in enumerate(lexed): if type(o) not in [str, str]: @@ -140,7 +156,7 @@ class pesterQuirks(object): newlist.append(string) newlist.append(o) continue - lastStr = (i == len(lexed)-1) + lastStr = i == len(lexed) - 1 string = o for q in self.quirklist: try: @@ -170,17 +186,17 @@ class pesterQuirks(object): excludes.sort(key=lambda exclude: exclude.start()) # Recursion check. # Strings like http://:3: require this. - for n in range(0, len(excludes)-1): - if excludes[n].end() > excludes[n+1].start(): + for n in range(0, len(excludes) - 1): + if excludes[n].end() > excludes[n + 1].start(): excludes.pop(n) # Seperate parts to be quirked. sendparts = list() # Add string until start of exclude at index 0. until = excludes[0].start() - sendparts.append(string[:until]) + sendparts.append(string[:until]) # Add strings between excludes. for part in range(1, len(excludes)): - after = excludes[part-1].end() + after = excludes[part - 1].end() until = excludes[part].start() sendparts.append(string[after:until]) # Add string after exclude at last index. @@ -191,50 +207,46 @@ class pesterQuirks(object): recvparts = list() for part in sendparts: # No split, apply like normal. - if q.type == 'regexp' or q.type == 'random': - recvparts.append(q.apply(part, - first=(i==0), - last=lastStr)) - elif q.type == 'prefix' and i == 0: + if q.type == "regexp" or q.type == "random": + recvparts.append( + q.apply(part, first=(i == 0), last=lastStr) + ) + elif q.type == "prefix" and i == 0: recvparts.append(q.apply(part)) - elif q.type == 'suffix' and lastStr: + elif q.type == "suffix" and lastStr: recvparts.append(q.apply(part)) else: recvparts.append(q.apply(part)) # Reconstruct and update string. - string = '' - #print("excludes: " + str(excludes)) - #print("sendparts: " + str(sendparts)) - #print("recvparts: " + str(recvparts)) + string = "" + # print("excludes: " + str(excludes)) + # print("sendparts: " + str(sendparts)) + # print("recvparts: " + str(recvparts)) for part in range(0, len(excludes)): string += recvparts[part] string += excludes[part].group() string += recvparts[-1] else: # No split, apply like normal. - if q.type != 'prefix' and q.type != 'suffix': - if q.type == 'regexp' or q.type == 'random': - string = q.apply(string, - first=(i==0), - last=lastStr) + if q.type != "prefix" and q.type != "suffix": + if q.type == "regexp" or q.type == "random": + string = q.apply(string, first=(i == 0), last=lastStr) else: string = q.apply(string) - elif q.type == 'prefix' and i == 0: + elif q.type == "prefix" and i == 0: string = q.apply(string) - elif q.type == 'suffix' and lastStr: + elif q.type == "suffix" and lastStr: string = q.apply(string) else: # No split, apply like normal. - if q.type != 'prefix' and q.type != 'suffix': - if q.type == 'regexp' or q.type == 'random': - string = q.apply(string, - first=(i==0), - last=lastStr) + if q.type != "prefix" and q.type != "suffix": + if q.type == "regexp" or q.type == "random": + string = q.apply(string, first=(i == 0), last=lastStr) else: string = q.apply(string) - elif q.type == 'prefix' and i == 0: + elif q.type == "prefix" and i == 0: string = q.apply(string) - elif q.type == 'suffix' and lastStr: + elif q.type == "suffix" and lastStr: string = q.apply(string) newlist.append(string) final = [] @@ -249,8 +261,17 @@ class pesterQuirks(object): for q in self.quirklist: yield q + class PesterProfile(object): - def __init__(self, handle, color=None, mood=Mood("offline"), group=None, notes="", chumdb=None): + def __init__( + self, + handle, + color=None, + mood=Mood("offline"), + group=None, + notes="", + chumdb=None, + ): self.handle = handle if color is None: if chumdb: @@ -266,6 +287,7 @@ class PesterProfile(object): group = "Chums" self.group = group self.notes = notes + def initials(self, time=None): handle = self.handle caps = [l for l in handle if l.isupper()] @@ -275,37 +297,46 @@ class PesterProfile(object): PchumLog.debug("caps = " + str(caps)) # Fallback for invalid string try: - initials = (handle[0]+caps[0]).upper() + initials = (handle[0] + caps[0]).upper() except: - PchumLog.exception('') + PchumLog.exception("") initials = "XX" PchumLog.debug("initials = " + str(initials)) - if hasattr(self, 'time') and time: + if hasattr(self, "time") and time: if self.time > time: - return "F"+initials + return "F" + initials elif self.time < time: - return "P"+initials + return "P" + initials else: - return "C"+initials + return "C" + initials else: return initials + def colorhtml(self): if self.color: return self.color.name() else: return "#000000" + def colorcmd(self): if self.color: (r, g, b, a) = self.color.getRgb() - return "%d,%d,%d" % (r,g,b) + return "%d,%d,%d" % (r, g, b) else: return "0,0,0" + def plaindict(self): - return (self.handle, {"handle": self.handle, - "mood": self.mood.name(), - "color": str(self.color.name()), - "group": str(self.group), - "notes": str(self.notes)}) + return ( + self.handle, + { + "handle": self.handle, + "mood": self.mood.name(), + "color": str(self.color.name()), + "group": str(self.group), + "notes": str(self.notes), + }, + ) + def blocked(self, config): return self.handle in config.getBlocklist() @@ -315,112 +346,248 @@ class PesterProfile(object): uppersuffix = suffix.upper() if time is not None: handle = "%s %s" % (time.temporal, self.handle) - initials = time.pcf+self.initials()+time.number+uppersuffix + initials = time.pcf + self.initials() + time.number + uppersuffix else: handle = self.handle - initials = self.initials()+uppersuffix - return "-- %s%s [%s] %s --" % (syscolor.name(), handle, suffix, self.colorhtml(), initials, msg) + initials = self.initials() + uppersuffix + return "-- %s%s [%s] %s --" % ( + syscolor.name(), + handle, + suffix, + self.colorhtml(), + initials, + msg, + ) + def pestermsg(self, otherchum, syscolor, verb): - return "-- %s [%s] %s %s [%s] at %s --" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), verb, otherchum.handle, otherchum.colorhtml(), otherchum.initials(), datetime.now().strftime("%H:%M")) + return "-- %s [%s] %s %s [%s] at %s --" % ( + syscolor.name(), + self.handle, + self.colorhtml(), + self.initials(), + verb, + otherchum.handle, + otherchum.colorhtml(), + otherchum.initials(), + datetime.now().strftime("%H:%M"), + ) + def moodmsg(self, mood, syscolor, theme): - return "-- %s [%s] changed their mood to %s --" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), mood.name().upper(), theme["main/chums/moods"][mood.name()]["icon"]) + return ( + "-- %s [%s] changed their mood to %s --" + % ( + syscolor.name(), + self.handle, + self.colorhtml(), + self.initials(), + mood.name().upper(), + theme["main/chums/moods"][mood.name()]["icon"], + ) + ) + def idlemsg(self, syscolor, verb): - return "-- %s [%s] %s --" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), verb) + return "-- %s [%s] %s --" % ( + syscolor.name(), + self.handle, + self.colorhtml(), + self.initials(), + verb, + ) + def memoclosemsg(self, syscolor, initials, verb): if type(initials) == type(list()): - return "%s %s." % (syscolor.name(), self.colorhtml(), ", ".join(initials), verb) + return "%s %s." % ( + syscolor.name(), + self.colorhtml(), + ", ".join(initials), + verb, + ) else: - return "%s%s%s %s." % (syscolor.name(), self.colorhtml(), initials.pcf, self.initials(), initials.number, verb) + return "%s%s%s %s." % ( + syscolor.name(), + self.colorhtml(), + initials.pcf, + self.initials(), + initials.number, + verb, + ) + def memonetsplitmsg(self, syscolor, initials): if len(initials) <= 0: return "Netsplit quits: None" % (syscolor.name()) else: - return "Netsplit quits: %s" % (syscolor.name(), ", ".join(initials)) + return "Netsplit quits: %s" % ( + syscolor.name(), + ", ".join(initials), + ) + def memoopenmsg(self, syscolor, td, timeGrammar, verb, channel): - (temporal, pcf, when) = (timeGrammar.temporal, timeGrammar.pcf, timeGrammar.when) + (temporal, pcf, when) = ( + timeGrammar.temporal, + timeGrammar.pcf, + timeGrammar.when, + ) timetext = timeDifference(td) PchumLog.debug("pre pcf+self.initials()") - initials = pcf+self.initials() + initials = pcf + self.initials() PchumLog.debug("post pcf+self.initials()") - return "%s %s %s %s." % \ - (syscolor.name(), self.colorhtml(), initials, timetext, verb, channel[1:].upper().replace("_", " ")) + return "%s %s %s %s." % ( + syscolor.name(), + self.colorhtml(), + initials, + timetext, + verb, + channel[1:].upper().replace("_", " "), + ) + def memobanmsg(self, opchum, opgrammar, syscolor, initials, reason): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number if type(initials) == type(list()): if opchum.handle == reason: - return "%s banned %s from responding to memo." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials)) + return "%s banned %s from responding to memo." % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + ", ".join(initials), + ) else: - return "%s banned %s from responding to memo: [%s]." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials), str(reason)) + return ( + "%s banned %s from responding to memo: [%s]." + % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + ", ".join(initials), + str(reason), + ) + ) else: # Is timeGrammar defined? Not sure if this works as intented, added try except block to be safe. try: - initials = timeGrammar.pcf+self.initials()+timeGrammar.number + initials = timeGrammar.pcf + self.initials() + timeGrammar.number if opchum.handle == reason: - return "%s banned %s from responding to memo." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), initials) + return ( + "%s banned %s from responding to memo." + % (opchum.colorhtml(), opinit, self.colorhtml(), initials) + ) else: - return "%s banned %s from responding to memo: [%s]." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), initials, str(reason)) + return ( + "%s banned %s from responding to memo: [%s]." + % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + initials, + str(reason), + ) + ) except: - PchumLog.exception('') + PchumLog.exception("") initials = self.initials() if opchum.handle == reason: - return "%s banned %s from responding to memo." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), initials) + return ( + "%s banned %s from responding to memo." + % (opchum.colorhtml(), opinit, self.colorhtml(), initials) + ) else: - return "%s banned %s from responding to memo: [%s]." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), initials, str(reason)) + return ( + "%s banned %s from responding to memo: [%s]." + % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + initials, + str(reason), + ) + ) # As far as I'm aware, there's no IRC reply for this, this seems impossible to check for in practice. def memopermabanmsg(self, opchum, opgrammar, syscolor, timeGrammar): - initials = (timeGrammar.pcf - + self.initials() - + timeGrammar.number) - opinit = (opgrammar.pcf - + opchum.initials() - + opgrammar.number) - return "%s permabanned %s from the memo." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), initials) - + initials = timeGrammar.pcf + self.initials() + timeGrammar.number + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + return "%s permabanned %s from the memo." % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + initials, + ) + def memojoinmsg(self, syscolor, td, timeGrammar, verb): - #(temporal, pcf, when) = (timeGrammar.temporal, timeGrammar.pcf, timeGrammar.when) + # (temporal, pcf, when) = (timeGrammar.temporal, timeGrammar.pcf, timeGrammar.when) timetext = timeDifference(td) - initials = timeGrammar.pcf+self.initials()+timeGrammar.number - return "%s %s [%s] %s %s." % \ - (syscolor.name(), self.colorhtml(), timeGrammar.temporal, self.handle, - initials, timetext, verb) + initials = timeGrammar.pcf + self.initials() + timeGrammar.number + return "%s %s [%s] %s %s." % ( + syscolor.name(), + self.colorhtml(), + timeGrammar.temporal, + self.handle, + initials, + timetext, + verb, + ) + def memoopmsg(self, opchum, opgrammar, syscolor): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number - return "%s made %s an OP." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), self.initials()) + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + return "%s made %s an OP." % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + self.initials(), + ) + def memodeopmsg(self, opchum, opgrammar, syscolor): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number - return "%s took away %s's OP powers." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), self.initials()) + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + return "%s took away %s's OP powers." % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + self.initials(), + ) + def memovoicemsg(self, opchum, opgrammar, syscolor): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number - return "%s gave %s voice." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), self.initials()) + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + return "%s gave %s voice." % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + self.initials(), + ) + def memodevoicemsg(self, opchum, opgrammar, syscolor): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number - return "%s took away %s's voice." % \ - (opchum.colorhtml(), opinit, self.colorhtml(), self.initials()) + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + return "%s took away %s's voice." % ( + opchum.colorhtml(), + opinit, + self.colorhtml(), + self.initials(), + ) + def memomodemsg(self, opchum, opgrammar, syscolor, modeverb, modeon): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number - if modeon: modeon = "now" - else: modeon = "no longer" - return "Memo is %s %s by %s" % \ - (syscolor.name(), modeon, modeverb, opchum.colorhtml(), opinit) + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + if modeon: + modeon = "now" + else: + modeon = "no longer" + return "Memo is %s %s by %s" % ( + syscolor.name(), + modeon, + modeverb, + opchum.colorhtml(), + opinit, + ) + def memoquirkkillmsg(self, opchum, opgrammar, syscolor): - opinit = opgrammar.pcf+opchum.initials()+opgrammar.number - return "%s turned off your quirk." % \ - (syscolor.name(), opchum.colorhtml(), opinit) + opinit = opgrammar.pcf + opchum.initials() + opgrammar.number + return "%s turned off your quirk." % ( + syscolor.name(), + opchum.colorhtml(), + opinit, + ) @staticmethod def checkLength(handle): return len(handle) <= 256 + @staticmethod def checkValid(handle): caps = [l for l in handle if l.isupper()] @@ -432,11 +599,13 @@ class PesterProfile(object): return (False, "Only alphanumeric characters allowed") return (True,) + class PesterHistory(object): def __init__(self): self.history = [] self.current = 0 self.saved = None + def next(self, text): if self.current == 0: return None @@ -445,20 +614,25 @@ class PesterHistory(object): self.current -= 1 text = self.history[self.current] return text + def prev(self): self.current += 1 if self.current >= len(self.history): self.current = len(self.history) return self.retrieve() return self.history[self.current] + def reset(self): self.current = len(self.history) self.saved = None + def save(self, text): self.saved = text + def retrieve(self): return self.saved + def add(self, text): - if len(self.history) == 0 or text != self.history[len(self.history)-1]: + if len(self.history) == 0 or text != self.history[len(self.history) - 1]: self.history.append(text) self.reset() diff --git a/generic.py b/generic.py index f8d79f4..0f06980 100644 --- a/generic.py +++ b/generic.py @@ -5,30 +5,40 @@ except ImportError: from PyQt5 import QtGui, QtWidgets from datetime import timedelta + class mysteryTime(timedelta): def __sub__(self, other): return self + def __eq__(self, other): - return (type(other) is mysteryTime) + return type(other) is mysteryTime + def __neq__(self, other): - return (type(other) is not mysteryTime) + return type(other) is not mysteryTime + class CaseInsensitiveDict(dict): def __setitem__(self, key, value): super(CaseInsensitiveDict, self).__setitem__(key.lower(), value) + def __getitem__(self, key): return super(CaseInsensitiveDict, self).__getitem__(key.lower()) + def __contains__(self, key): return super(CaseInsensitiveDict, self).__contains__(key.lower()) + def has_key(self, key): return key.lower() in super(CaseInsensitiveDict, self) + def __delitem__(self, key): super(CaseInsensitiveDict, self).__delitem__(key.lower()) + class PesterList(list): def __init__(self, l): self.extend(l) + class PesterIcon(QtGui.QIcon): def __init__(self, *x): super(PesterIcon, self).__init__(x[0]) @@ -36,6 +46,7 @@ class PesterIcon(QtGui.QIcon): self.icon_pixmap = QtGui.QPixmap(x[0]) else: self.icon_pixmap = None + def realsize(self): if self.icon_pixmap: return self.icon_pixmap.size() @@ -45,18 +56,21 @@ class PesterIcon(QtGui.QIcon): except IndexError: return None + class RightClickList(QtWidgets.QListWidget): def contextMenuEvent(self, event): - #fuckin Qt <--- I feel that 5: self.moveupdate = 0 self.update() + def mousePressEvent(self, event): # Assuming everything is supported, we only need this function to call "self.windowHandle().startSystemMove()". # If not supported, startSystemMove() returns False and the legacy code runs anyway. @@ -135,18 +155,27 @@ class MovingWindow(QtWidgets.QFrame): print("PyQt <= 5.14?") print(str(e)) if event.button() == 1: - self.moving = event.globalPos() - self.pos() + self.moving = event.globalPos() - self.pos() + def mouseReleaseEvent(self, event): if event.button() == 1: self.update() self.moving = None + class NoneSound(object): def __init__(self, *args, **kwargs): pass - def play(self): pass - def setVolume(self, v): pass - def set_volume(self, v): pass + + def play(self): + pass + + def setVolume(self, v): + pass + + def set_volume(self, v): + pass + class WMButton(QtWidgets.QPushButton): def __init__(self, icon, parent=None): diff --git a/irc.py b/irc.py index 5553540..a321b0d 100644 --- a/irc.py +++ b/irc.py @@ -21,22 +21,23 @@ from oyoyo.client import IRCClient from oyoyo.cmdhandler import DefaultCommandHandler from oyoyo import helpers, services -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") # Python 3 QString = str # Copied from pesterchum.py -#CUSTOMBOTS = ["CALSPRITE", "RANDOMENCOUNTER"] -#BOTNAMES = ["NICKSERV", "CHANSERV", "MEMOSERV", "OPERSERV", "HELPSERV", "HOSTSERV", "BOTSERV"] -#BOTNAMES.extend(CUSTOMBOTS) +# CUSTOMBOTS = ["CALSPRITE", "RANDOMENCOUNTER"] +# BOTNAMES = ["NICKSERV", "CHANSERV", "MEMOSERV", "OPERSERV", "HELPSERV", "HOSTSERV", "BOTSERV"] +# BOTNAMES.extend(CUSTOMBOTS) -#if ostools.isOSXBundle(): +# if ostools.isOSXBundle(): # logging.basicConfig(level=logging.WARNING) -#else: +# else: # # karxi; We do NOT need this set to INFO; it's very, very spammy. # logging.basicConfig(level=logging.WARNING) + class PesterIRC(QtCore.QThread): def __init__(self, config, window, verify_hostname=True): QtCore.QThread.__init__(self) @@ -49,15 +50,18 @@ class PesterIRC(QtCore.QThread): self.stopIRC = None self.NickServ = services.NickServ() self.ChanServ = services.ChanServ() + def IRCConnect(self): - self.cli = IRCClient(PesterHandler, - host=self.config.server(), - port=self.config.port(), - ssl=self.config.ssl(), - nick=self.mainwindow.profile().handle, - username='pcc31', - realname='pcc31', - timeout=120) + self.cli = IRCClient( + PesterHandler, + host=self.config.server(), + port=self.config.port(), + ssl=self.config.ssl(), + nick=self.mainwindow.profile().handle, + username="pcc31", + realname="pcc31", + timeout=120, + ) self.cli.command_handler.parent = self self.cli.command_handler.mainwindow = self.mainwindow try: @@ -67,6 +71,7 @@ class PesterIRC(QtCore.QThread): self.askToConnect.emit(e) raise e self.conn = self.cli.conn() + def run(self): try: self.IRCConnect() @@ -96,10 +101,12 @@ class PesterIRC(QtCore.QThread): def setConnected(self): self.registeredIRC = True self.connected.emit() + def setConnectionBroken(self): PchumLog.critical("setconnection broken") self.disconnectIRC() - #self.brokenConnection = True # Unused + # self.brokenConnection = True # Unused + @QtCore.pyqtSlot() def updateIRC(self): try: @@ -111,9 +118,7 @@ class PesterIRC(QtCore.QThread): raise se except OSError as se: raise se - except (OSError, - ValueError, - IndexError) as se: + except (OSError, ValueError, IndexError) as se: raise se except StopIteration: self.conn = self.cli.conn() @@ -123,15 +128,17 @@ class PesterIRC(QtCore.QThread): @QtCore.pyqtSlot(PesterProfile) def getMood(self, *chums): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): self.cli.command_handler.getMood(*chums) + @QtCore.pyqtSlot(PesterList) def getMoods(self, chums): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): self.cli.command_handler.getMood(*chums) + @QtCore.pyqtSlot(QString, QString) def sendNotice(self, text, handle): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) t = str(text) try: @@ -139,20 +146,22 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString, QString) def sendMessage(self, text, handle): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) textl = [str(text)] + def splittext(l): if len(l[0]) > 450: - space = l[0].rfind(" ", 0,430) + space = l[0].rfind(" ", 0, 430) if space == -1: space = 450 - elif l[0][space+1:space+5] == "": - space = space+4 - a = l[0][0:space+1] - b = l[0][space+1:] + elif l[0][space + 1 : space + 5] == "": + space = space + 4 + a = l[0][0 : space + 1] + b = l[0][space + 1 :] if a.count(" a.count(""): # oh god ctags will break!! D= hanging = [] @@ -161,17 +170,18 @@ class PesterIRC(QtCore.QThread): while c != -1: d = a.find("", c) while d in usedends: - d = a.find("", d+1) - if d != -1: usedends.append(d) + d = a.find("", d + 1) + if d != -1: + usedends.append(d) else: - f = a.find(">", c)+1 + f = a.find(">", c) + 1 hanging.append(a[c:f]) - c = a.rfind("")): + for i in range(a.count("")): a = a + "" - #start them up again in the second part + # start them up again in the second part for c in hanging: b = c + b if len(b) > 0: @@ -180,6 +190,7 @@ class PesterIRC(QtCore.QThread): return [a] else: return l + textl = splittext(textl) try: for t in textl: @@ -187,37 +198,46 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() - @QtCore.pyqtSlot(QString, QString,) + + @QtCore.pyqtSlot( + QString, + QString, + ) def sendCTCP(self, handle, text): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): try: helpers.ctcp(self.cli, handle, text) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString, bool) def startConvo(self, handle, initiated): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) try: - helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) + helpers.msg( + self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd()) + ) if initiated: helpers.msg(self.cli, h, "PESTERCHUM:BEGIN") except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def endConvo(self, handle): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) try: helpers.msg(self.cli, h, "PESTERCHUM:CEASE") except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot() def updateProfile(self): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): me = self.mainwindow.profile() handle = me.handle try: @@ -230,13 +250,14 @@ class PesterIRC(QtCore.QThread): self.mainwindow.autoJoinDone = False self.mainwindow.doAutoJoins() self.updateMood() + @QtCore.pyqtSlot() def updateMood(self): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): me = self.mainwindow.profile() # Moods via metadata try: - helpers.metadata(self.cli, '*', "set", "mood", str(me.mood.value())) + helpers.metadata(self.cli, "*", "set", "mood", str(me.mood.value())) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() @@ -246,63 +267,73 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot() def updateColor(self): - if hasattr(self, 'cli'): - #PchumLog.debug("irc updateColor (outgoing)") - #me = self.mainwindow.profile() + if hasattr(self, "cli"): + # PchumLog.debug("irc updateColor (outgoing)") + # me = self.mainwindow.profile() # Update color metadata field try: color = self.mainwindow.profile().color - helpers.metadata(self.cli, '*', "set", "color", str(color.name())) + helpers.metadata(self.cli, "*", "set", "color", str(color.name())) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() # Send color messages for h in list(self.mainwindow.convos.keys()): try: - helpers.msg(self.cli, h, "COLOR >%s" % (self.mainwindow.profile().colorcmd())) + helpers.msg( + self.cli, + h, + "COLOR >%s" % (self.mainwindow.profile().colorcmd()), + ) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def blockedChum(self, handle): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) try: helpers.msg(self.cli, h, "PESTERCHUM:BLOCK") except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def unblockedChum(self, handle): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) try: helpers.msg(self.cli, h, "PESTERCHUM:UNBLOCK") except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def requestNames(self, channel): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): c = str(channel) try: helpers.names(self.cli, c) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot() def requestChannelList(self): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): try: helpers.channel_list(self.cli) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def joinChannel(self, channel): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): c = str(channel) try: helpers.join(self.cli, c) @@ -310,9 +341,10 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def leftChannel(self, channel): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): c = str(channel) try: helpers.part(self.cli, c) @@ -320,17 +352,18 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString, QString) def kickUser(self, handle, channel): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): l = handle.split(":") c = str(channel) h = str(l[0]) if len(l) > 1: reason = str(l[1]) if len(l) > 2: - for x in l[2:]: - reason += str(":") + str(x) + for x in l[2:]: + reason += str(":") + str(x) else: reason = "" try: @@ -338,13 +371,14 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString, QString, QString) def setChannelMode(self, channel, mode, command): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): c = str(channel) m = str(mode) cmd = str(command) - PchumLog.debug("c=%s\nm=%s\ncmd=%s" % (c,m,cmd)) + PchumLog.debug("c=%s\nm=%s\ncmd=%s" % (c, m, cmd)) if cmd == "": cmd = None try: @@ -352,18 +386,20 @@ class PesterIRC(QtCore.QThread): except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString) def channelNames(self, channel): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): c = str(channel) try: helpers.names(self.cli, c) except OSError as e: PchumLog.warning(e) self.setConnectionBroken() + @QtCore.pyqtSlot(QString, QString) def inviteChum(self, handle, channel): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): h = str(handle) c = str(channel) try: @@ -374,9 +410,9 @@ class PesterIRC(QtCore.QThread): @QtCore.pyqtSlot() def pingServer(self): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): try: - if hasattr(self, 'cli'): + if hasattr(self, "cli"): self.cli.send("PING :B33") except OSError as e: PchumLog.warning(e) @@ -384,7 +420,7 @@ class PesterIRC(QtCore.QThread): @QtCore.pyqtSlot(bool) def setAway(self, away=True): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): try: if away: self.cli.send("AWAY Idle") @@ -396,7 +432,7 @@ class PesterIRC(QtCore.QThread): @QtCore.pyqtSlot(QString, QString) def killSomeQuirks(self, channel, handle): - if hasattr(self, 'cli'): + if hasattr(self, "cli"): c = str(channel) h = str(handle) try: @@ -407,42 +443,47 @@ class PesterIRC(QtCore.QThread): @QtCore.pyqtSlot() def disconnectIRC(self): - if hasattr(self, 'cli'): + 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) - messageReceived = QtCore.pyqtSignal('QString', 'QString') - memoReceived = QtCore.pyqtSignal('QString', 'QString', 'QString') - noticeReceived = QtCore.pyqtSignal('QString', 'QString') - inviteReceived = QtCore.pyqtSignal('QString', 'QString') - timeCommand = QtCore.pyqtSignal('QString', 'QString', 'QString') - namesReceived = QtCore.pyqtSignal('QString', PesterList) + moodUpdated = QtCore.pyqtSignal("QString", Mood) + colorUpdated = QtCore.pyqtSignal("QString", QtGui.QColor) + messageReceived = QtCore.pyqtSignal("QString", "QString") + memoReceived = QtCore.pyqtSignal("QString", "QString", "QString") + noticeReceived = QtCore.pyqtSignal("QString", "QString") + inviteReceived = QtCore.pyqtSignal("QString", "QString") + timeCommand = QtCore.pyqtSignal("QString", "QString", "QString") + 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') + nickCollision = QtCore.pyqtSignal("QString", "QString") + getSvsnickedOn = QtCore.pyqtSignal("QString", "QString") + myHandleChanged = QtCore.pyqtSignal("QString") + chanInviteOnly = QtCore.pyqtSignal("QString") + modesUpdated = QtCore.pyqtSignal("QString", "QString") connected = QtCore.pyqtSignal() askToConnect = QtCore.pyqtSignal(Exception) - userPresentUpdate = QtCore.pyqtSignal('QString', 'QString', - 'QString') - cannotSendToChan = QtCore.pyqtSignal('QString', 'QString') + userPresentUpdate = QtCore.pyqtSignal("QString", "QString", "QString") + cannotSendToChan = QtCore.pyqtSignal("QString", "QString") tooManyPeeps = QtCore.pyqtSignal() - quirkDisable = QtCore.pyqtSignal('QString', 'QString', 'QString') - forbiddenchannel = QtCore.pyqtSignal('QString', 'QString') + quirkDisable = QtCore.pyqtSignal("QString", "QString", "QString") + forbiddenchannel = QtCore.pyqtSignal("QString", "QString") + class PesterHandler(DefaultCommandHandler): def notice(self, nick, chan, msg): - handle = nick[0:nick.find("!")] - PchumLog.info("---> recv \"NOTICE %s :%s\"" % (handle, msg)) - if handle == "ChanServ" and chan == self.parent.mainwindow.profile().handle and msg[0:2] == "[#": - self.parent.memoReceived.emit(msg[1:msg.index("]")], handle, msg) + handle = nick[0 : nick.find("!")] + PchumLog.info('---> recv "NOTICE %s :%s"' % (handle, msg)) + if ( + handle == "ChanServ" + and chan == self.parent.mainwindow.profile().handle + and msg[0:2] == "[#" + ): + self.parent.memoReceived.emit(msg[1 : msg.index("]")], handle, msg) else: self.parent.noticeReceived.emit(handle, msg) + def metadata(self, target, nick, key, visibility, value): # The format of the METADATA server notication is: # METADATA @@ -453,35 +494,37 @@ class PesterHandler(DefaultCommandHandler): except ValueError: PchumLog.warning("Invalid mood value, %s, %s" % (nick, mood)) elif key.lower() == "color": - color = QtGui.QColor(value) # Invalid color becomes rgb 0,0,0 + color = QtGui.QColor(value) # Invalid color becomes rgb 0,0,0 self.parent.colorUpdated.emit(nick, color) def tagmsg(self, prefix, tags, *args): - PchumLog.info('TAGMSG: %s %s %s' % (prefix, tags, str(args))) - message_tags = tags[1:].split(';') + PchumLog.info("TAGMSG: %s %s %s" % (prefix, tags, str(args))) + message_tags = tags[1:].split(";") for m in message_tags: if m.startswith("+pesterchum"): # Pesterchum tag try: - key, value = m.split('=') + key, value = m.split("=") except ValueError: return - PchumLog.info('Pesterchum tag: %s=%s' % (key, value)) + PchumLog.info("Pesterchum tag: %s=%s" % (key, value)) # PESTERCHUM: syntax check - if ((value == "BEGIN") + if ( + (value == "BEGIN") or (value == "BLOCK") or (value == "CEASE") or (value == "BLOCK") or (value == "BLOCKED") or (value == "UNBLOCK") or (value == "IDLE") - or (value == "ME")): + or (value == "ME") + ): # Process like it's a PESTERCHUM: PRIVMSG msg = "PESTERCHUM:" + value self.privmsg(prefix, args[0], msg) elif value.startswith("COLOR>"): # Process like it's a COLOR >0,0,0 PRIVMSG - msg = value.replace('>', " >") + msg = value.replace(">", " >") self.privmsg(prefix, args[0], msg) elif value.startswith("TIME>"): # Process like it's a PESTERCHUM:TIME> PRIVMSG @@ -490,46 +533,60 @@ class PesterHandler(DefaultCommandHandler): else: # Invalid syntax PchumLog.warning("TAGMSG with invalid syntax.") + def error(self, *params): # Server is ending connection. - reason = '' + reason = "" for x in params: - if (x != None) and (x != ''): - reason += x + ' ' + if (x != None) and (x != ""): + reason += x + " " self.parent.stopIRC = reason.strip() self.parent.disconnectIRC() - + def privmsg(self, nick, chan, msg): - handle = nick[0:nick.find("!")] + handle = nick[0 : nick.find("!")] if len(msg) == 0: return # CTCP # ACTION, IRC /me (The CTCP kind) - if msg[0:8] == '\x01ACTION ': - msg = '/me' + msg[7:-1] + if msg[0:8] == "\x01ACTION ": + msg = "/me" + msg[7:-1] # CTCPs that don't need to be shown - elif msg[0] == '\x01': - PchumLog.info("---> recv \"CTCP %s :%s\"" % (handle, msg[1:-1])) + elif msg[0] == "\x01": + PchumLog.info('---> recv "CTCP %s :%s"' % (handle, msg[1:-1])) # VERSION, return version if msg[1:-1].startswith("VERSION"): - helpers.ctcp_reply(self.parent.cli, handle, "VERSION", "Pesterchum %s" % (_pcVersion)) - # CLIENTINFO, return supported CTCP commands. + helpers.ctcp_reply( + self.parent.cli, handle, "VERSION", "Pesterchum %s" % (_pcVersion) + ) + # CLIENTINFO, return supported CTCP commands. elif msg[1:-1].startswith("CLIENTINFO"): - helpers.ctcp_reply(self.parent.cli, handle, "CLIENTINFO", - "ACTION VERSION CLIENTINFO PING SOURCE NOQUIRKS GETMOOD") + helpers.ctcp_reply( + self.parent.cli, + handle, + "CLIENTINFO", + "ACTION VERSION CLIENTINFO PING SOURCE NOQUIRKS GETMOOD", + ) # PING, return pong elif msg[1:-1].startswith("PING"): if len(msg[1:-1].split("PING ")) > 1: - helpers.ctcp_reply(self.parent.cli, handle, "PING", msg[1:-1].split("PING ")[1]) + helpers.ctcp_reply( + self.parent.cli, handle, "PING", msg[1:-1].split("PING ")[1] + ) else: helpers.ctcp_reply(self.parent.cli, handle, "PING") # SOURCE, return source elif msg[1:-1].startswith("SOURCE"): - helpers.ctcp_reply(self.parent.cli, handle, "SOURCE", "https://github.com/Dpeta/pesterchum-alt-servers") + helpers.ctcp_reply( + self.parent.cli, + handle, + "SOURCE", + "https://github.com/Dpeta/pesterchum-alt-servers", + ) # ??? elif msg[1:-1].startswith("NOQUIRKS") and chan[0] == "#": - op = nick[0:nick.find("!")] + op = nick[0 : nick.find("!")] self.parent.quirkDisable.emit(chan, msg[10:-1], op) # GETMOOD via CTCP elif msg[1:-1].startswith("GETMOOD"): @@ -543,7 +600,7 @@ class PesterHandler(DefaultCommandHandler): if chan != "#pesterchum": # We don't need anywhere near that much spam. - PchumLog.info("---> recv \"PRIVMSG %s :%s\"" % (handle, msg)) + PchumLog.info('---> recv "PRIVMSG %s :%s"' % (handle, msg)) if chan == "#pesterchum": # follow instructions @@ -557,9 +614,8 @@ class PesterHandler(DefaultCommandHandler): mychumhandle = self.mainwindow.profile().handle mymood = self.mainwindow.profile().mood.value() if msg.find(mychumhandle, 8) != -1: - helpers.msg(self.client, "#pesterchum", - "MOOD >%d" % (mymood)) - elif chan[0] == '#': + helpers.msg(self.client, "#pesterchum", "MOOD >%d" % (mymood)) + elif chan[0] == "#": if msg[0:16] == "PESTERCHUM:TIME>": self.parent.timeCommand.emit(chan, handle, msg[16:]) else: @@ -575,39 +631,42 @@ class PesterHandler(DefaultCommandHandler): colors = [int(d) for d in colors] except ValueError as e: PchumLog.warning(e) - colors = [0,0,0] + colors = [0, 0, 0] PchumLog.debug("colors: " + str(colors)) color = QtGui.QColor(*colors) self.parent.colorUpdated.emit(handle, color) else: self.parent.messageReceived.emit(handle, msg) - def pong(self, *args): # source, server, token - #print("PONG", source, server, token) - #self.parent.mainwindow.lastrecv = time.time() - #print("PONG TIME: %s" % self.parent.mainwindow.lastpong) + # print("PONG", source, server, token) + # self.parent.mainwindow.lastrecv = time.time() + # print("PONG TIME: %s" % self.parent.mainwindow.lastpong) pass - + def welcome(self, server, nick, msg): self.parent.setConnected() - #mychumhandle = self.mainwindow.profile().handle + # mychumhandle = self.mainwindow.profile().handle mymood = self.mainwindow.profile().mood.value() color = self.mainwindow.profile().color if not self.mainwindow.config.lowBandwidth(): # Negotiate capabilities helpers.cap(self.client, "REQ", "message-tags") - helpers.cap(self.client, "REQ", "draft/metadata-notify-2") # <--- Not required in the unreal5 module implementation - helpers.cap(self.client, "REQ", "pesterchum-tag") # <--- Currently not using this - time.sleep(0.413 + 0.097) # <--- somehow, this actually helps. + helpers.cap( + self.client, "REQ", "draft/metadata-notify-2" + ) # <--- Not required in the unreal5 module implementation + helpers.cap( + self.client, "REQ", "pesterchum-tag" + ) # <--- Currently not using this + time.sleep(0.413 + 0.097) # <--- somehow, this actually helps. helpers.join(self.client, "#pesterchum") # Moods via metadata - helpers.metadata(self.client, '*', 'sub', 'mood') - helpers.metadata(self.client, '*', "set", "mood", str(mymood)) + helpers.metadata(self.client, "*", "sub", "mood") + helpers.metadata(self.client, "*", "set", "mood", str(mymood)) # Color via metadata - helpers.metadata(self.client, '*', 'sub', 'color') - helpers.metadata(self.client, '*', "set", "color", str(color.name())) + helpers.metadata(self.client, "*", "sub", "color") + helpers.metadata(self.client, "*", "set", "color", str(color.name())) # Backwards compatible moods helpers.msg(self.client, "#pesterchum", "MOOD >%d" % (mymood)) @@ -615,8 +674,8 @@ class PesterHandler(DefaultCommandHandler): # Server is not allowing us to connect. reason = "Handle is not allowed on this server.\n" for x in args: - if (x != None) and (x != ''): - reason += x + ' ' + if (x != None) and (x != ""): + reason += x + " " self.parent.stopIRC = reason.strip() self.parent.disconnectIRC() @@ -626,7 +685,7 @@ class PesterHandler(DefaultCommandHandler): if key == "mood": mood = Mood(int(value[0])) self.parent.moodUpdated.emit(handle_owner, mood) - + def metadatasubok(self, *params): PchumLog.info("metadatasubok: " + str(params)) @@ -672,59 +731,62 @@ class PesterHandler(DefaultCommandHandler): def cap(self, server, nick, subcommand, tag): PchumLog.info("CAP %s %s %s %s" % (server, nick, subcommand, tag)) - #if tag == "message-tags": + # if tag == "message-tags": # if subcommand == "ACK": - - + def nicknameinuse(self, server, cmd, nick, msg): - newnick = "pesterClient%d" % (random.randint(100,999)) + 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)) + 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)) + handle = nick[0 : nick.find("!")] + PchumLog.info('---> recv "QUIT %s: %s"' % (handle, reason)) if handle == self.parent.mainwindow.randhandler.randNick: self.parent.mainwindow.randhandler.setRunning(False) server = self.parent.mainwindow.config.server() - baseserver = server[server.rfind(".", 0, server.rfind(".")):] + baseserver = server[server.rfind(".", 0, server.rfind(".")) :] if reason.count(baseserver) == 2: self.parent.userPresentUpdate.emit(handle, "", "netsplit") else: self.parent.userPresentUpdate.emit(handle, "", "quit") self.parent.moodUpdated.emit(handle, Mood("offline")) + def kick(self, opnick, channel, handle, reason): - op = opnick[0:opnick.find("!")] + op = opnick[0 : opnick.find("!")] self.parent.userPresentUpdate.emit(handle, channel, "kick:%s:%s" % (op, reason)) # ok i shouldnt be overloading that but am lazy + def part(self, nick, channel, reason="nanchos"): - handle = nick[0:nick.find("!")] - PchumLog.info("---> recv \"PART %s: %s\"" % (handle, channel)) + handle = nick[0 : nick.find("!")] + PchumLog.info('---> recv "PART %s: %s"' % (handle, channel)) self.parent.userPresentUpdate.emit(handle, channel, "left") if channel == "#pesterchum": self.parent.moodUpdated.emit(handle, Mood("offline")) + def join(self, nick, channel): - handle = nick[0:nick.find("!")] - PchumLog.info("---> recv \"JOIN %s: %s\"" % (handle, channel)) + handle = nick[0 : nick.find("!")] + PchumLog.info('---> recv "JOIN %s: %s"' % (handle, channel)) self.parent.userPresentUpdate.emit(handle, channel, "join") if channel == "#pesterchum": if handle == self.parent.mainwindow.randhandler.randNick: self.parent.mainwindow.randhandler.setRunning(True) self.parent.moodUpdated.emit(handle, Mood("chummy")) - + def mode(self, op, channel, mode, *handles): PchumLog.debug("op=" + str(op)) PchumLog.debug("channel=" + str(channel)) PchumLog.debug("mode=" + str(mode)) PchumLog.debug("*handles=" + str(handles)) - if len(handles) <= 0: handles = [""] - opnick = op[0:op.find("!")] + if len(handles) <= 0: + handles = [""] + opnick = op[0 : op.find("!")] PchumLog.debug("opnick=" + opnick) # Channel section @@ -732,7 +794,7 @@ class PesterHandler(DefaultCommandHandler): # So "MODE #channel +ro handleHandle" will set +r to channel #channel as well as set +o to handleHandle # Therefore the bellow method causes a crash if both user and channel mode are being set in one command. - #if op == channel or channel == self.parent.mainwindow.profile().handle: + # if op == channel or channel == self.parent.mainwindow.profile().handle: # modes = list(self.parent.mainwindow.modes) # if modes and modes[0] == "+": modes = modes[1:] # if mode[0] == "+": @@ -748,59 +810,96 @@ class PesterHandler(DefaultCommandHandler): # modes.sort() # self.parent.mainwindow.modes = "+" + "".join(modes) - # EXPIRIMENTAL FIX # No clue how stable this is but since it doesn't seem to cause a crash it's probably an improvement. # This might be clunky with non-unrealircd IRC servers channel_mode = "" - unrealircd_channel_modes = ['c', 'C', 'd', 'f', 'G', 'H', 'i', 'k', 'K', 'L', 'l', 'm', 'M', 'N', 'n', 'O', 'P', 'p', 'Q', 'R', 'r', 's', 'S', 'T', 't', 'V', 'z', 'Z'] + unrealircd_channel_modes = [ + "c", + "C", + "d", + "f", + "G", + "H", + "i", + "k", + "K", + "L", + "l", + "m", + "M", + "N", + "n", + "O", + "P", + "p", + "Q", + "R", + "r", + "s", + "S", + "T", + "t", + "V", + "z", + "Z", + ] if any(md in mode for md in unrealircd_channel_modes): PchumLog.debug("Channel mode in string.") modes = list(self.parent.mainwindow.modes) for md in unrealircd_channel_modes: - if mode.find(md)!= -1: # -1 means not found + if mode.find(md) != -1: # -1 means not found PchumLog.debug("md=" + md) if mode[0] == "+": modes.extend(md) - channel_mode = "+" + md + channel_mode = "+" + md elif mode[0] == "-": try: modes.remove(md) channel_mode = "-" + md except ValueError: - PchumLog.warning("Can't remove channel mode that isn't set.") + PchumLog.warning( + "Can't remove channel mode that isn't set." + ) pass - self.parent.userPresentUpdate.emit("", channel, channel_mode+":%s" % (op)) + self.parent.userPresentUpdate.emit( + "", channel, channel_mode + ":%s" % (op) + ) PchumLog.debug("pre-mode=" + str(mode)) mode = mode.replace(md, "") PchumLog.debug("post-mode=" + str(mode)) modes.sort() self.parent.mainwindow.modes = "+" + "".join(modes) - + modes = [] cur = "+" for l in mode: - if l in ["+","-"]: cur = l + if l in ["+", "-"]: + cur = l else: modes.append("%s%s" % (cur, l)) PchumLog.debug("handles=" + str(handles)) PchumLog.debug("enumerate(modes) = " + str(list(enumerate(modes)))) - for (i,m) in enumerate(modes): + for (i, m) in enumerate(modes): # Server-set usermodes don't need to be passed. - if (handles == ['']) & ( ('x' in m) | ('z' in m) | ('o' in m) | ('x' in m) )!=True: + if (handles == [""]) & ( + ("x" in m) | ("z" in m) | ("o" in m) | ("x" in m) + ) != True: try: - self.parent.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op)) + self.parent.userPresentUpdate.emit( + handles[i], channel, m + ":%s" % (op) + ) except IndexError as e: PchumLog.exception("modeSetIndexError: %s" % e) - #print("i = " + i) - #print("m = " + m) - #self.parent.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op)) - #self.parent.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op)) + # print("i = " + i) + # print("m = " + m) + # self.parent.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op)) + # self.parent.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op)) # Passing an empty handle here might cause a crash. - #except IndexError: - #self.parent.userPresentUpdate.emit("", channel, m+":%s" % (op)) - + # except IndexError: + # self.parent.userPresentUpdate.emit("", channel, m+":%s" % (op)) + def nick(self, oldnick, newnick, hopcount=0): PchumLog.info("%s, %s" % (oldnick, newnick)) # svsnick @@ -809,10 +908,11 @@ class PesterHandler(DefaultCommandHandler): self.parent.getSvsnickedOn.emit(oldnick, newnick) # etc. - oldhandle = oldnick[0:oldnick.find("!")] - if ((oldhandle == self.mainwindow.profile().handle) - or (newnick == self.mainwindow.profile().handle)): - #print('hewwo') + oldhandle = oldnick[0 : oldnick.find("!")] + if (oldhandle == self.mainwindow.profile().handle) or ( + newnick == self.mainwindow.profile().handle + ): + # print('hewwo') self.parent.myHandleChanged.emit(newnick) newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb) self.parent.moodUpdated.emit(oldhandle, Mood("offline")) @@ -820,18 +920,20 @@ class PesterHandler(DefaultCommandHandler): if newnick in self.mainwindow.chumList.chums: self.getMood(newchum) if oldhandle == self.parent.mainwindow.randhandler.randNick: - self.parent.mainwindow.randhandler.setRunning(False) + self.parent.mainwindow.randhandler.setRunning(False) elif newnick == self.parent.mainwindow.randhandler.randNick: - self.parent.mainwindow.randhandler.setRunning(True) + self.parent.mainwindow.randhandler.setRunning(True) + def namreply(self, server, nick, op, channel, names): namelist = names.split(" ") - PchumLog.info("---> recv \"NAMES %s: %d names\"" % (channel, len(namelist))) - if not hasattr(self, 'channelnames'): + PchumLog.info('---> recv "NAMES %s: %d names"' % (channel, len(namelist))) + if not hasattr(self, "channelnames"): self.channelnames = {} if channel not in self.channelnames: self.channelnames[channel] = [] self.channelnames[channel].extend(namelist) - #def ison(self, server, nick, nicks): + + # def ison(self, server, nick, nicks): # nicklist = nicks.split(" ") # getglub = "GETMOOD " # PchumLog.info("---> recv \"ISON :%s\"" % nicks) @@ -841,7 +943,7 @@ class PesterHandler(DefaultCommandHandler): # getglub += nick_it # if getglub != "GETMOOD ": # helpers.msg(self.client, "#pesterchum", getglub) - + def endofnames(self, server, nick, channel, msg): try: namelist = self.channelnames[channel] @@ -854,50 +956,62 @@ class PesterHandler(DefaultCommandHandler): pl = PesterList(namelist) del self.channelnames[channel] self.parent.namesReceived.emit(channel, pl) - if channel == "#pesterchum" and (not hasattr(self, "joined") or not self.joined): + if channel == "#pesterchum" and ( + not hasattr(self, "joined") or not self.joined + ): self.joined = True - self.parent.mainwindow.randhandler.setRunning(self.parent.mainwindow.randhandler.randNick in namelist) + self.parent.mainwindow.randhandler.setRunning( + self.parent.mainwindow.randhandler.randNick in namelist + ) chums = self.mainwindow.chumList.chums - #self.isOn(*chums) + # self.isOn(*chums) lesschums = [] for c in chums: chandle = c.handle if chandle in namelist: lesschums.append(c) self.getMood(*lesschums) - + def liststart(self, server, handle, *info): self.channel_list = [] info = list(info) - self.channel_field = info.index("Channel") # dunno if this is protocol - PchumLog.info("---> recv \"CHANNELS: %s " % (self.channel_field)) + self.channel_field = info.index("Channel") # dunno if this is protocol + PchumLog.info('---> recv "CHANNELS: %s ' % (self.channel_field)) + def list(self, server, handle, *info): channel = info[self.channel_field] usercount = info[1] if channel not in self.channel_list and channel != "#pesterchum": self.channel_list.append((channel, usercount)) - PchumLog.info("---> recv \"CHANNELS: %s " % (channel)) + PchumLog.info('---> recv "CHANNELS: %s ' % (channel)) + def listend(self, server, handle, msg): pl = PesterList(self.channel_list) - PchumLog.info("---> recv \"CHANNELS END\"") + PchumLog.info('---> recv "CHANNELS END"') self.parent.channelListReceived.emit(pl) self.channel_list = [] def umodeis(self, server, handle, modes): self.parent.mainwindow.modes = modes + def invite(self, sender, you, channel): - handle = sender.split('!')[0] + handle = sender.split("!")[0] self.parent.inviteReceived.emit(handle, channel) + def inviteonlychan(self, server, handle, channel, msg): self.parent.chanInviteOnly.emit(channel) + # channelmodeis can have six arguments. def channelmodeis(self, server, handle, channel, modes, mode_params=""): self.parent.modesUpdated.emit(channel, modes) + def cannotsendtochan(self, server, handle, channel, msg): self.parent.cannotSendToChan.emit(channel, msg) + def toomanypeeps(self, *stuff): self.parent.tooManyPeeps.emit() - #def badchanmask(channel, *args): + + # def badchanmask(channel, *args): # # Channel name is not valid. # msg = ' '.join(args) # self.parent.forbiddenchannel.emit(channel, msg) @@ -905,10 +1019,10 @@ class PesterHandler(DefaultCommandHandler): # Channel is forbidden. self.parent.forbiddenchannel.emit(channel, msg) self.parent.userPresentUpdate.emit(handle, channel, "left") - + def ping(self, prefix, server): - #self.parent.mainwindow.lastping = time.time() - self.client.send('PONG', server) + # self.parent.mainwindow.lastping = time.time() + self.client.send("PONG", server) def getMood(self, *chums): """Get mood via metadata if supported""" @@ -925,11 +1039,13 @@ class PesterHandler(DefaultCommandHandler): self.parent.setConnectionBroken() else: # Legacy - PchumLog.warning("Server doesn't seem to support metadata, using legacy GETMOOD.") + PchumLog.warning( + "Server doesn't seem to support metadata, using legacy GETMOOD." + ) chumglub = "GETMOOD " for c in chums: chandle = c.handle - if len(chumglub+chandle) >= 350: + if len(chumglub + chandle) >= 350: try: helpers.msg(self.client, "#pesterchum", chumglub) except OSError as e: @@ -943,9 +1059,8 @@ class PesterHandler(DefaultCommandHandler): except OSError as e: PchumLog.warning(e) self.parent.setConnectionBroken() - - #def isOn(self, *chums): + # def isOn(self, *chums): # isonNicks = "" # for c in chums: # chandle = c.handle diff --git a/logviewer.py b/logviewer.py index e26c586..b079fde 100644 --- a/logviewer.py +++ b/logviewer.py @@ -4,6 +4,7 @@ import codecs import re import ostools from time import strftime, strptime + try: from PyQt6 import QtCore, QtGui, QtWidgets except ImportError: @@ -15,13 +16,15 @@ from convo import PesterText _datadir = ostools.getDataDir() + class PesterLogSearchInput(QtWidgets.QLineEdit): def __init__(self, theme, parent=None): QtWidgets.QLineEdit.__init__(self, parent) self.setStyleSheet(theme["convo/input/style"] + "; margin-right:0px;") + def keyPressEvent(self, event): QtWidgets.QLineEdit.keyPressEvent(self, event) - if hasattr(self.parent(), 'textArea'): + if hasattr(self.parent(), "textArea"): if event.key() == QtCore.Qt.Key.Key_Return: self.parent().logSearch(self.text()) if self.parent().textArea.find(self.text()): @@ -29,6 +32,7 @@ class PesterLogSearchInput(QtWidgets.QLineEdit): else: self.parent().logSearch(self.text()) + class PesterLogHighlighter(QtGui.QSyntaxHighlighter): def __init__(self, parent): QtGui.QSyntaxHighlighter.__init__(self, parent) @@ -36,11 +40,16 @@ class PesterLogHighlighter(QtGui.QSyntaxHighlighter): self.hilightstyle = QtGui.QTextCharFormat() self.hilightstyle.setBackground(QtGui.QBrush(QtCore.Qt.GlobalColor.green)) self.hilightstyle.setForeground(QtGui.QBrush(QtCore.Qt.GlobalColor.black)) + def highlightBlock(self, text): - for i in range(0, len(text)-(len(self.searchTerm)-1)): - if str(text[i:i+len(self.searchTerm)]).lower() == str(self.searchTerm).lower(): + for i in range(0, len(text) - (len(self.searchTerm) - 1)): + if ( + str(text[i : i + len(self.searchTerm)]).lower() + == str(self.searchTerm).lower() + ): self.setFormat(i, len(self.searchTerm), self.hilightstyle) + class PesterLogUserSelect(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) @@ -49,7 +58,7 @@ class PesterLogUserSelect(QtWidgets.QDialog): self.theme = theme self.parent = parent self.handle = parent.profile().handle - self.logpath = _datadir+"logs" + self.logpath = _datadir + "logs" self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.setWindowTitle("Pesterlogs") @@ -72,7 +81,9 @@ class PesterLogUserSelect(QtWidgets.QDialog): for (i, t) in enumerate(chumMemoList): item = QtWidgets.QListWidgetItem(t) - item.setForeground(QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"]))) + item.setForeground( + QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) + ) self.chumsBox.addItem(item) self.search = PesterLogSearchInput(theme, self) @@ -109,10 +120,12 @@ class PesterLogUserSelect(QtWidgets.QDialog): @QtCore.pyqtSlot() def viewActivatedLog(self): selectedchum = self.selectedchum().text() - if not hasattr(self, 'pesterlogviewer'): + if not hasattr(self, "pesterlogviewer"): self.pesterlogviewer = None if not self.pesterlogviewer: - self.pesterlogviewer = PesterLogViewer(selectedchum, self.config, self.theme, self.parent) + self.pesterlogviewer = PesterLogViewer( + selectedchum, self.config, self.theme, self.parent + ) self.pesterlogviewer.rejected.connect(self.closeActiveLog) self.pesterlogviewer.show() self.pesterlogviewer.raise_() @@ -126,7 +139,13 @@ class PesterLogUserSelect(QtWidgets.QDialog): @QtCore.pyqtSlot() def openDir(self): - QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + os.path.join(_datadir, "logs"), QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl( + "file:///" + os.path.join(_datadir, "logs"), + QtCore.QUrl.ParsingMode.TolerantMode, + ) + ) + class PesterLogViewer(QtWidgets.QDialog): def __init__(self, chum, config, theme, parent): @@ -140,18 +159,27 @@ class PesterLogViewer(QtWidgets.QDialog): self.handle = parent.profile().handle self.chum = chum self.convos = {} - self.logpath = _datadir+"logs" + self.logpath = _datadir + "logs" self.setStyleSheet(self.theme["main/defaultwindow/style"]) self.setWindowTitle("Pesterlogs with " + self.chum) self.format = "bbcode" - if os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)): - self.logList = os.listdir("%s/%s/%s/%s/" % (self.logpath, self.handle, self.chum, self.format)) + if os.path.exists( + "%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format) + ): + self.logList = os.listdir( + "%s/%s/%s/%s/" % (self.logpath, self.handle, self.chum, self.format) + ) else: self.logList = [] - if not os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)) or len(self.logList) == 0: + if ( + not os.path.exists( + "%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format) + ) + or len(self.logList) == 0 + ): instructions = QtWidgets.QLabel("No Pesterlogs were found") self.ok = QtWidgets.QPushButton("CLOSE", self) @@ -166,15 +194,28 @@ class PesterLogViewer(QtWidgets.QDialog): self.setLayout(layout_0) else: - self.instructions = QtWidgets.QLabel("Pesterlog with " +self.chum+ " on") + self.instructions = QtWidgets.QLabel("Pesterlog with " + self.chum + " on") self.textArea = PesterLogText(theme, self.parent) self.textArea.setReadOnly(True) self.textArea.setFixedWidth(600) if "convo/scrollbar" in theme: - self.textArea.setStyleSheet("QTextEdit { width:500px; %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] )) + self.textArea.setStyleSheet( + "QTextEdit { width:500px; %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" + % ( + theme["convo/textarea/style"], + theme["convo/scrollbar/style"], + theme["convo/scrollbar/handle"], + theme["convo/scrollbar/downarrow"], + theme["convo/scrollbar/uparrow"], + theme["convo/scrollbar/uarrowstyle"], + theme["convo/scrollbar/darrowstyle"], + ) + ) else: - self.textArea.setStyleSheet("QTextEdit { width:500px; %s }" % (theme["convo/textarea/style"])) + self.textArea.setStyleSheet( + "QTextEdit { width:500px; %s }" % (theme["convo/textarea/style"]) + ) self.logList.sort() self.logList.reverse() @@ -184,20 +225,31 @@ class PesterLogViewer(QtWidgets.QDialog): self.tree.setFixedSize(260, 300) self.tree.header().hide() if "convo/scrollbar" in theme: - self.tree.setStyleSheet("QTreeWidget { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] )) + self.tree.setStyleSheet( + "QTreeWidget { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" + % ( + theme["convo/textarea/style"], + theme["convo/scrollbar/style"], + theme["convo/scrollbar/handle"], + theme["convo/scrollbar/downarrow"], + theme["convo/scrollbar/uparrow"], + theme["convo/scrollbar/uarrowstyle"], + theme["convo/scrollbar/darrowstyle"], + ) + ) else: self.tree.setStyleSheet("%s" % (theme["convo/textarea/style"])) self.tree.itemSelectionChanged.connect(self.loadSelectedLog) self.tree.setSortingEnabled(False) child_1 = None - last = ["",""] - #blackbrush = QtGui.QBrush(QtCore.Qt.GlobalColor.black) - for (i,l) in enumerate(self.logList): + last = ["", ""] + # blackbrush = QtGui.QBrush(QtCore.Qt.GlobalColor.black) + for (i, l) in enumerate(self.logList): my = self.fileToMonthYear(l) if my[0] != last[0]: child_1 = QtWidgets.QTreeWidgetItem(["%s %s" % (my[0], my[1])]) - #child_1.setForeground(0, blackbrush) + # child_1.setForeground(0, blackbrush) self.tree.addTopLevelItem(child_1) if i == 0: child_1.setExpanded(True) @@ -205,7 +257,8 @@ class PesterLogViewer(QtWidgets.QDialog): last = self.fileToMonthYear(l) self.hilight = PesterLogHighlighter(self.textArea) - if len(self.logList) > 0: self.loadLog(self.logList[0]) + if len(self.logList) > 0: + self.loadLog(self.logList[0]) self.search = PesterLogSearchInput(theme, self) self.search.setFocus() @@ -246,29 +299,48 @@ class PesterLogViewer(QtWidgets.QDialog): self.loadLog(self.timeToFile(self.tree.currentItem().text(0))) def loadLog(self, fname): - fp = codecs.open("%s/%s/%s/%s/%s" % (self.logpath, self.handle, self.chum, self.format, fname), encoding='utf-8', mode='r') + fp = codecs.open( + "%s/%s/%s/%s/%s" + % (self.logpath, self.handle, self.chum, self.format, fname), + encoding="utf-8", + mode="r", + ) self.textArea.clear() for line in fp: - cline = line.replace("\r\n", "").replace("[/color]","").replace("[url]","").replace("[/url]","") + cline = ( + line.replace("\r\n", "") + .replace("[/color]", "") + .replace("[url]", "") + .replace("[/url]", "") + ) cline = re.sub("\[color=(#.{6})]", r"", cline) self.textArea.append(convertTags(cline)) textCur = self.textArea.textCursor() - #textCur.movePosition(1) + # textCur.movePosition(1) self.textArea.setTextCursor(textCur) - self.instructions.setText("Pesterlog with " +self.chum+ " on " + self.fileToTime(str(fname))) + self.instructions.setText( + "Pesterlog with " + self.chum + " on " + self.fileToTime(str(fname)) + ) def logSearch(self, search): self.hilight.searchTerm = search self.hilight.rehighlight() def fileToMonthYear(self, fname): - time = strptime(fname[(fname.index(".")+1):fname.index(".txt")], "%Y-%m-%d.%H.%M") + time = strptime( + fname[(fname.index(".") + 1) : fname.index(".txt")], "%Y-%m-%d.%H.%M" + ) return [strftime("%B", time), strftime("%Y", time)] + def fileToTime(self, fname): - timestr = fname[(fname.index(".")+1):fname.index(".txt")] + timestr = fname[(fname.index(".") + 1) : fname.index(".txt")] return strftime("%a %d %b %Y %H %M", strptime(timestr, "%Y-%m-%d.%H.%M")) + def timeToFile(self, time): - return self.chum + strftime(".%Y-%m-%d.%H.%M.txt", strptime(str(time), "%a %d %b %Y %H %M")) + return self.chum + strftime( + ".%Y-%m-%d.%H.%M.txt", strptime(str(time), "%a %d %b %Y %H %M") + ) + class PesterLogText(PesterText): def __init__(self, theme, parent=None): @@ -276,6 +348,7 @@ class PesterLogText(PesterText): def focusInEvent(self, event): QtWidgets.QTextEdit.focusInEvent(self, event) + def mousePressEvent(self, event): try: # PyQt6 @@ -290,8 +363,11 @@ class PesterLogText(PesterText): handle = str(url[1:]) self.parent().parent.newConversation(handle) else: - QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl(url, QtCore.QUrl.ParsingMode.TolerantMode) + ) QtWidgets.QTextEdit.mousePressEvent(self, event) + def mouseMoveEvent(self, event): QtWidgets.QTextEdit.mouseMoveEvent(self, event) try: @@ -301,8 +377,13 @@ class PesterLogText(PesterText): # PyQt5 pos = event.pos() if self.anchorAt(pos): - if self.viewport().cursor().shape != QtCore.Qt.CursorShape.PointingHandCursor: - self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) + if ( + self.viewport().cursor().shape + != QtCore.Qt.CursorShape.PointingHandCursor + ): + self.viewport().setCursor( + QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor) + ) else: self.viewport().setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor)) diff --git a/memos.py b/memos.py index 2884ae3..30e8be5 100644 --- a/memos.py +++ b/memos.py @@ -16,15 +16,22 @@ import parsetools from dataobjs import PesterProfile, PesterHistory from generic import PesterIcon, RightClickList, mysteryTime from convo import PesterConvo, PesterInput, PesterText, PesterTabWindow -from parsetools import (convertTags, timeProtocol, lexMessage, colorBegin, - mecmd, smiledict) +from parsetools import ( + convertTags, + timeProtocol, + lexMessage, + colorBegin, + mecmd, + smiledict, +) from logviewer import PesterLogViewer -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") # Python 3 QString = str + def delta2txt(d, format="pc"): if type(d) is mysteryTime: return "?" @@ -35,7 +42,7 @@ def delta2txt(d, format="pc"): return "i" sign = "F" if d >= timedelta(0) else "P" d = abs(d) - totalminutes = (d.days*86400 + d.seconds) // 60 + totalminutes = (d.days * 86400 + d.seconds) // 60 hours = totalminutes // 60 leftovermins = totalminutes % 60 if hours < 100: @@ -49,13 +56,14 @@ def delta2txt(d, format="pc"): else: return "%s%02d:%02d" % (sign, hours, leftovermins) + def txt2delta(txt): sign = 1 - if txt[0] == '?': + if txt[0] == "?": return mysteryTime() - if txt[0] == '+': + if txt[0] == "+": txt = txt[1:] - elif txt[0] == '-': + elif txt[0] == "-": sign = -1 txt = txt[1:] l = txt.split(":") @@ -64,7 +72,7 @@ def txt2delta(txt): m = 0 if len(l) > 1: m = int(l[1]) - timed = timedelta(0, h*3600+m*60) + timed = timedelta(0, h * 3600 + m * 60) except ValueError: timed = timedelta(0) except OverflowError: @@ -72,10 +80,11 @@ def txt2delta(txt): return timedelta.min else: return timedelta.max - return sign*timed + return sign * timed + def pcfGrammar(td): - if td == timedelta(microseconds=1): # Replacement for mysteryTime 1: - return self.timerecord[pcf].index(timed)+1 + return self.timerecord[pcf].index(timed) + 1 else: return 0 + def removeTime(self, timed): try: self.pop(self.index(timed)) - self.current = len(self)-1 + self.current = len(self) - 1 del self.open[timed] return True except ValueError: return None + def openTime(self, time): if time in self.open: self.open[time] = True + def openCurrentTime(self): timed = self.getTime() self.openTime(timed) + def isFirstTime(self): timed = self.getTime() return not self.open[timed] + def getTime(self): if self.current >= 0: return self[self.current] else: return None + def getGrammar(self): timed = self.getTime() return self.getGrammarTime(timed) + def getGrammarTime(self, timed): mytime = timedelta(0) try: @@ -198,6 +222,7 @@ class TimeTracker(list): return TimeGrammar(temporal, pcf, when, 0) return TimeGrammar(temporal, pcf, when, self.getRecord(timed)) + class TimeInput(QtWidgets.QLineEdit): def __init__(self, timeslider, parent): super(TimeInput, self).__init__(parent) @@ -205,9 +230,11 @@ class TimeInput(QtWidgets.QLineEdit): self.setText("+0:00") self.timeslider.valueChanged[int].connect(self.setTime) self.editingFinished.connect(self.setSlider) + @QtCore.pyqtSlot(int) def setTime(self, sliderval): self.setText(self.timeslider.getTime()) + @QtCore.pyqtSlot() def setSlider(self): value = str(self.text()) @@ -221,12 +248,13 @@ class TimeInput(QtWidgets.QLineEdit): index = 50 for i, td in enumerate(timedlist): if abstimed < td: - index = i-1 + index = i - 1 break - self.timeslider.setValue(sign*index) + self.timeslider.setValue(sign * index) text = delta2txt(timed) self.setText(text) + class TimeSlider(QtWidgets.QSlider): def __init__(self, orientation, parent): super(TimeSlider, self).__init__(orientation, parent) @@ -235,35 +263,44 @@ class TimeSlider(QtWidgets.QSlider): self.setMaximum(50) self.setValue(0) self.setPageStep(1) + def getTime(self): time = timelist[abs(self.value())] sign = "+" if self.value() >= 0 else "-" - return sign+time + return sign + time + def mouseDoubleClickEvent(self, event): self.setValue(0) + class MemoTabWindow(PesterTabWindow): def __init__(self, mainwindow, parent=None): super(MemoTabWindow, self).__init__(mainwindow, parent, "memos") + def addChat(self, convo): self.convos[convo.channel] = convo # either addTab or setCurrentIndex will trigger changed() newindex = self.tabs.addTab(convo.channel) self.tabIndices[convo.channel] = newindex self.tabs.setCurrentIndex(newindex) - self.tabs.setTabIcon(newindex, - PesterIcon(self.mainwindow.theme["memos/memoicon"])) + self.tabs.setTabIcon( + newindex, PesterIcon(self.mainwindow.theme["memos/memoicon"]) + ) + def updateBlocked(self): pass + def updateMood(self): pass -_ctag_begin = re.compile(r'') + +_ctag_begin = re.compile(r"") + class MemoText(PesterText): def __init__(self, theme, parent=None): super(MemoText, self).__init__(theme, parent) - if hasattr(self.parent(), 'mainwindow'): + if hasattr(self.parent(), "mainwindow"): self.mainwindow = self.parent().mainwindow else: self.mainwindow = self.parent() @@ -279,35 +316,41 @@ class MemoText(PesterText): self.copyAvailable[bool].connect(self.textReady) self.urls = {} for k in smiledict: - self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), - "smilies/%s" % (smiledict[k])) - #self.mainwindow.animationSetting[bool].connect(self.animateChanged) + self.addAnimation( + QtCore.QUrl("smilies/%s" % (smiledict[k])), + "smilies/%s" % (smiledict[k]), + ) + # self.mainwindow.animationSetting[bool].connect(self.animateChanged) def initTheme(self, theme): if "memos/scrollbar" in theme: - self.setStyleSheet("QTextEdit { %s }" - "QScrollBar:vertical { %s }" - "QScrollBar::handle:vertical { %s }" - "QScrollBar::add-line:vertical { %s }" - "QScrollBar::sub-line:vertical { %s }" - "QScrollBar:up-arrow:vertical { %s }" - "QScrollBar:down-arrow:vertical { %s }" - % (theme["memos/textarea/style"], - theme["memos/scrollbar/style"], - theme["memos/scrollbar/handle"], - theme["memos/scrollbar/downarrow"], - theme["memos/scrollbar/uparrow"], - theme["memos/scrollbar/uarrowstyle"], - theme["memos/scrollbar/darrowstyle"] )) + self.setStyleSheet( + "QTextEdit { %s }" + "QScrollBar:vertical { %s }" + "QScrollBar::handle:vertical { %s }" + "QScrollBar::add-line:vertical { %s }" + "QScrollBar::sub-line:vertical { %s }" + "QScrollBar:up-arrow:vertical { %s }" + "QScrollBar:down-arrow:vertical { %s }" + % ( + theme["memos/textarea/style"], + theme["memos/scrollbar/style"], + theme["memos/scrollbar/handle"], + theme["memos/scrollbar/downarrow"], + theme["memos/scrollbar/uparrow"], + theme["memos/scrollbar/uarrowstyle"], + theme["memos/scrollbar/darrowstyle"], + ) + ) else: self.setStyleSheet("QTextEdit { %s }" % theme["memos/textarea/style"]) # So it doesn't inherit the memo's background image. # Fixes floating "PESTERLOG:" try: - self.setStyleSheet(self.styleSheet() - + ("QMenu{ %s }" - % theme["main/defaultwindow/style"])) + self.setStyleSheet( + self.styleSheet() + ("QMenu{ %s }" % theme["main/defaultwindow/style"]) + ) except: pass @@ -325,8 +368,8 @@ class MemoText(PesterText): if m.state() == QtGui.QMovie.MovieState.NotRunning: m.start() chumdb = window.chumdb - if chum is not me: # SO MUCH WH1T3SP4C3 >:] - if type(lexmsg[0]) is colorBegin: # get color tag + if chum is not me: # SO MUCH WH1T3SP4C3 >:] + if type(lexmsg[0]) is colorBegin: # get color tag colortag = lexmsg[0] try: color = QtGui.QColor(*[int(c) for c in colortag.color.split(",")]) @@ -363,10 +406,12 @@ class MemoText(PesterText): if time.isFirstTime(): grammar = time.getGrammar() - joinmsg = chum.memojoinmsg(systemColor, - time.getTime(), - grammar, - window.theme["convo/text/joinmemo"]) + joinmsg = chum.memojoinmsg( + systemColor, + time.getTime(), + grammar, + window.theme["convo/text/joinmemo"], + ) self.append(convertTags(joinmsg)) parent.mainwindow.chatlog.log(parent.channel, joinmsg) time.openCurrentTime() @@ -375,7 +420,8 @@ class MemoText(PesterText): if msg.count(" msg.count(""): for i in range(msg.count("")): msg = msg + "" - return "" + msg + "" + return '' + msg + "" + if type(lexmsg[0]) is mecmd: memsg = chum.memsg(systemColor, lexmsg, time=time.getGrammar()) window.chatlog.log(parent.channel, memsg) @@ -387,12 +433,14 @@ class MemoText(PesterText): def changeTheme(self, theme): self.initTheme(theme) + class MemoInput(PesterInput): stylesheet_path = "memos/input/style" # karxi: Because of the use of stylesheet_path, we don't have to rewrite # this code. # Neat, huh? + class PesterMemo(PesterConvo): # TODO: Clean up inheritance between these!! The inits are ugly. def __init__(self, channel, timestr, mainwindow, parent=None): @@ -404,71 +452,107 @@ class PesterMemo(PesterConvo): self.time = TimeTracker(txt2delta(timestr)) self.setWindowTitle(channel) self.channelLabel = QtWidgets.QLabel(self) - self.channelLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, - QtWidgets.QSizePolicy.Policy.Expanding)) + self.channelLabel.setSizePolicy( + QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + ) self.textArea = MemoText(self.mainwindow.theme, self) self.textInput = MemoInput(self.mainwindow.theme, self) self.textInput.setFocus() self.miniUserlist = QtWidgets.QPushButton(">\n>", self) - #self.miniUserlist.setStyleSheet("border:1px solid #a68168; border-width: 2px 0px 2px 2px; height: 90px; width: 10px; color: #cd8f9d; font-family: 'Arial'; background: white; margin-left: 2px;") + # self.miniUserlist.setStyleSheet("border:1px solid #a68168; border-width: 2px 0px 2px 2px; height: 90px; width: 10px; color: #cd8f9d; font-family: 'Arial'; background: white; margin-left: 2px;") self.miniUserlist.clicked.connect(self.toggleUserlist) - self.userlist = RightClickList(self) - self.userlist.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, - QtWidgets.QSizePolicy.Policy.Expanding)) + self.userlist.setSizePolicy( + QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Fixed, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + ) self.userlist.optionsMenu = QtWidgets.QMenu(self) - self.pesterChumAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self) + self.pesterChumAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/pester"], self + ) self.pesterChumAction.triggered.connect(self.newPesterSlot) - self.addchumAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self) + self.addchumAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self + ) self.addchumAction.triggered.connect(self.addChumSlot) - self.banuserAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/banuser"], self) + self.banuserAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/banuser"], self + ) self.banuserAction.triggered.connect(self.banSelectedUser) - self.opAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/opuser"], self) + self.opAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/opuser"], self + ) self.opAction.triggered.connect(self.opSelectedUser) - self.voiceAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/voiceuser"], self) + self.voiceAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/voiceuser"], self + ) self.voiceAction.triggered.connect(self.voiceSelectedUser) - self.quirkDisableAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self) + self.quirkDisableAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self + ) self.quirkDisableAction.triggered.connect(self.killQuirkUser) self.userlist.optionsMenu.addAction(self.pesterChumAction) self.userlist.optionsMenu.addAction(self.addchumAction) # ban & op list added if we are op self.optionsMenu = QtWidgets.QMenu(self) - self.optionsMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) # So it doesn't inherit the memo's background image. - # Fixes floating "PESTERLOG:" - self.oocToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self) + self.optionsMenu.setStyleSheet( + self.mainwindow.theme["main/defaultwindow/style"] + ) # So it doesn't inherit the memo's background image. + # Fixes floating "PESTERLOG:" + self.oocToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self + ) self.oocToggle.setCheckable(True) self.oocToggle.toggled[bool].connect(self.toggleOOC) - self.quirksOff = QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self) + self.quirksOff = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self + ) self.quirksOff.setCheckable(True) self.quirksOff.toggled[bool].connect(self.toggleQuirks) - self.logchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self) + self.logchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self + ) self.logchum.triggered.connect(self.openChumLogs) - self.invitechum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self) + self.invitechum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self + ) self.invitechum.triggered.connect(self.inviteChums) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): try: - self._beepToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"], self) + self._beepToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"], self + ) except: self._beepToggle = QAction("BEEP ON MESSAGE", self) self._beepToggle.setCheckable(True) self._beepToggle.toggled[bool].connect(self.toggleBeep) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): try: - self._flashToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"], self) + self._flashToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"], self + ) except: self._flashToggle = QAction("FLASH ON MESSAGE", self) self._flashToggle.setCheckable(True) self._flashToggle.toggled[bool].connect(self.toggleFlash) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): try: - self._muteToggle = QAction(self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"], self) + self._muteToggle = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"], + self, + ) except: self._muteToggle = QAction("MUTE NOTIFICATIONS", self) self._muteToggle.setCheckable(True) @@ -484,24 +568,36 @@ class PesterMemo(PesterConvo): self.optionsMenu.addAction(self.logchum) self.optionsMenu.addAction(self.invitechum) - self.chanModeMenu = QtWidgets.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/memosetting"], self) - self.chanNoquirks = QAction(self.mainwindow.theme["main/menus/rclickchumlist/memonoquirk"], self) + self.chanModeMenu = QtWidgets.QMenu( + self.mainwindow.theme["main/menus/rclickchumlist/memosetting"], self + ) + self.chanNoquirks = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/memonoquirk"], self + ) self.chanNoquirks.setCheckable(True) self.chanNoquirks.toggled[bool].connect(self.noquirksChan) - self.chanHide = QAction(self.mainwindow.theme["main/menus/rclickchumlist/memohidden"], self) + self.chanHide = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/memohidden"], self + ) self.chanHide.setCheckable(True) self.chanHide.toggled[bool].connect(self.hideChan) - self.chanInvite = QAction(self.mainwindow.theme["main/menus/rclickchumlist/memoinvite"], self) + self.chanInvite = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/memoinvite"], self + ) self.chanInvite.setCheckable(True) self.chanInvite.toggled[bool].connect(self.inviteChan) - self.chanMod = QAction(self.mainwindow.theme["main/menus/rclickchumlist/memomute"], self) + self.chanMod = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/memomute"], self + ) self.chanMod.setCheckable(True) self.chanMod.toggled[bool].connect(self.modChan) self.chanModeMenu.addAction(self.chanNoquirks) self.chanModeMenu.addAction(self.chanHide) self.chanModeMenu.addAction(self.chanInvite) self.chanModeMenu.addAction(self.chanMod) - self.chanModeMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) # BWAH BWAH FLOATING "PESTERLOG:" + self.chanModeMenu.setStyleSheet( + self.mainwindow.theme["main/defaultwindow/style"] + ) # BWAH BWAH FLOATING "PESTERLOG:" self.timeslider = TimeSlider(QtCore.Qt.Orientation.Horizontal, self) self.timeinput = TimeInput(self.timeslider, self) @@ -533,9 +629,9 @@ class PesterMemo(PesterConvo): layout_1.addWidget(self.miniUserlist) layout_1.addWidget(self.userlist) -# layout_1 = QtGui.QGridLayout() -# layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignmentFlag.AlignHCenter) -# layout_1.addWidget(self.timeinput, 1, 0, 1, 3) + # layout_1 = QtGui.QGridLayout() + # layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignmentFlag.AlignHCenter) + # layout_1.addWidget(self.timeinput, 1, 0, 1, 3) layout_2 = QtWidgets.QHBoxLayout() layout_2.addWidget(self.timeslider) layout_2.addWidget(self.timeinput) @@ -550,8 +646,9 @@ class PesterMemo(PesterConvo): self.layout.addLayout(layout_2) self.layout.setSpacing(0) margins = self.mainwindow.theme["memos/margins"] - self.layout.setContentsMargins(margins["left"], margins["top"], - margins["right"], margins["bottom"]) + self.layout.setContentsMargins( + margins["left"], margins["top"], margins["right"], margins["bottom"] + ) self.setLayout(self.layout) @@ -561,11 +658,13 @@ class PesterMemo(PesterConvo): p = self.mainwindow.profile() timeGrammar = self.time.getGrammar() systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) - msg = p.memoopenmsg(systemColor, - self.time.getTime(), - timeGrammar, - self.mainwindow.theme["convo/text/openmemo"], - self.channel) + msg = p.memoopenmsg( + systemColor, + self.time.getTime(), + timeGrammar, + self.mainwindow.theme["convo/text/openmemo"], + self.channel, + ) self.time.openCurrentTime() self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) @@ -585,35 +684,45 @@ class PesterMemo(PesterConvo): if self.userlist.isHidden(): self.userlist.show() self.miniUserlist.setText(">\n>") - self.miniUserlist.setStyleSheet("%s border-width: 2px 0px 2px 2px;" - % self.miniUserlist.styleSheet()) + self.miniUserlist.setStyleSheet( + "%s border-width: 2px 0px 2px 2px;" % self.miniUserlist.styleSheet() + ) else: self.userlist.hide() self.miniUserlist.setText("<\n<") - self.miniUserlist.setStyleSheet("%s border-width: 2px;" - % self.miniUserlist.styleSheet()) + self.miniUserlist.setStyleSheet( + "%s border-width: 2px;" % self.miniUserlist.styleSheet() + ) def title(self): return self.channel + def icon(self): return PesterIcon(self.mainwindow.theme["memos/memoicon"]) def sendTimeInfo(self, newChum=False): if newChum: - self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")+"i"), - self.title()) + self.messageSent.emit( + "PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server") + "i"), + self.title(), + ) else: - self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")), - self.title()) + self.messageSent.emit( + "PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")), + self.title(), + ) def updateMood(self): pass + def updateBlocked(self): pass + def updateColor(self, handle, color): chums = self.userlist.findItems(handle, QtCore.Qt.MatchFlag.MatchExactly) for c in chums: c.setForeground(QtGui.QBrush(color)) + def addMessage(self, text, handle): if type(handle) is bool: chum = self.mainwindow.profile() @@ -627,68 +736,80 @@ class PesterMemo(PesterConvo): self.setStyleSheet("QtWidgets.QFrame { %s };" % (theme["memos/style"])) self.setWindowIcon(PesterIcon(theme["memos/memoicon"])) t = Template(theme["memos/label/text"]) - if self.mainwindow.advanced and hasattr(self, 'modes'): - self.channelLabel.setText(t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes)) + if self.mainwindow.advanced and hasattr(self, "modes"): + self.channelLabel.setText( + t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes) + ) else: self.channelLabel.setText(t.safe_substitute(channel=self.channel)) self.channelLabel.setStyleSheet(theme["memos/label/style"]) - self.channelLabel.setAlignment(self.aligndict["h"][theme["memos/label/align/h"]] - | self.aligndict["v"][theme["memos/label/align/v"]]) + self.channelLabel.setAlignment( + self.aligndict["h"][theme["memos/label/align/h"]] + | self.aligndict["v"][theme["memos/label/align/v"]] + ) self.channelLabel.setMaximumHeight(theme["memos/label/maxheight"]) self.channelLabel.setMinimumHeight(theme["memos/label/minheight"]) self.userlist.optionsMenu.setStyleSheet(theme["main/defaultwindow/style"]) scrolls = "width: 12px; height: 12px; border: 0; padding: 0;" if "main/chums/scrollbar" in theme: - self.userlist.setStyleSheet("QListWidget { %s }" - "QScrollBar { %s }" - "QScrollBar::handle { %s }" - "QScrollBar::add-line { %s }" - "QScrollBar::sub-line { %s }" - "QScrollBar:up-arrow { %s }" - "QScrollBar:down-arrow { %s }" - % (theme["memos/userlist/style"], - theme["main/chums/scrollbar/style"] + scrolls, - theme["main/chums/scrollbar/handle"], - theme["main/chums/scrollbar/downarrow"], - theme["main/chums/scrollbar/uparrow"], - theme["main/chums/scrollbar/uarrowstyle"], - theme["main/chums/scrollbar/darrowstyle"])) + self.userlist.setStyleSheet( + "QListWidget { %s }" + "QScrollBar { %s }" + "QScrollBar::handle { %s }" + "QScrollBar::add-line { %s }" + "QScrollBar::sub-line { %s }" + "QScrollBar:up-arrow { %s }" + "QScrollBar:down-arrow { %s }" + % ( + theme["memos/userlist/style"], + theme["main/chums/scrollbar/style"] + scrolls, + theme["main/chums/scrollbar/handle"], + theme["main/chums/scrollbar/downarrow"], + theme["main/chums/scrollbar/uparrow"], + theme["main/chums/scrollbar/uarrowstyle"], + theme["main/chums/scrollbar/darrowstyle"], + ) + ) elif "convo/scrollbar" in theme: - self.userlist.setStyleSheet("QListWidget { %s }" - "QScrollBar { %s }" - "QScrollBar::handle { %s }" - "QScrollBar::add-line { %s }" - "QScrollBar::sub-line { %s }" - "QScrollBar:up-arrow { %s }" - "QScrollBar:down-arrow { %s }" - % (theme["memos/userlist/style"], - theme["convo/scrollbar/style"] + scrolls, - theme["convo/scrollbar/handle"], - "display: none;", - "display: none;", - "display: none;", - "display: none;")) + self.userlist.setStyleSheet( + "QListWidget { %s }" + "QScrollBar { %s }" + "QScrollBar::handle { %s }" + "QScrollBar::add-line { %s }" + "QScrollBar::sub-line { %s }" + "QScrollBar:up-arrow { %s }" + "QScrollBar:down-arrow { %s }" + % ( + theme["memos/userlist/style"], + theme["convo/scrollbar/style"] + scrolls, + theme["convo/scrollbar/handle"], + "display: none;", + "display: none;", + "display: none;", + "display: none;", + ) + ) else: - self.userlist.setStyleSheet("QListWidget { %s }" - "QScrollBar { %s }" - "QScrollBar::handle { %s }" - % (theme["memos/userlist/style"], - scrolls, - "background-color: black;")) + self.userlist.setStyleSheet( + "QListWidget { %s }" + "QScrollBar { %s }" + "QScrollBar::handle { %s }" + % (theme["memos/userlist/style"], scrolls, "background-color: black;") + ) self.userlist.setFixedWidth(theme["memos/userlist/width"]) if self.userlist.isHidden(): borders = "border-width: 2px;" else: borders = "border-width: 2px 0px 2px 2px;" - self.miniUserlist.setStyleSheet("padding: 0px;" - "margin: 0px;" - "margin-left: 5px;" - "width: 10px;" - "height: 90px;" - + borders - + theme["memos/userlist/style"]) + self.miniUserlist.setStyleSheet( + "padding: 0px;" + "margin: 0px;" + "margin-left: 5px;" + "width: 10px;" + "height: 90px;" + borders + theme["memos/userlist/style"] + ) self.addchumAction.setText(theme["main/menus/rclickchumlist/addchum"]) self.banuserAction.setText(theme["main/menus/rclickchumlist/banuser"]) @@ -706,12 +827,16 @@ class PesterMemo(PesterConvo): self.timeinput.setFixedWidth(theme["memos/time/text/width"]) self.timeinput.setStyleSheet(theme["memos/time/text/style"]) - slidercss = ("QSlider { %s }" - "Slider::groove { %s }" - "QSlider::handle { %s }" - % (theme["memos/time/slider/style"], - theme["memos/time/slider/groove"], - theme["memos/time/slider/handle"])) + slidercss = ( + "QSlider { %s }" + "Slider::groove { %s }" + "QSlider::handle { %s }" + % ( + theme["memos/time/slider/style"], + theme["memos/time/slider/groove"], + theme["memos/time/slider/handle"], + ) + ) self.timeslider.setStyleSheet(slidercss) larrow = PesterIcon(self.mainwindow.theme["memos/time/arrows/left"]) @@ -726,27 +851,35 @@ class PesterMemo(PesterConvo): self.timeswitchr.setIconSize(rarrow.realsize()) self.timeswitchr.setStyleSheet(self.mainwindow.theme["memos/time/arrows/style"]) - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"): try: - self._beepToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"]) + self._beepToggle.setText( + self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"] + ) except: self._beepToggle.setText("BEEP ON MESSAGE") - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"): try: - self._flashToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"]) + self._flashToggle.setText( + self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"] + ) except: self._flashToggle.setText("FLASH ON MESSAGE") - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"): try: - self._muteToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"]) + self._muteToggle.setText( + self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"] + ) except: self._muteToggle.setText("MUTE NOTIFICATIONS") - #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/pester"): + # if self.mainwindow.theme.has_key("main/menus/rclickchumlist/pester"): try: - self.pesterChumAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/pester"]) + self.pesterChumAction.setText( + self.mainwindow.theme["main/menus/rclickchumlist/pester"] + ) except: pass @@ -755,20 +888,21 @@ class PesterMemo(PesterConvo): self.textArea.changeTheme(theme) self.textInput.changeTheme(theme) margins = theme["memos/margins"] - self.layout.setContentsMargins(margins["left"], margins["top"], - margins["right"], margins["bottom"]) - for item in [self.userlist.item(i) for i in range(0,self.userlist.count())]: + self.layout.setContentsMargins( + margins["left"], margins["top"], margins["right"], margins["bottom"] + ) + for item in [self.userlist.item(i) for i in range(0, self.userlist.count())]: self.iconCrap(item) def addUser(self, handle): chumdb = self.mainwindow.chumdb defaultcolor = QtGui.QColor("black") founder = False - op = False - halfop = False - admin = False - voice = False - if handle[0] == '@': + op = False + halfop = False + admin = False + voice = False + if handle[0] == "@": op = True handle = handle[1:] if handle == self.mainwindow.profile().handle: @@ -776,7 +910,7 @@ class PesterMemo(PesterConvo): self.userlist.optionsMenu.addAction(self.banuserAction) self.optionsMenu.addMenu(self.chanModeMenu) self.op = True - elif handle[0] == '%': + elif handle[0] == "%": halfop = True handle = handle[1:] if handle == self.mainwindow.profile().handle: @@ -784,13 +918,13 @@ class PesterMemo(PesterConvo): self.userlist.optionsMenu.addAction(self.banuserAction) self.optionsMenu.addMenu(self.chanModeMenu) self.halfop = True - elif handle[0] == '+': + elif handle[0] == "+": voice = True handle = handle[1:] - elif handle[0] == '~': + elif handle[0] == "~": founder = True handle = handle[1:] - elif handle[0] == '&': + elif handle[0] == "&": admin = True handle = handle[1:] item = QtWidgets.QListWidgetItem(handle) @@ -798,7 +932,7 @@ class PesterMemo(PesterConvo): color = self.mainwindow.profile().color else: color = chumdb.getColor(handle, defaultcolor) - item.box = (handle == "evacipatedBox") + item.box = handle == "evacipatedBox" item.setForeground(QtGui.QBrush(color)) item.founder = founder item.op = op @@ -816,14 +950,37 @@ class PesterMemo(PesterConvo): while listing is not None: users.append(self.userlist.takeItem(0)) listing = self.userlist.item(0) - users.sort(key=lambda x: ((-1 if x.box else (0 if x.founder else (1 if x.admin else (2 if x.op else (3 if x.halfop else (4 if x.voice else 5)))))), x.text())) + users.sort( + key=lambda x: ( + ( + -1 + if x.box + else ( + 0 + if x.founder + else ( + 1 + if x.admin + else ( + 2 + if x.op + else (3 if x.halfop else (4 if x.voice else 5)) + ) + ) + ) + ), + x.text(), + ) + ) for u in users: self.userlist.addItem(u) def updateChanModes(self, modes, op): - if not hasattr(self, 'modes'): self.modes = "" + if not hasattr(self, "modes"): + self.modes = "" chanmodes = list(str(self.modes)) - if chanmodes and chanmodes[0] == "+": chanmodes = chanmodes[1:] + if chanmodes and chanmodes[0] == "+": + chanmodes = chanmodes[1:] modes = str(modes) if op: systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) @@ -845,117 +1002,165 @@ class PesterMemo(PesterConvo): self.quirksOff.setChecked(True) self.applyquirks = False if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "A No-Quirk zone", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "A No-Quirk zone", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("s") >= 0: self.chanHide.setChecked(True) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secret", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Secret", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("i") >= 0: self.chanInvite.setChecked(True) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Invite-Only", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Invite-Only", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("m") >= 0: self.chanMod.setChecked(True) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Muted", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Muted", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) - #New + # New if modes.find("C") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-CTCP", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-CTCP", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("D") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Join Delayed", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Join Delayed", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("f") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Flood Protected", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Flood Protected", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("G") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Censored", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Censored", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("H") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Remembering Chat History", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Remembering Chat History", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("k") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Key-only", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Key-only", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("K") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-Knock", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("L") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Redirecting", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Redirecting", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("K") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-Knock", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("l") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Limiting maximum amount of users", True) + msg = chum.memomodemsg( + opchum, + opgrammar, + systemColor, + "Limiting maximum amount of users", + True, + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("M") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Non-Auth muted", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Non-Auth muted", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("N") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Handle-locked", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Handle-locked", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("O") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "An Oper-Only channel", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "An Oper-Only channel", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("P") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Permanent", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Permanent", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("Q") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-kick", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-kick", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("R") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered users only", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Registered users only", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("r") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Registered", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("z") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secure-only", True) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Secure-only", True + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) elif modes[0] == "-": @@ -967,125 +1172,178 @@ class PesterMemo(PesterConvo): if modes.find("c") >= 0: self.chanNoquirks.setChecked(False) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "A No-Quirk zone", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "A No-Quirk zone", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("s") >= 0: self.chanHide.setChecked(False) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secret", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Secret", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("i") >= 0: self.chanInvite.setChecked(False) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Invite-Only", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Invite-Only", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("m") >= 0: self.chanMod.setChecked(False) if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Muted", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Muted", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) - #New + # New if modes.find("C") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-CTCP", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-CTCP", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("D") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Join Delayed", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Join Delayed", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("f") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Flood Protected", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Flood Protected", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("G") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Censored", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Censored", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("H") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Remembering Chat History", False) + msg = chum.memomodemsg( + opchum, + opgrammar, + systemColor, + "Remembering Chat History", + False, + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("k") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Key-only", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Key-only", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("K") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-Knock", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("L") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Redirecting", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Redirecting", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("K") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-Knock", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("l") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Limiting maximum amount of users", False) + msg = chum.memomodemsg( + opchum, + opgrammar, + systemColor, + "Limiting maximum amount of users", + False, + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("M") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Non-Auth muted", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Non-Auth muted", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("N") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Handle-locked", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Handle-locked", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("O") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "An Oper-Only channel", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "An Oper-Only channel", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("P") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Permanent", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Permanent", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("Q") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-kick", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "No-kick", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("R") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered users only", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Registered users only", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("r") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Registered", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if modes.find("z") >= 0: if op: - msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secure-only", False) + msg = chum.memomodemsg( + opchum, opgrammar, systemColor, "Secure-only", False + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) chanmodes.sort() self.modes = "+" + "".join(chanmodes) if self.mainwindow.advanced: t = Template(self.mainwindow.theme["memos/label/text"]) - self.channelLabel.setText(t.safe_substitute(channel=self.channel) - + "(%s)" % (self.modes)) + self.channelLabel.setText( + t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes) + ) def timeUpdate(self, handle, cmd): window = self.mainwindow @@ -1097,7 +1355,7 @@ class PesterMemo(PesterConvo): secs = int(cmd) time = datetime.fromtimestamp(secs) timed = time - datetime.now() - s = (timed.seconds // 60)*60 + s = (timed.seconds // 60) * 60 timed = timedelta(timed.days, s) except OverflowError: if secs < 0: @@ -1109,7 +1367,7 @@ class PesterMemo(PesterConvo): if cmd == "i": timed = timedelta(0) else: - if cmd[len(cmd)-1] == 'c': + if cmd[len(cmd) - 1] == "c": close = timeProtocol(cmd) timed = None else: @@ -1124,9 +1382,9 @@ class PesterMemo(PesterConvo): self.times[handle].setCurrent(close) grammar = self.times[handle].getGrammar() self.times[handle].removeTime(close) - msg = chum.memoclosemsg(systemColor, - grammar, - window.theme["convo/text/closememo"]) + msg = chum.memoclosemsg( + systemColor, grammar, window.theme["convo/text/closememo"] + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) elif timed not in self.times[handle]: @@ -1141,19 +1399,21 @@ class PesterMemo(PesterConvo): @QtCore.pyqtSlot() def sentMessage(self): text = str(self.textInput.text()) - + return parsetools.kxhandleInput(self, text, flavor="memos") - + @QtCore.pyqtSlot(QString) def namesUpdated(self, channel): c = str(channel) - if c.lower() != self.channel.lower(): return + if c.lower() != self.channel.lower(): + return # get namesdb (unused) - #namesdb = self.mainwindow.namesdb + # namesdb = self.mainwindow.namesdb # reload names self.userlist.clear() for n in self.mainwindow.namesdb[self.channel]: self.addUser(n) + @QtCore.pyqtSlot(QString, QString) def modesUpdated(self, channel, modes): c = str(channel) @@ -1164,7 +1424,7 @@ class PesterMemo(PesterConvo): def closeInviteOnly(self, channel): c = str(channel) if c.lower() == self.channel.lower(): - self.mainwindow.inviteOnlyChan['QString'].disconnect(self.closeInviteOnly) + self.mainwindow.inviteOnlyChan["QString"].disconnect(self.closeInviteOnly) if self.parent(): PchumLog.info(self.channel) i = self.parent().tabIndices[self.channel] @@ -1172,11 +1432,14 @@ class PesterMemo(PesterConvo): else: self.close() msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.mainwindow.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.mainwindow.theme["main/defaultwindow/style"] + ) msgbox.setText("%s: Invites only!" % (c)) - msgbox.setInformativeText("This channel is invite-only. " - "You must get an invitation from someone on the inside before entering.") + msgbox.setInformativeText( + "This channel is invite-only. " + "You must get an invitation from someone on the inside before entering." + ) msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msgbox.exec() @@ -1184,7 +1447,9 @@ class PesterMemo(PesterConvo): def closeForbidden(self, channel, reason): c = str(channel) if c.lower() == self.channel.lower(): - self.mainwindow.forbiddenChan['QString', 'QString'].disconnect(self.closeForbidden) + self.mainwindow.forbiddenChan["QString", "QString"].disconnect( + self.closeForbidden + ) if self.parent(): PchumLog.info(self.channel) i = self.parent().tabIndices[self.channel] @@ -1192,21 +1457,24 @@ class PesterMemo(PesterConvo): else: self.close() msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.mainwindow.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.mainwindow.theme["main/defaultwindow/style"] + ) msgbox.setText("%s: D: CANT JOIN MEMO!!!" % (c)) msgbox.setInformativeText(reason) msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msgbox.exec() def quirkDisable(self, op, msg): - chums = self.userlist.findItems(op, QtCore.Qt.MatchFlag.MatchExactly) + chums = self.userlist.findItems(op, QtCore.Qt.MatchFlag.MatchExactly) for c in chums: if c.op: if msg == self.mainwindow.profile().handle: self.quirksOff.setChecked(True) self.applyquirks = False - systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) + systemColor = QtGui.QColor( + self.mainwindow.theme["memos/systemMsgColor"] + ) chum = self.mainwindow.profile() opchum = PesterProfile(op) if op in self.times: @@ -1223,11 +1491,11 @@ class PesterMemo(PesterConvo): chum = PesterProfile(h) if h == self.mainwindow.profile().handle: chum = self.mainwindow.profile() - #ttracker = self.time - #curtime = self.time.getTime() - #elif h in self.times: + # ttracker = self.time + # curtime = self.time.getTime() + # elif h in self.times: # ttracker = self.times[h] - #else: + # else: # ttracker = TimeTracker(timedelta(0)) opchum = PesterProfile(op) PchumLog.debug("op = " + op) @@ -1239,13 +1507,14 @@ class PesterMemo(PesterConvo): else: opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW") return (chum, opchum, opgrammar) + def iconCrap(self, c, down=True): - for m in (self.umodes if down else reversed(self.umodes)): - if eval("c."+m): + for m in self.umodes if down else reversed(self.umodes): + if eval("c." + m): if m == "box": icon = PesterIcon("smilies/box.png") else: - icon = PesterIcon(self.mainwindow.theme["memos/"+m+"/icon"]) + icon = PesterIcon(self.mainwindow.theme["memos/" + m + "/icon"]) c.setIcon(icon) return icon = QtGui.QIcon() @@ -1253,7 +1522,7 @@ class PesterMemo(PesterConvo): @QtCore.pyqtSlot() def dumpNetsplit(self): - if (len(self.netsplit) > 0): + if len(self.netsplit) > 0: chum = self.mainwindow.profile() systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) msg = chum.memonetsplitmsg(systemColor, self.netsplit) @@ -1263,12 +1532,12 @@ class PesterMemo(PesterConvo): @QtCore.pyqtSlot(QString, QString, QString) def userPresentChange(self, handle, channel, update): - #print("handle: %s, channel: %s, update: %s" % (handle, channel, update)) + # print("handle: %s, channel: %s, update: %s" % (handle, channel, update)) h = str(handle) c = str(channel) update = str(update) - #PchumLog.debug("h=%s\nc=%s\nupdate=%s" % (h,c,update)) - if update[0:4] == "kick": # yeah, i'm lazy. + # PchumLog.debug("h=%s\nc=%s\nupdate=%s" % (h,c,update)) + if update[0:4] == "kick": # yeah, i'm lazy. l = update.split(":") update = l[0] op = l[1] @@ -1282,21 +1551,33 @@ class PesterMemo(PesterConvo): l = update.split(":") update = l[0] op = l[1] - if (update in ["join","left", "kick", \ - "+q", "-q", "+o", "-o", "+h", "-h", \ - "+a", "-a", "+v", "-v"]) \ - and c.lower() != self.channel.lower(): + if ( + update + in [ + "join", + "left", + "kick", + "+q", + "-q", + "+o", + "-o", + "+h", + "-h", + "+a", + "-a", + "+v", + "-v", + ] + ) and c.lower() != self.channel.lower(): return - chums = self.userlist.findItems(h, QtCore.Qt.MatchFlag.MatchExactly) + chums = self.userlist.findItems(h, QtCore.Qt.MatchFlag.MatchExactly) systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) # print exit if update in ("quit", "left", "nick", "netsplit"): if update == "netsplit": if not hasattr(self, "netsplit"): self.netsplit = [] - QtCore.QTimer.singleShot(1500, - self, - QtCore.SLOT('dumpNetsplit()')) + QtCore.QTimer.singleShot(1500, self, QtCore.SLOT("dumpNetsplit()")) for c in chums: chum = PesterProfile(h) self.userlist.takeItem(self.userlist.row(c)) @@ -1306,28 +1587,31 @@ class PesterMemo(PesterConvo): while self.times[h].getTime() is not None: t = self.times[h] grammar = t.getGrammar() - allinitials.append("%s%s%s" % (grammar.pcf, - chum.initials(), - grammar.number)) + allinitials.append( + "%s%s%s" % (grammar.pcf, chum.initials(), grammar.number) + ) self.times[h].removeTime(t.getTime()) if update == "netsplit": self.netsplit.extend(allinitials) else: - msg = chum.memoclosemsg(systemColor, - allinitials, - self.mainwindow.theme["convo/text/closememo"]) + msg = chum.memoclosemsg( + systemColor, + allinitials, + self.mainwindow.theme["convo/text/closememo"], + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) if update == "nick": self.addUser(newnick) - newchums = self.userlist.findItems(newnick, - QtCore.Qt.MatchFlag.MatchExactly) + newchums = self.userlist.findItems( + newnick, QtCore.Qt.MatchFlag.MatchExactly + ) for nc in newchums: for c in chums: nc.founder = c.founder - nc.op = c.op - nc.halfop = c.halfop - nc.admin = c.admin + nc.op = c.op + nc.halfop = c.halfop + nc.admin = c.admin self.iconCrap(nc) self.sortUsers() elif update == "kick": @@ -1353,7 +1637,9 @@ class PesterMemo(PesterConvo): opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW") while ttracker.getTime() is not None: grammar = ttracker.getGrammar() - allinitials.append("%s%s%s" % (grammar.pcf, chum.initials(), grammar.number)) + allinitials.append( + "%s%s%s" % (grammar.pcf, chum.initials(), grammar.number) + ) ttracker.removeTime(ttracker.getTime()) msg = chum.memobanmsg(opchum, opgrammar, systemColor, allinitials, reason) self.textArea.append(convertTags(msg)) @@ -1362,34 +1648,47 @@ class PesterMemo(PesterConvo): if chum is self.mainwindow.profile(): # are you next? msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.mainwindow.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" + % self.mainwindow.theme["main/defaultwindow/style"] + ) msgbox.setText(self.mainwindow.theme["convo/text/kickedmemo"]) # Add ban(kick) reason # l = split update - kick_msg = ("press 0k to rec0nnect or cancel to absc0nd") + kick_msg = "press 0k to rec0nnect or cancel to absc0nd" if len(l) >= 3: try: - if l[1] != l[2]: # If there's no reason then reason is set to handle + if ( + l[1] != l[2] + ): # If there's no reason then reason is set to handle # Process spare ':' characters (this might not be safe?) aggrievocation = l[1] + ": " + l[2] if len(l) > 3: - aggrievocation += ':' + aggrievocation += ":" for x in range(3, len(l)): aggrievocation += l[x] # Not for last slice - if x != (len(l)-1): - aggrievocation += ':' - kick_msg = ("%s\n\npress 0k to rec0nnect or cancel to absc0nd" % aggrievocation) + if x != (len(l) - 1): + aggrievocation += ":" + kick_msg = ( + "%s\n\npress 0k to rec0nnect or cancel to absc0nd" + % aggrievocation + ) except IndexError as e: # This shouldn't happen PchumLog.warning("kickmsg IndexError: %s" % e) msgbox.setInformativeText(kick_msg) - msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok | QtWidgets.QMessageBox.StandardButton.Cancel) + msgbox.setStandardButtons( + QtWidgets.QMessageBox.StandardButton.Ok + | QtWidgets.QMessageBox.StandardButton.Cancel + ) # Find the OK button and make it default for b in msgbox.buttons(): - if msgbox.buttonRole(b) == QtWidgets.QMessageBox.ButtonRole.AcceptRole: + if ( + msgbox.buttonRole(b) + == QtWidgets.QMessageBox.ButtonRole.AcceptRole + ): # We found the 'OK' button, set it as the default b.setDefault(True) b.setAutoDefault(True) @@ -1405,11 +1704,13 @@ class PesterMemo(PesterConvo): self.mainwindow.joinChannel.emit(self.channel) me = self.mainwindow.profile() self.time.openCurrentTime() - msg = me.memoopenmsg(systemColor, - self.time.getTime(), - self.time.getGrammar(), - self.mainwindow.theme["convo/text/openmemo"], - self.channel) + msg = me.memoopenmsg( + systemColor, + self.time.getTime(), + self.time.getGrammar(), + self.mainwindow.theme["convo/text/openmemo"], + self.channel, + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) elif ret == QtWidgets.QMessageBox.StandardButton.Cancel: @@ -1424,7 +1725,7 @@ class PesterMemo(PesterConvo): elif update == "join": self.addUser(h) time = self.time.getTime() - serverText = "PESTERCHUM:TIME>"+delta2txt(time, "server") + serverText = "PESTERCHUM:TIME>" + delta2txt(time, "server") self.messageSent.emit(serverText, self.title()) elif update == "+q": for c in chums: @@ -1439,10 +1740,10 @@ class PesterMemo(PesterConvo): elif update == "+o": if self.mainwindow.config.opvoiceMessages(): (chum, opchum, opgrammar) = self.chumOPstuff(h, op) - #PchumLog.debug("chum.handle = %s\nopchum.handle = %s\nopgrammar = %s\n systemColor = %s\n" + # PchumLog.debug("chum.handle = %s\nopchum.handle = %s\nopgrammar = %s\n systemColor = %s\n" # % (chum.handle, opchum.handle, opgrammar, systemColor)) msg = chum.memoopmsg(opchum, opgrammar, systemColor) - #PchumLog.debug("post memoopmsg") + # PchumLog.debug("post memoopmsg") self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) for c in chums: @@ -1515,7 +1816,7 @@ class PesterMemo(PesterConvo): c.admin = False self.iconCrap(c) self.sortUsers() - elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]: + elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+", "-"]: self.updateChanModes(update, op) elif update == "+v": if self.mainwindow.config.opvoiceMessages(): @@ -1537,7 +1838,7 @@ class PesterMemo(PesterConvo): c.voice = False self.iconCrap(c) self.sortUsers() - elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]: + elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+", "-"]: self.updateChanModes(update, op) @QtCore.pyqtSlot() @@ -1555,28 +1856,34 @@ class PesterMemo(PesterConvo): return currentChum = PesterProfile(str(self.userlist.currentItem().text())) self.mainwindow.addChum(currentChum) + @QtCore.pyqtSlot() def banSelectedUser(self): if not self.userlist.currentItem(): return currentHandle = str(self.userlist.currentItem().text()) - (reason, ok) = QtWidgets.QInputDialog.getText(self, - "Ban User", - "Enter the reason you are banning this user (optional):") + (reason, ok) = QtWidgets.QInputDialog.getText( + self, "Ban User", "Enter the reason you are banning this user (optional):" + ) if ok: - self.mainwindow.kickUser.emit("%s:%s" % (currentHandle, reason), self.channel) + self.mainwindow.kickUser.emit( + "%s:%s" % (currentHandle, reason), self.channel + ) + @QtCore.pyqtSlot() def opSelectedUser(self): if not self.userlist.currentItem(): return currentHandle = str(self.userlist.currentItem().text()) self.mainwindow.setChannelMode.emit(self.channel, "+o", currentHandle) + @QtCore.pyqtSlot() def voiceSelectedUser(self): if not self.userlist.currentItem(): return currentHandle = str(self.userlist.currentItem().text()) self.mainwindow.setChannelMode.emit(self.channel, "+v", currentHandle) + @QtCore.pyqtSlot() def killQuirkUser(self): if not self.userlist.currentItem(): @@ -1593,23 +1900,26 @@ class PesterMemo(PesterConvo): @QtCore.pyqtSlot() def openChumLogs(self): currentChum = self.channel - self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, - self.mainwindow.config, - self.mainwindow.theme, - self.mainwindow) - self.mainwindow.chumList.pesterlogviewer.rejected.connect(self.mainwindow.chumList.closeActiveLog) + self.mainwindow.chumList.pesterlogviewer = PesterLogViewer( + currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow + ) + self.mainwindow.chumList.pesterlogviewer.rejected.connect( + self.mainwindow.chumList.closeActiveLog + ) self.mainwindow.chumList.pesterlogviewer.show() self.mainwindow.chumList.pesterlogviewer.raise_() self.mainwindow.chumList.pesterlogviewer.activateWindow() @QtCore.pyqtSlot() def inviteChums(self): - if not hasattr(self, 'invitechums'): + if not hasattr(self, "invitechums"): self.invitechums = None if not self.invitechums: - (chum, ok) = QtWidgets.QInputDialog.getText(self, - "Invite to Chat", - "Enter the chumhandle of the user you'd like to invite:") + (chum, ok) = QtWidgets.QInputDialog.getText( + self, + "Invite to Chat", + "Enter the chumhandle of the user you'd like to invite:", + ) if ok: chum = str(chum) self.mainwindow.inviteChum.emit(chum, self.channel) @@ -1617,31 +1927,35 @@ class PesterMemo(PesterConvo): @QtCore.pyqtSlot(bool) def noquirksChan(self, on): - x = ["-","+"][on] - self.mainwindow.setChannelMode.emit(self.channel, x+"c", "") + x = ["-", "+"][on] + self.mainwindow.setChannelMode.emit(self.channel, x + "c", "") + @QtCore.pyqtSlot(bool) def hideChan(self, on): - x = ["-","+"][on] - self.mainwindow.setChannelMode.emit(self.channel, x+"s", "") + x = ["-", "+"][on] + self.mainwindow.setChannelMode.emit(self.channel, x + "s", "") + @QtCore.pyqtSlot(bool) def inviteChan(self, on): - x = ["-","+"][on] - self.mainwindow.setChannelMode.emit(self.channel, x+"i", "") + x = ["-", "+"][on] + self.mainwindow.setChannelMode.emit(self.channel, x + "i", "") + @QtCore.pyqtSlot(bool) def modChan(self, on): - x = ["-","+"][on] - self.mainwindow.setChannelMode.emit(self.channel, x+"m", "") + x = ["-", "+"][on] + self.mainwindow.setChannelMode.emit(self.channel, x + "m", "") @QtCore.pyqtSlot() def sendtime(self): - #me = self.mainwindow.profile() - #systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) + # me = self.mainwindow.profile() + # systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) time = txt2delta(self.timeinput.text()) - #present = self.time.addTime(time) + # present = self.time.addTime(time) self.time.addTime(time) - serverText = "PESTERCHUM:TIME>"+delta2txt(time, "server") + serverText = "PESTERCHUM:TIME>" + delta2txt(time, "server") self.messageSent.emit(serverText, self.title()) + @QtCore.pyqtSlot() def smashclock(self): me = self.mainwindow.profile() @@ -1650,7 +1964,9 @@ class PesterMemo(PesterConvo): if removed: grammar = self.time.getGrammarTime(time) systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"]) - msg = me.memoclosemsg(systemColor, grammar, self.mainwindow.theme["convo/text/closememo"]) + msg = me.memoclosemsg( + systemColor, grammar, self.mainwindow.theme["convo/text/closememo"] + ) self.textArea.append(convertTags(msg)) self.mainwindow.chatlog.log(self.channel, msg) @@ -1660,126 +1976,132 @@ class PesterMemo(PesterConvo): self.resetSlider(newtime, send=False) else: self.resetSlider(newtime) + @QtCore.pyqtSlot() def prevtime(self): time = self.time.prevTime() self.time.setCurrent(time) self.resetSlider(time) self.textInput.setFocus() + @QtCore.pyqtSlot() def nexttime(self): time = self.time.nextTime() self.time.setCurrent(time) self.resetSlider(time) self.textInput.setFocus() + def closeEvent(self, event): self.mainwindow.waitingMessages.messageAnswered(self.channel) self.windowClosed.emit(self.title()) - windowClosed = QtCore.pyqtSignal('QString') + windowClosed = QtCore.pyqtSignal("QString") -timelist = ["0:00", - "0:01", - "0:02", - "0:04", - "0:06", - "0:10", - "0:14", - "0:22", - "0:30", - "0:41", - "1:00", - "1:34", - "2:16", - "3:14", - "4:13", - "4:20", - "5:25", - "6:12", - "7:30", - "8:44", - "10:25", - "11:34", - "14:13", - "16:12", - "17:44", - "22:22", - "25:10", - "33:33", - "42:00", - "43:14", - "50:00", - "62:12", - "75:00", - "88:44", - "100", - "133", - "143", - "188", - "200", - "222", - "250", - "314", - "333", - "413", - "420", - "500", - "600", - "612", - "888", - "1000", - "1025"] - -timedlist = [timedelta(0), - timedelta(0, 60), - timedelta(0, 120), - timedelta(0, 240), - timedelta(0, 360), - timedelta(0, 600), - timedelta(0, 840), - timedelta(0, 1320), - timedelta(0, 1800), - timedelta(0, 2460), - timedelta(0, 3600), - timedelta(0, 5640), - timedelta(0, 8160), - timedelta(0, 11640), - timedelta(0, 15180), - timedelta(0, 15600), - timedelta(0, 19500), - timedelta(0, 22320), - timedelta(0, 27000), - timedelta(0, 31440), - timedelta(0, 37500), - timedelta(0, 41640), - timedelta(0, 51180), - timedelta(0, 58320), - timedelta(0, 63840), - timedelta(0, 80520), - timedelta(1, 4200), - timedelta(1, 34380), - timedelta(1, 64800), - timedelta(1, 69240), - timedelta(2, 7200), - timedelta(2, 51120), - timedelta(3, 10800), - timedelta(3, 60240), - timedelta(4, 14400), - timedelta(5, 46800), - timedelta(5, 82800), - timedelta(7, 72000), - timedelta(8, 28800), - timedelta(9, 21600), - timedelta(10, 36000), - timedelta(13, 7200), - timedelta(13, 75600), - timedelta(17, 18000), - timedelta(17, 43200), - timedelta(20, 72000), - timedelta(25), - timedelta(25, 43200), - timedelta(37), - timedelta(41, 57600), - timedelta(42, 61200)] +timelist = [ + "0:00", + "0:01", + "0:02", + "0:04", + "0:06", + "0:10", + "0:14", + "0:22", + "0:30", + "0:41", + "1:00", + "1:34", + "2:16", + "3:14", + "4:13", + "4:20", + "5:25", + "6:12", + "7:30", + "8:44", + "10:25", + "11:34", + "14:13", + "16:12", + "17:44", + "22:22", + "25:10", + "33:33", + "42:00", + "43:14", + "50:00", + "62:12", + "75:00", + "88:44", + "100", + "133", + "143", + "188", + "200", + "222", + "250", + "314", + "333", + "413", + "420", + "500", + "600", + "612", + "888", + "1000", + "1025", +] +timedlist = [ + timedelta(0), + timedelta(0, 60), + timedelta(0, 120), + timedelta(0, 240), + timedelta(0, 360), + timedelta(0, 600), + timedelta(0, 840), + timedelta(0, 1320), + timedelta(0, 1800), + timedelta(0, 2460), + timedelta(0, 3600), + timedelta(0, 5640), + timedelta(0, 8160), + timedelta(0, 11640), + timedelta(0, 15180), + timedelta(0, 15600), + timedelta(0, 19500), + timedelta(0, 22320), + timedelta(0, 27000), + timedelta(0, 31440), + timedelta(0, 37500), + timedelta(0, 41640), + timedelta(0, 51180), + timedelta(0, 58320), + timedelta(0, 63840), + timedelta(0, 80520), + timedelta(1, 4200), + timedelta(1, 34380), + timedelta(1, 64800), + timedelta(1, 69240), + timedelta(2, 7200), + timedelta(2, 51120), + timedelta(3, 10800), + timedelta(3, 60240), + timedelta(4, 14400), + timedelta(5, 46800), + timedelta(5, 82800), + timedelta(7, 72000), + timedelta(8, 28800), + timedelta(9, 21600), + timedelta(10, 36000), + timedelta(13, 7200), + timedelta(13, 75600), + timedelta(17, 18000), + timedelta(17, 43200), + timedelta(20, 72000), + timedelta(25), + timedelta(25, 43200), + timedelta(37), + timedelta(41, 57600), + timedelta(42, 61200), +] diff --git a/menus.py b/menus.py index 193d2b2..f88a476 100644 --- a/menus.py +++ b/menus.py @@ -22,24 +22,30 @@ from parsetools import lexMessage QString = str _datadir = ostools.getDataDir() + class PesterQuirkItem(QtWidgets.QTreeWidgetItem): def __init__(self, quirk): parent = None QtWidgets.QTreeWidgetItem.__init__(self, parent) self.quirk = quirk self.setText(0, str(quirk)) + def update(self, quirk): self.quirk = quirk self.setText(0, str(quirk)) + def __lt__(self, quirkitem): """Sets the order of quirks if auto-sorted by Qt. Obsolete now.""" if self.quirk.type == "prefix": return True - elif (self.quirk.type == "replace" or self.quirk.type == "regexp") and \ - quirkitem.type == "suffix": + elif ( + self.quirk.type == "replace" or self.quirk.type == "regexp" + ) and quirkitem.type == "suffix": return True else: return False + + class PesterQuirkList(QtWidgets.QTreeWidget): def __init__(self, mainwindow, parent): QtWidgets.QTreeWidget.__init__(self, parent) @@ -54,18 +60,20 @@ class PesterQuirkList(QtWidgets.QTreeWidget): item = PesterQuirkItem(q) self.addItem(item, False) self.changeCheckState() - #self.setDragEnabled(True) - #self.setDragDropMode(QtGui.QAbstractItemView.DragDropMode.InternalMove) + # self.setDragEnabled(True) + # self.setDragDropMode(QtGui.QAbstractItemView.DragDropMode.InternalMove) self.setDropIndicatorShown(True) self.setSortingEnabled(False) self.setIndentation(15) self.header().hide() def addItem(self, item, new=True): - item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable - | QtCore.Qt.ItemFlag.ItemIsDragEnabled - | QtCore.Qt.ItemFlag.ItemIsUserCheckable - | QtCore.Qt.ItemFlag.ItemIsEnabled) + item.setFlags( + QtCore.Qt.ItemFlag.ItemIsSelectable + | QtCore.Qt.ItemFlag.ItemIsDragEnabled + | QtCore.Qt.ItemFlag.ItemIsUserCheckable + | QtCore.Qt.ItemFlag.ItemIsEnabled + ) if item.quirk.on: item.setCheckState(0, QtCore.Qt.CheckState.Checked) else: @@ -73,7 +81,8 @@ class PesterQuirkList(QtWidgets.QTreeWidget): if new: curgroup = self.currentItem() if curgroup: - if curgroup.parent(): curgroup = curgroup.parent() + if curgroup.parent(): + curgroup = curgroup.parent() item.quirk.quirk["group"] = item.quirk.group = curgroup.text(0) found = self.findItems(item.quirk.group, QtCore.Qt.MatchFlag.MatchExactly) if len(found) > 0: @@ -81,10 +90,14 @@ class PesterQuirkList(QtWidgets.QTreeWidget): else: child_1 = QtWidgets.QTreeWidgetItem([item.quirk.group]) self.addTopLevelItem(child_1) - child_1.setFlags(child_1.flags() - | QtCore.Qt.ItemFlag.ItemIsUserCheckable - | QtCore.Qt.ItemFlag.ItemIsEnabled) - child_1.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ChildIndicatorPolicy.DontShowIndicatorWhenChildless) + child_1.setFlags( + child_1.flags() + | QtCore.Qt.ItemFlag.ItemIsUserCheckable + | QtCore.Qt.ItemFlag.ItemIsEnabled + ) + child_1.setChildIndicatorPolicy( + QtWidgets.QTreeWidgetItem.ChildIndicatorPolicy.DontShowIndicatorWhenChildless + ) child_1.setCheckState(0, QtCore.Qt.CheckState.Unchecked) child_1.setExpanded(True) child_1.addChild(item) @@ -93,93 +106,113 @@ class PesterQuirkList(QtWidgets.QTreeWidget): def currentQuirk(self): if type(self.currentItem()) is PesterQuirkItem: return self.currentItem() - else: return None + else: + return None @QtCore.pyqtSlot() def upShiftQuirk(self): - found = self.findItems(self.currentItem().text(0), - QtCore.Qt.MatchFlag.MatchExactly) - if len(found): # group + found = self.findItems( + self.currentItem().text(0), QtCore.Qt.MatchFlag.MatchExactly + ) + if len(found): # group i = self.indexOfTopLevelItem(found[0]) if i > 0: expand = found[0].isExpanded() shifted_item = self.takeTopLevelItem(i) - self.insertTopLevelItem(i-1, shifted_item) + self.insertTopLevelItem(i - 1, shifted_item) shifted_item.setExpanded(expand) self.setCurrentItem(shifted_item) - else: # quirk - found = self.findItems(self.currentItem().text(0), - QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive) + else: # quirk + found = self.findItems( + self.currentItem().text(0), + QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, + ) for f in found: - if not f.isSelected(): continue - if not f.parent(): continue + if not f.isSelected(): + continue + if not f.parent(): + continue i = f.parent().indexOfChild(f) - if i > 0: # keep in same group + if i > 0: # keep in same group p = f.parent() shifted_item = f.parent().takeChild(i) - p.insertChild(i-1, shifted_item) + p.insertChild(i - 1, shifted_item) self.setCurrentItem(shifted_item) - else: # move to another group + else: # move to another group j = self.indexOfTopLevelItem(f.parent()) - if j <= 0: continue + if j <= 0: + continue shifted_item = f.parent().takeChild(i) - self.topLevelItem(j-1).addChild(shifted_item) + self.topLevelItem(j - 1).addChild(shifted_item) self.setCurrentItem(shifted_item) self.changeCheckState() @QtCore.pyqtSlot() def downShiftQuirk(self): - found = self.findItems(self.currentItem().text(0), - QtCore.Qt.MatchFlag.MatchExactly) - if len(found): # group + found = self.findItems( + self.currentItem().text(0), QtCore.Qt.MatchFlag.MatchExactly + ) + if len(found): # group i = self.indexOfTopLevelItem(found[0]) - if i < self.topLevelItemCount()-1 and i >= 0: + if i < self.topLevelItemCount() - 1 and i >= 0: expand = found[0].isExpanded() shifted_item = self.takeTopLevelItem(i) - self.insertTopLevelItem(i+1, shifted_item) + self.insertTopLevelItem(i + 1, shifted_item) shifted_item.setExpanded(expand) self.setCurrentItem(shifted_item) - else: # quirk - found = self.findItems(self.currentItem().text(0), - QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive) + else: # quirk + found = self.findItems( + self.currentItem().text(0), + QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, + ) for f in found: - if not f.isSelected(): continue - if not f.parent(): continue + if not f.isSelected(): + continue + if not f.parent(): + continue i = f.parent().indexOfChild(f) - if i < f.parent().childCount()-1 and i >= 0: + if i < f.parent().childCount() - 1 and i >= 0: p = f.parent() shifted_item = f.parent().takeChild(i) - p.insertChild(i+1, shifted_item) + p.insertChild(i + 1, shifted_item) self.setCurrentItem(shifted_item) else: j = self.indexOfTopLevelItem(f.parent()) - if j >= self.topLevelItemCount()-1 or j < 0: continue + if j >= self.topLevelItemCount() - 1 or j < 0: + continue shifted_item = f.parent().takeChild(i) - self.topLevelItem(j+1).insertChild(0, shifted_item) + self.topLevelItem(j + 1).insertChild(0, shifted_item) self.setCurrentItem(shifted_item) self.changeCheckState() @QtCore.pyqtSlot() def removeCurrent(self): i = self.currentItem() - found = self.findItems(i.text(0), - QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive) + found = self.findItems( + i.text(0), + QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, + ) for f in found: - if not f.isSelected(): continue - if not f.parent(): # group + if not f.isSelected(): + continue + if not f.parent(): # group msgbox = QtWidgets.QMessageBox() msgbox.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) msgbox.setObjectName("delquirkwarning") msgbox.setWindowTitle("WARNING!") - msgbox.setInformativeText("Are you sure you want to delete the quirk group: %s" % (f.text(0))) - msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok - | QtWidgets.QMessageBox.StandardButton.Cancel) + msgbox.setInformativeText( + "Are you sure you want to delete the quirk group: %s" % (f.text(0)) + ) + msgbox.setStandardButtons( + QtWidgets.QMessageBox.StandardButton.Ok + | QtWidgets.QMessageBox.StandardButton.Cancel + ) # Find the Cancel button and make it default for b in msgbox.buttons(): - if msgbox.buttonRole(b) == QtWidgets.QMessageBox.ButtonRole.RejectRole: + if ( + msgbox.buttonRole(b) + == QtWidgets.QMessageBox.ButtonRole.RejectRole + ): # We found the 'OK' button, set it as the default b.setDefault(True) b.setAutoDefault(True) @@ -196,10 +229,12 @@ class PesterQuirkList(QtWidgets.QTreeWidget): @QtCore.pyqtSlot() def addQuirkGroup(self): - if not hasattr(self, 'addgroupdialog'): + if not hasattr(self, "addgroupdialog"): self.addgroupdialog = None if not self.addgroupdialog: - (gname, ok) = QtWidgets.QInputDialog.getText(self, "Add Group", "Enter a name for the new quirk group:") + (gname, ok) = QtWidgets.QInputDialog.getText( + self, "Add Group", "Enter a name for the new quirk group:" + ) if ok: gname = str(gname) if re.search("[^A-Za-z0-9_\s]", gname) is not None: @@ -218,10 +253,14 @@ class PesterQuirkList(QtWidgets.QTreeWidget): return child_1 = QtWidgets.QTreeWidgetItem([gname]) self.addTopLevelItem(child_1) - child_1.setFlags(child_1.flags() - | QtCore.Qt.ItemFlag.ItemIsUserCheckable - | QtCore.Qt.ItemFlag.ItemIsEnabled) - child_1.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ChildIndicatorPolicy.DontShowIndicatorWhenChildless) + child_1.setFlags( + child_1.flags() + | QtCore.Qt.ItemFlag.ItemIsUserCheckable + | QtCore.Qt.ItemFlag.ItemIsEnabled + ) + child_1.setChildIndicatorPolicy( + QtWidgets.QTreeWidgetItem.ChildIndicatorPolicy.DontShowIndicatorWhenChildless + ) child_1.setCheckState(0, QtCore.Qt.CheckState.Unchecked) child_1.setExpanded(True) @@ -239,14 +278,20 @@ class PesterQuirkList(QtWidgets.QTreeWidget): noneChecked = False else: allChecked = False - if allChecked: self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.Checked) - elif noneChecked: self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.PartiallyChecked) - else: self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.Checked) + if allChecked: + self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.Checked) + elif noneChecked: + self.topLevelItem(i).setCheckState( + 0, QtCore.Qt.CheckState.PartiallyChecked + ) + else: + self.topLevelItem(i).setCheckState(0, QtCore.Qt.CheckState.Checked) else: state = self.topLevelItem(index).checkState(0) for j in range(self.topLevelItem(index).childCount()): self.topLevelItem(index).child(j).setCheckState(0, state) + class QuirkTesterWindow(QtWidgets.QDialog): def __init__(self, parent): QtWidgets.QDialog.__init__(self, parent) @@ -254,7 +299,7 @@ class QuirkTesterWindow(QtWidgets.QDialog): self.mainwindow = parent.mainwindow self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.setWindowTitle("Quirk Tester") - self.resize(350,300) + self.resize(350, 300) self.textArea = PesterText(self.mainwindow.theme, self) self.textInput = PesterInput(self.mainwindow.theme, self) @@ -276,6 +321,7 @@ class QuirkTesterWindow(QtWidgets.QDialog): def clearNewMessage(self): pass + @QtCore.pyqtSlot() def sentMessage(self): text = str(self.textInput.text()) @@ -296,13 +342,14 @@ class QuirkTesterWindow(QtWidgets.QDialog): def closeEvent(self, event): self.parent().quirktester = None + class PesterQuirkTypes(QtWidgets.QDialog): def __init__(self, parent, quirk=None): QtWidgets.QDialog.__init__(self, parent) self.mainwindow = parent.mainwindow self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.setWindowTitle("Quirk Wizard") - self.resize(500,310) + self.resize(500, 310) self.quirk = quirk self.pages = QtWidgets.QStackedWidget(self) @@ -335,7 +382,8 @@ class PesterQuirkTypes(QtWidgets.QDialog): self.funclist2.setStyleSheet("color: #000000; background-color: #FFFFFF;") from parsetools import quirkloader - funcs = [q+"()" for q in list(quirkloader.quirks.keys())] + + funcs = [q + "()" for q in list(quirkloader.quirks.keys())] funcs.sort() self.funclist.addItems(funcs) self.funclist2.addItems(funcs) @@ -346,9 +394,9 @@ class PesterQuirkTypes(QtWidgets.QDialog): self.reloadQuirkFuncButton2.clicked.connect(self.reloadQuirkFuncSlot) self.funclist.setMaximumWidth(160) - self.funclist.resize(160,50) + self.funclist.resize(160, 50) self.funclist2.setMaximumWidth(160) - self.funclist2.resize(160,50) + self.funclist2.resize(160, 50) layout_f = QtWidgets.QVBoxLayout() layout_f.addWidget(QtWidgets.QLabel("Available Regexp\nFunctions")) layout_f.addWidget(self.funclist) @@ -414,8 +462,10 @@ class PesterQuirkTypes(QtWidgets.QDialog): layout_replace.addLayout(layout_3) layout_3 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") - excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos." - + "\nThe replace is applied on every substring individually.") + excludeCheckbox.setToolTip( + "Splits input to exclude smilies, weblinks, @handles, and #memos." + + "\nThe replace is applied on every substring individually." + ) layout_3.addWidget(excludeCheckbox) layout_replace.addLayout(layout_3) @@ -436,9 +486,11 @@ class PesterQuirkTypes(QtWidgets.QDialog): layout_regexp.addLayout(layout_3) layout_3 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") - excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos." - + "\nSince the replace is applied on every substring individually," - + "\ncertain patterns or functions like gradients may not work correctly.") + excludeCheckbox.setToolTip( + "Splits input to exclude smilies, weblinks, @handles, and #memos." + + "\nSince the replace is applied on every substring individually," + + "\ncertain patterns or functions like gradients may not work correctly." + ) layout_3.addWidget(excludeCheckbox) layout_regexp.addLayout(layout_3) layout_all.addLayout(layout_f) @@ -480,9 +532,11 @@ class PesterQuirkTypes(QtWidgets.QDialog): layout_random.addLayout(layout_6) layout_9 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") - excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos." - + "\nSince the replace is applied on every substring individually," - + "\ncertain patterns or functions like gradients may not work correctly.") + excludeCheckbox.setToolTip( + "Splits input to exclude smilies, weblinks, @handles, and #memos." + + "\nSince the replace is applied on every substring individually," + + "\ncertain patterns or functions like gradients may not work correctly." + ) layout_9.addWidget(excludeCheckbox) layout_random.addLayout(layout_9) @@ -514,33 +568,43 @@ class PesterQuirkTypes(QtWidgets.QDialog): layout_3 = QtWidgets.QHBoxLayout() excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies") - excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos." - + "\nThe replace is applied on every substring individually.") + excludeCheckbox.setToolTip( + "Splits input to exclude smilies, weblinks, @handles, and #memos." + + "\nThe replace is applied on every substring individually." + ) layout_3.addWidget(excludeCheckbox) layout_mispeller.addLayout(layout_3) if quirk: - types = ["prefix","suffix","replace","regexp","random","spelling"] - for (i,r) in enumerate(self.radios): + types = ["prefix", "suffix", "replace", "regexp", "random", "spelling"] + for (i, r) in enumerate(self.radios): if i == types.index(quirk.quirk.type): r.setChecked(True) - self.changePage(types.index(quirk.quirk.type)+1) + self.changePage(types.index(quirk.quirk.type) + 1) page = self.pages.currentWidget().layout() q = quirk.quirk.quirk - if q["type"] in ("prefix","suffix"): + if q["type"] in ("prefix", "suffix"): page.itemAt(1).layout().itemAt(1).widget().setText(q["value"]) elif q["type"] == "replace": page.itemAt(1).layout().itemAt(1).widget().setText(q["from"]) page.itemAt(2).layout().itemAt(1).widget().setText(q["to"]) try: - page.itemAt(3).layout().itemAt(0).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) + page.itemAt(3).layout().itemAt(0).widget().setCheckState( + QtCore.Qt.CheckState(int(q["checkstate"])) + ) except (KeyError, ValueError) as e: print("KeyError: %s" % str(e)) elif q["type"] == "regexp": - page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().setText(q["from"]) - page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().setText(q["to"]) + page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().setText( + q["from"] + ) + page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().setText( + q["to"] + ) try: - page.itemAt(2).layout().itemAt(3).layout().itemAt(0).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) + page.itemAt(2).layout().itemAt(3).layout().itemAt( + 0 + ).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) except (KeyError, ValueError) as e: print("KeyError: %s" % str(e)) elif q["type"] == "random": @@ -548,13 +612,17 @@ class PesterQuirkTypes(QtWidgets.QDialog): for v in q["randomlist"]: item = QtWidgets.QListWidgetItem(v, self.replacelist) try: - page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) + page.itemAt(2).layout().itemAt(2).layout().itemAt( + 0 + ).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) except (KeyError, ValueError) as e: print("KeyError: %s" % str(e)) elif q["type"] == "spelling": self.slider.setValue(q["percentage"]) try: - page.itemAt(3).layout().itemAt(0).widget().setCheckState(QtCore.Qt.CheckState(int(q["checkstate"]))) + page.itemAt(3).layout().itemAt(0).widget().setCheckState( + QtCore.Qt.CheckState(int(q["checkstate"])) + ) except (KeyError, ValueError) as e: print("KeyError: %s" % str(e)) @@ -565,13 +633,15 @@ class PesterQuirkTypes(QtWidgets.QDialog): def changePage(self, page): c = self.pages.count() - if page >= c or page < 0: return + if page >= c or page < 0: + return self.back.setEnabled(page > 0) if page >= 1 and page <= 6: self.next.setText("Finish") else: self.next.setText("Next") self.pages.setCurrentIndex(page) + @QtCore.pyqtSlot() def nextPage(self): if self.next.text() == "Finish": @@ -579,11 +649,12 @@ class PesterQuirkTypes(QtWidgets.QDialog): return cur = self.pages.currentIndex() if cur == 0: - for (i,r) in enumerate(self.radios): + for (i, r) in enumerate(self.radios): if r.isChecked(): - self.changePage(i+1) + self.changePage(i + 1) else: - self.changePage(cur+1) + self.changePage(cur + 1) + @QtCore.pyqtSlot() def backPage(self): cur = self.pages.currentIndex() @@ -592,13 +663,15 @@ class PesterQuirkTypes(QtWidgets.QDialog): @QtCore.pyqtSlot(int) def printValue(self, value): - self.current.setText(str(value)+"%") + self.current.setText(str(value) + "%") + @QtCore.pyqtSlot() def addRandomString(self): text = str(self.replaceinput.text()) item = QtWidgets.QListWidgetItem(text, self.replacelist) self.replaceinput.setText("") self.replaceinput.setFocus() + @QtCore.pyqtSlot() def removeRandomString(self): if not self.replacelist.currentItem(): @@ -610,14 +683,16 @@ class PesterQuirkTypes(QtWidgets.QDialog): @QtCore.pyqtSlot() def reloadQuirkFuncSlot(self): from parsetools import reloadQuirkFunctions, quirkloader + reloadQuirkFunctions() - funcs = [q+"()" for q in list(quirkloader.quirks.keys())] + funcs = [q + "()" for q in list(quirkloader.quirks.keys())] funcs.sort() self.funclist.clear() self.funclist.addItems(funcs) self.funclist2.clear() self.funclist2.addItems(funcs) + class PesterChooseQuirks(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) @@ -644,8 +719,8 @@ class PesterChooseQuirks(QtWidgets.QDialog): self.newGroupButton.setToolTip("New Quirk Group") self.newGroupButton.clicked.connect(self.quirkList.addQuirkGroup) - layout_quirklist = QtWidgets.QHBoxLayout() #the nude layout quirklist - layout_shiftbuttons = QtWidgets.QVBoxLayout() #the shift button layout + layout_quirklist = QtWidgets.QHBoxLayout() # the nude layout quirklist + layout_shiftbuttons = QtWidgets.QVBoxLayout() # the shift button layout layout_shiftbuttons.addWidget(self.upShiftButton) layout_shiftbuttons.addWidget(self.newGroupButton) layout_shiftbuttons.addWidget(self.downShiftButton) @@ -678,7 +753,7 @@ class PesterChooseQuirks(QtWidgets.QDialog): layout_0 = QtWidgets.QVBoxLayout() layout_0.addLayout(layout_quirklist) layout_0.addLayout(layout_1) - #layout_0.addLayout(layout_2) + # layout_0.addLayout(layout_2) layout_0.addLayout(layout_3) layout_0.addLayout(layout_ok) @@ -690,19 +765,20 @@ class PesterChooseQuirks(QtWidgets.QDialog): for j in range(self.quirkList.topLevelItem(i).childCount()): u.append(self.quirkList.topLevelItem(i).child(j).quirk) return u - #return [self.quirkList.item(i).quirk for i in range(self.quirkList.count())] + # return [self.quirkList.item(i).quirk for i in range(self.quirkList.count())] + def testquirks(self): u = [] for i in range(self.quirkList.topLevelItemCount()): for j in range(self.quirkList.topLevelItem(i).childCount()): item = self.quirkList.topLevelItem(i).child(j) - if (item.checkState(0) == QtCore.Qt.CheckState.Checked): + if item.checkState(0) == QtCore.Qt.CheckState.Checked: u.append(item.quirk) return u @QtCore.pyqtSlot() def testQuirks(self): - if not hasattr(self, 'quirktester'): + if not hasattr(self, "quirktester"): self.quirktester = None if self.quirktester: return @@ -712,13 +788,14 @@ class PesterChooseQuirks(QtWidgets.QDialog): @QtCore.pyqtSlot() def editSelected(self): q = self.quirkList.currentQuirk() - if not q: return - #quirk = q.quirk + if not q: + return + # quirk = q.quirk self.addQuirkDialog(q) @QtCore.pyqtSlot() def addQuirkDialog(self, quirk=None): - if not hasattr(self, 'quirkadd'): + if not hasattr(self, "quirkadd"): self.quirkadd = None if self.quirkadd: return @@ -726,51 +803,100 @@ class PesterChooseQuirks(QtWidgets.QDialog): self.quirkadd.accepted.connect(self.addQuirk) self.quirkadd.rejected.connect(self.closeQuirk) self.quirkadd.show() + @QtCore.pyqtSlot() def addQuirk(self): types = ["prefix", "suffix", "replace", "regexp", "random", "spelling"] vdict = {} - vdict["type"] = types[self.quirkadd.pages.currentIndex()-1] + vdict["type"] = types[self.quirkadd.pages.currentIndex() - 1] page = self.quirkadd.pages.currentWidget().layout() - if vdict["type"] in ("prefix","suffix"): + if vdict["type"] in ("prefix", "suffix"): vdict["value"] = str(page.itemAt(1).layout().itemAt(1).widget().text()) elif vdict["type"] == "replace": vdict["from"] = str(page.itemAt(1).layout().itemAt(1).widget().text()) vdict["to"] = str(page.itemAt(2).layout().itemAt(1).widget().text()) try: # PyQt6 - vdict["checkstate"] = str(page.itemAt(3).layout().itemAt(0).widget().checkState().value) + vdict["checkstate"] = str( + page.itemAt(3).layout().itemAt(0).widget().checkState().value + ) except AttributeError: # PyQt5 - vdict["checkstate"] = str(page.itemAt(3).layout().itemAt(0).widget().checkState()) + vdict["checkstate"] = str( + page.itemAt(3).layout().itemAt(0).widget().checkState() + ) elif vdict["type"] == "regexp": - vdict["from"] = str(page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().text()) - vdict["to"] = str(page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().text()) + vdict["from"] = str( + page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().text() + ) + vdict["to"] = str( + page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().text() + ) try: # PyQt6 - vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(3).layout().itemAt(0).widget().checkState().value) + vdict["checkstate"] = str( + page.itemAt(2) + .layout() + .itemAt(3) + .layout() + .itemAt(0) + .widget() + .checkState() + .value + ) except AttributeError: # PyQt5 - vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(3).layout().itemAt(0).widget().checkState()) + vdict["checkstate"] = str( + page.itemAt(2) + .layout() + .itemAt(3) + .layout() + .itemAt(0) + .widget() + .checkState() + ) elif vdict["type"] == "random": vdict["from"] = str(self.quirkadd.regexp.text()) try: # PyQt6 - vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().checkState().value) + vdict["checkstate"] = str( + page.itemAt(2) + .layout() + .itemAt(2) + .layout() + .itemAt(0) + .widget() + .checkState() + .value + ) except AttributeError: # PyQt5 - vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().checkState()) - randomlist = [str(self.quirkadd.replacelist.item(i).text()) - for i in range(0,self.quirkadd.replacelist.count())] + vdict["checkstate"] = str( + page.itemAt(2) + .layout() + .itemAt(2) + .layout() + .itemAt(0) + .widget() + .checkState() + ) + randomlist = [ + str(self.quirkadd.replacelist.item(i).text()) + for i in range(0, self.quirkadd.replacelist.count()) + ] vdict["randomlist"] = randomlist elif vdict["type"] == "spelling": vdict["percentage"] = self.quirkadd.slider.value() try: # PyQt6 - vdict["checkstate"] = str(page.itemAt(3).layout().itemAt(0).widget().checkState().value) + vdict["checkstate"] = str( + page.itemAt(3).layout().itemAt(0).widget().checkState().value + ) except AttributeError: # PyQt5 - vdict["checkstate"] = str(page.itemAt(3).layout().itemAt(0).widget().checkState()) + vdict["checkstate"] = str( + page.itemAt(3).layout().itemAt(0).widget().checkState() + ) if vdict["type"] in ("regexp", "random"): try: re.compile(vdict["from"]) @@ -789,10 +915,12 @@ class PesterChooseQuirks(QtWidgets.QDialog): else: self.quirkadd.quirk.update(quirk) self.quirkadd = None + @QtCore.pyqtSlot() def closeQuirk(self): self.quirkadd = None + class PesterChooseTheme(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) @@ -830,8 +958,11 @@ class PesterChooseTheme(QtWidgets.QDialog): self.accepted.connect(parent.themeSelected) self.rejected.connect(parent.closeTheme) + class PesterChooseProfile(QtWidgets.QDialog): - def __init__(self, userprofile, config, theme, parent, collision=None, svsnick=None): + def __init__( + self, userprofile, config, theme, parent, collision=None, svsnick=None + ): QtWidgets.QDialog.__init__(self, parent) self.userprofile = userprofile self.theme = theme @@ -839,15 +970,21 @@ class PesterChooseProfile(QtWidgets.QDialog): self.parent = parent self.setStyleSheet(self.theme["main/defaultwindow/style"]) - self.currentHandle = QtWidgets.QLabel("CHANGING FROM %s" % userprofile.chat.handle) + self.currentHandle = QtWidgets.QLabel( + "CHANGING FROM %s" % userprofile.chat.handle + ) self.chumHandle = QtWidgets.QLineEdit(self) self.chumHandle.setMinimumWidth(200) self.chumHandle.setObjectName("setprofilehandle") - self.chumHandleLabel = QtWidgets.QLabel(self.theme["main/mychumhandle/label/text"], self) + self.chumHandleLabel = QtWidgets.QLabel( + self.theme["main/mychumhandle/label/text"], self + ) self.chumColorButton = QtWidgets.QPushButton(self) self.chumColorButton.setObjectName("setprofilecolor") self.chumColorButton.resize(50, 20) - self.chumColorButton.setStyleSheet("background: %s" % (userprofile.chat.colorhtml())) + self.chumColorButton.setStyleSheet( + "background: %s" % (userprofile.chat.colorhtml()) + ) self.chumcolor = userprofile.chat.color self.chumColorButton.clicked.connect(self.openColorDialog) layout_1 = QtWidgets.QHBoxLayout() @@ -861,7 +998,7 @@ class PesterChooseProfile(QtWidgets.QDialog): self.profileBox = QtWidgets.QComboBox(self) self.profileBox.addItem("Choose a profile...") for p in avail_profiles: - #PchumLog.debug("Adding profile: %s" % p.chat.handle) + # PchumLog.debug("Adding profile: %s" % p.chat.handle) self.profileBox.addItem(p.chat.handle) else: self.profileBox = None @@ -886,13 +1023,19 @@ class PesterChooseProfile(QtWidgets.QDialog): layout_0 = QtWidgets.QVBoxLayout() if collision: - collision_warning = QtWidgets.QLabel("%s is taken already! Pick a new profile." % (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) + 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.addWidget( + self.currentHandle, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter + ) layout_0.addLayout(layout_1) if avail_profiles: profileLabel = QtWidgets.QLabel("Or choose an existing profile:", self) @@ -927,7 +1070,10 @@ class PesterChooseProfile(QtWidgets.QDialog): self.errorMsg.setText("PROFILE HANDLE IS TOO LONG") return if not PesterProfile.checkValid(handle)[0]: - self.errorMsg.setText("NOT A VALID CHUMTAG. REASON:\n%s" % (PesterProfile.checkValid(handle)[1])) + self.errorMsg.setText( + "NOT A VALID CHUMTAG. REASON:\n%s" + % (PesterProfile.checkValid(handle)[1]) + ) return self.accept() @@ -941,7 +1087,9 @@ class PesterChooseProfile(QtWidgets.QDialog): problem.setObjectName("errmsg") problem.setStyleSheet(self.theme["main/defaultwindow/style"]) problem.setWindowTitle("Problem!") - problem.setInformativeText("You can't delete the profile you're currently using!") + problem.setInformativeText( + "You can't delete the profile you're currently using!" + ) problem.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) problem.exec() return @@ -949,22 +1097,29 @@ class PesterChooseProfile(QtWidgets.QDialog): msgbox = QtWidgets.QMessageBox() msgbox.setStyleSheet(self.theme["main/defaultwindow/style"]) msgbox.setWindowTitle("WARNING!") - msgbox.setInformativeText("Are you sure you want to delete the profile: %s" % (handle)) - msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok - | QtWidgets.QMessageBox.StandardButton.Cancel) + msgbox.setInformativeText( + "Are you sure you want to delete the profile: %s" % (handle) + ) + msgbox.setStandardButtons( + QtWidgets.QMessageBox.StandardButton.Ok + | QtWidgets.QMessageBox.StandardButton.Cancel + ) ret = msgbox.exec() if ret == QtWidgets.QMessageBox.StandardButton.Ok: try: - remove(_datadir+"profiles/%s.js" % (handle)) + remove(_datadir + "profiles/%s.js" % (handle)) except OSError: problem = QtWidgets.QMessageBox() problem.setObjectName("errmsg") problem.setStyleSheet(self.theme["main/defaultwindow/style"]) problem.setWindowTitle("Problem!") - problem.setInformativeText("There was a problem deleting the profile: %s" % (handle)) + problem.setInformativeText( + "There was a problem deleting the profile: %s" % (handle) + ) problem.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) problem.exec() + class PesterMentions(QtWidgets.QDialog): def __init__(self, window, theme, parent): QtWidgets.QDialog.__init__(self, parent) @@ -1014,7 +1169,7 @@ class PesterMentions(QtWidgets.QDialog): @QtCore.pyqtSlot() def addMention(self, mitem=None): - d = {"label": "Mention:", "inputname": "value" } + d = {"label": "Mention:", "inputname": "value"} if mitem is not None: d["value"] = str(mitem.text()) pdict = MultiTextDialog("ENTER MENTION", self, d).getText() @@ -1039,6 +1194,7 @@ class PesterMentions(QtWidgets.QDialog): if i >= 0: self.mentionlist.takeItem(i) + class PesterOptions(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) @@ -1059,16 +1215,19 @@ class PesterOptions(QtWidgets.QDialog): self.tabs = QtWidgets.QButtonGroup(self) self.tabs.buttonClicked.connect(self.changePage) # Verify working - self.tabNames = ["Chum List", - "Conversations", - "Interface", - "Sound", - "Notifications", - "Logging", - "Idle/Updates", - "Theme", - "Connection"] - if parent.advanced: self.tabNames.append("Advanced") + self.tabNames = [ + "Chum List", + "Conversations", + "Interface", + "Sound", + "Notifications", + "Logging", + "Idle/Updates", + "Theme", + "Connection", + ] + if parent.advanced: + self.tabNames.append("Advanced") for t in self.tabNames: button = QtWidgets.QPushButton(t) self.tabs.addButton(button) @@ -1080,8 +1239,10 @@ class PesterOptions(QtWidgets.QDialog): self.bandwidthcheck = QtWidgets.QCheckBox("Low Bandwidth", self) if self.config.lowBandwidth(): self.bandwidthcheck.setChecked(True) - bandwidthLabel = QtWidgets.QLabel("(Stops you for receiving the flood of MOODS,\n" - " though stops chumlist from working properly)") + bandwidthLabel = QtWidgets.QLabel( + "(Stops you for receiving the flood of MOODS,\n" + " though stops chumlist from working properly)" + ) font = bandwidthLabel.font() font.setPointSize(8) bandwidthLabel.setFont(font) @@ -1144,18 +1305,17 @@ class PesterOptions(QtWidgets.QDialog): # Disable the volume slider if we can't actually use it. if parent.canSetVolume(): self.currentVol = QtWidgets.QLabel( - "{0!s}%".format(self.config.volume()), self) + "{0!s}%".format(self.config.volume()), self + ) # We don't need to explicitly set this, but it helps drive the # point home self.volume.setEnabled(True) else: # We can't set the volume.... - self.currentVol = QtWidgets.QLabel( - "(Disabled: Sound Mixer Error)", self) + self.currentVol = QtWidgets.QLabel("(Disabled: Sound Mixer Error)", self) self.volume.setEnabled(False) self.currentVol.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter) - self.timestampcheck = QtWidgets.QCheckBox("Time Stamps", self) if self.config.showTimeStamps(): self.timestampcheck.setChecked(True) @@ -1171,7 +1331,9 @@ class PesterOptions(QtWidgets.QDialog): if self.config.showSeconds(): self.secondscheck.setChecked(True) - self.memomessagecheck = QtWidgets.QCheckBox("Show OP and Voice Messages in Memos", self) + self.memomessagecheck = QtWidgets.QCheckBox( + "Show OP and Voice Messages in Memos", self + ) if self.config.opvoiceMessages(): self.memomessagecheck.setChecked(True) @@ -1179,7 +1341,9 @@ class PesterOptions(QtWidgets.QDialog): self.animationscheck = QtWidgets.QCheckBox("Use animated smilies", self) if self.config.animations(): self.animationscheck.setChecked(True) - animateLabel = QtWidgets.QLabel("(Disable if you leave chats open for LOOOONG periods of time)") + animateLabel = QtWidgets.QLabel( + "(Disable if you leave chats open for LOOOONG periods of time)" + ) font = animateLabel.font() font.setPointSize(8) animateLabel.setFont(font) @@ -1188,13 +1352,14 @@ class PesterOptions(QtWidgets.QDialog): self.userlinkscheck.setChecked(self.config.disableUserLinks()) self.userlinkscheck.setVisible(False) - # Will add ability to turn off groups later - #self.groupscheck = QtGui.QCheckBox("Use Groups", self) - #self.groupscheck.setChecked(self.config.useGroups()) + # self.groupscheck = QtGui.QCheckBox("Use Groups", self) + # self.groupscheck.setChecked(self.config.useGroups()) self.showemptycheck = QtWidgets.QCheckBox("Show Empty Groups", self) self.showemptycheck.setChecked(self.config.showEmptyGroups()) - self.showonlinenumbers = QtWidgets.QCheckBox("Show Number of Online Chums", self) + self.showonlinenumbers = QtWidgets.QCheckBox( + "Show Number of Online Chums", self + ) self.showonlinenumbers.setChecked(self.config.showOnlineNumbers()) sortLabel = QtWidgets.QLabel("Sort Chums") @@ -1230,19 +1395,19 @@ class PesterOptions(QtWidgets.QDialog): layout_5.addWidget(QtWidgets.QLabel("Minutes before Idle:")) layout_5.addWidget(self.idleBox) - #self.updateBox = QtWidgets.QComboBox(self) - #self.updateBox.addItem("Once a Day") - #self.updateBox.addItem("Once a Week") - #self.updateBox.addItem("Only on Start") - #self.updateBox.addItem("Never") - #check = self.config.checkForUpdates() - #if check >= 0 and check < self.updateBox.count(): + # self.updateBox = QtWidgets.QComboBox(self) + # self.updateBox.addItem("Once a Day") + # self.updateBox.addItem("Once a Week") + # self.updateBox.addItem("Only on Start") + # self.updateBox.addItem("Never") + # check = self.config.checkForUpdates() + # if check >= 0 and check < self.updateBox.count(): # self.updateBox.setCurrentIndex(check) layout_6 = QtWidgets.QHBoxLayout() - #layout_6.addWidget(QtWidgets.QLabel("Check for\nPesterchum Updates:")) - #layout_6.addWidget(self.updateBox) + # layout_6.addWidget(QtWidgets.QLabel("Check for\nPesterchum Updates:")) + # layout_6.addWidget(self.updateBox) - #if not ostools.isOSXLeopard(): + # if not ostools.isOSXLeopard(): # self.mspaCheck = QtWidgets.QCheckBox("Check for MSPA Updates", self) # self.mspaCheck.setChecked(self.config.checkMSPA()) @@ -1253,7 +1418,7 @@ class PesterOptions(QtWidgets.QDialog): avail_themes = self.config.availableThemes() self.themeBox = QtWidgets.QComboBox(self) - notheme = (theme.name not in avail_themes) + notheme = theme.name not in avail_themes for (i, t) in enumerate(avail_themes): self.themeBox.addItem(t) if (not notheme and t == theme.name) or (notheme and t == "pesterchum"): @@ -1292,7 +1457,7 @@ class PesterOptions(QtWidgets.QDialog): types = self.parent().tm.availableTypes() cur = self.parent().tm.currentType() self.notifyOptions.addItems(types) - for (i,t) in enumerate(types): + for (i, t) in enumerate(types): if t == cur: self.notifyOptions.setCurrentIndex(i) break @@ -1300,13 +1465,13 @@ class PesterOptions(QtWidgets.QDialog): layout_type = QtWidgets.QHBoxLayout() layout_type.addWidget(self.notifyTypeLabel) layout_type.addWidget(self.notifyOptions) - self.notifySigninCheck = QtWidgets.QCheckBox("Chum signs in", self) + self.notifySigninCheck = QtWidgets.QCheckBox("Chum signs in", self) if self.config.notifyOptions() & self.config.SIGNIN: self.notifySigninCheck.setChecked(True) - self.notifySignoutCheck = QtWidgets.QCheckBox("Chum signs out", self) + self.notifySignoutCheck = QtWidgets.QCheckBox("Chum signs out", self) if self.config.notifyOptions() & self.config.SIGNOUT: self.notifySignoutCheck.setChecked(True) - self.notifyNewMsgCheck = QtWidgets.QCheckBox("New messages", self) + self.notifyNewMsgCheck = QtWidgets.QCheckBox("New messages", self) if self.config.notifyOptions() & self.config.NEWMSG: self.notifyNewMsgCheck.setChecked(True) self.notifyNewConvoCheck = QtWidgets.QCheckBox("Only new conversations", self) @@ -1339,7 +1504,7 @@ class PesterOptions(QtWidgets.QDialog): layout_chumlist = QtWidgets.QVBoxLayout(widget) layout_chumlist.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_chumlist.addWidget(self.hideOffline) - #layout_chumlist.addWidget(self.groupscheck) + # layout_chumlist.addWidget(self.groupscheck) layout_chumlist.addWidget(self.showemptycheck) layout_chumlist.addWidget(self.showonlinenumbers) layout_chumlist.addLayout(layout_3) @@ -1358,9 +1523,9 @@ class PesterOptions(QtWidgets.QDialog): layout_chat.addWidget(animateLabel) layout_chat.addWidget(self.randomscheck) # Re-enable these when it's possible to disable User and Memo links - #layout_chat.addWidget(hr) - #layout_chat.addWidget(QtGui.QLabel("User and Memo Links")) - #layout_chat.addWidget(self.userlinkscheck) + # layout_chat.addWidget(hr) + # layout_chat.addWidget(QtGui.QLabel("User and Memo Links")) + # layout_chat.addWidget(self.userlinkscheck) self.pages.addWidget(widget) # Interface @@ -1387,14 +1552,14 @@ class PesterOptions(QtWidgets.QDialog): layout_doubleindent.addWidget(self.memopingcheck) layout_doubleindent.addWidget(self.namesoundcheck) layout_doubleindent.addWidget(self.editMentions) - layout_doubleindent.setContentsMargins(22,0,0,0) + layout_doubleindent.setContentsMargins(22, 0, 0, 0) layout_indent.addLayout(layout_doubleindent) - layout_indent.setContentsMargins(22,0,0,0) + layout_indent.setContentsMargins(22, 0, 0, 0) layout_sound.addLayout(layout_indent) layout_sound.addSpacing(15) mvol = QtWidgets.QLabel("Master Volume:", self) # If we can't set the volume, grey this out as well - #~mvol.setEnabled(parent.canSetVolume()) + # ~mvol.setEnabled(parent.canSetVolume()) # Normally we'd grey this out, but that presently makes things # rather unreadable # Later we can look into changing the color to a theme[] entry @@ -1410,13 +1575,13 @@ class PesterOptions(QtWidgets.QDialog): layout_notify.addWidget(self.notifycheck) layout_indent = QtWidgets.QVBoxLayout() layout_indent.addLayout(layout_type) - layout_indent.setContentsMargins(22,0,0,0) + layout_indent.setContentsMargins(22, 0, 0, 0) layout_indent.addWidget(self.notifySigninCheck) layout_indent.addWidget(self.notifySignoutCheck) layout_indent.addWidget(self.notifyNewMsgCheck) layout_doubleindent = QtWidgets.QVBoxLayout() layout_doubleindent.addWidget(self.notifyNewConvoCheck) - layout_doubleindent.setContentsMargins(22,0,0,0) + layout_doubleindent.setContentsMargins(22, 0, 0, 0) layout_indent.addLayout(layout_doubleindent) layout_indent.addWidget(self.notifyMentionsCheck) layout_indent.addWidget(self.editMentions2) @@ -1439,7 +1604,7 @@ class PesterOptions(QtWidgets.QDialog): layout_idle.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_idle.addLayout(layout_5) layout_idle.addLayout(layout_6) - #if not ostools.isOSXLeopard(): + # if not ostools.isOSXLeopard(): # layout_idle.addWidget(self.mspaCheck) self.pages.addWidget(widget) @@ -1462,7 +1627,7 @@ class PesterOptions(QtWidgets.QDialog): layout_connect.addWidget(self.autonickserv) layout_indent = QtWidgets.QVBoxLayout() layout_indent.addWidget(self.nickservpass) - layout_indent.setContentsMargins(22,0,0,0) + layout_indent.setContentsMargins(22, 0, 0, 0) layout_connect.addLayout(layout_indent) layout_connect.addWidget(QtWidgets.QLabel("Auto-Join Memos:")) layout_connect.addWidget(self.autojoinlist) @@ -1477,7 +1642,9 @@ class PesterOptions(QtWidgets.QDialog): widget = QtWidgets.QWidget() layout_advanced = QtWidgets.QVBoxLayout(widget) layout_advanced.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) - layout_advanced.addWidget(QtWidgets.QLabel("Current User Mode: %s" % parent.modes)) + layout_advanced.addWidget( + QtWidgets.QLabel("Current User Mode: %s" % parent.modes) + ) layout_advanced.addLayout(layout_change) self.pages.addWidget(widget) @@ -1521,7 +1688,7 @@ class PesterOptions(QtWidgets.QDialog): @QtCore.pyqtSlot() def addAutoJoin(self, mitem=None): - d = {"label": "Memo:", "inputname": "value" } + d = {"label": "Memo:", "inputname": "value"} if mitem is not None: d["value"] = str(mitem.text()) pdict = MultiTextDialog("ENTER MEMO", self, d).getText() @@ -1529,8 +1696,9 @@ class PesterOptions(QtWidgets.QDialog): return pdict["value"] = "#" + pdict["value"] if mitem is None: - items = self.autojoinlist.findItems(pdict["value"], - QtCore.Qt.MatchFlag.MatchFixedString) + items = self.autojoinlist.findItems( + pdict["value"], QtCore.Qt.MatchFlag.MatchFixedString + ) if len(items) == 0: self.autojoinlist.addItem(pdict["value"]) else: @@ -1553,6 +1721,7 @@ class PesterOptions(QtWidgets.QDialog): self.memosoundcheck.setEnabled(True) if self.memosoundcheck.isChecked(): self.memoSoundChange(1) + @QtCore.pyqtSlot(int) def memoSoundChange(self, state): if state == 0: @@ -1561,13 +1730,14 @@ class PesterOptions(QtWidgets.QDialog): else: self.memopingcheck.setEnabled(True) self.namesoundcheck.setEnabled(True) + @QtCore.pyqtSlot(int) def printValue(self, v): - self.currentVol.setText(str(v)+"%") + self.currentVol.setText(str(v) + "%") @QtCore.pyqtSlot() def openMentions(self): - if not hasattr(self, 'mentionmenu'): + if not hasattr(self, "mentionmenu"): self.mentionmenu = None if not self.mentionmenu: self.mentionmenu = PesterMentions(self.parent(), self.theme, self) @@ -1576,10 +1746,12 @@ class PesterOptions(QtWidgets.QDialog): self.mentionmenu.show() self.mentionmenu.raise_() self.mentionmenu.activateWindow() + @QtCore.pyqtSlot() def closeMentions(self): self.mentionmenu.close() self.mentionmenu = None + @QtCore.pyqtSlot() def updateMentions(self): m = [] @@ -1588,6 +1760,7 @@ class PesterOptions(QtWidgets.QDialog): self.parent().userprofile.setMentions(m) self.mentionmenu = None + class PesterUserlist(QtWidgets.QDialog): def __init__(self, config, theme, parent): QtWidgets.QDialog.__init__(self, parent) @@ -1599,18 +1772,22 @@ class PesterUserlist(QtWidgets.QDialog): self.resize(200, 600) self.searchbox = QtWidgets.QLineEdit(self) - #self.searchbox.setStyleSheet(theme["convo/input/style"]) # which style is better? + # self.searchbox.setStyleSheet(theme["convo/input/style"]) # which style is better? self.searchbox.setPlaceholderText("Search") - self.searchbox.textChanged['QString'].connect(self.updateUsers) + self.searchbox.textChanged["QString"].connect(self.updateUsers) self.label = QtWidgets.QLabel("USERLIST") self.userarea = RightClickList(self) self.userarea.setStyleSheet(self.theme["main/chums/style"]) self.userarea.optionsMenu = QtWidgets.QMenu(self) - self.addChumAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self) + self.addChumAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self + ) self.addChumAction.triggered.connect(self.addChumSlot) - self.pesterChumAction = QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self) + self.pesterChumAction = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/pester"], self + ) self.pesterChumAction.triggered.connect(self.pesterChumSlot) self.userarea.optionsMenu.addAction(self.addChumAction) self.userarea.optionsMenu.addAction(self.pesterChumAction) @@ -1629,10 +1806,13 @@ class PesterUserlist(QtWidgets.QDialog): self.mainwindow.namesUpdated.connect(self.updateUsers) - self.mainwindow.userPresentSignal['QString', 'QString', 'QString'].connect(self.updateUserPresent) + self.mainwindow.userPresentSignal["QString", "QString", "QString"].connect( + self.updateUserPresent + ) self.updateUsers() self.searchbox.setFocus() + @QtCore.pyqtSlot() def updateUsers(self): try: @@ -1642,13 +1822,19 @@ class PesterUserlist(QtWidgets.QDialog): return self.userarea.clear() for n in names: - if str(self.searchbox.text()) == "" or n.lower().find(str(self.searchbox.text()).lower()) != -1: + if ( + str(self.searchbox.text()) == "" + or n.lower().find(str(self.searchbox.text()).lower()) != -1 + ): # Strip channel membership prefixes - n = n.strip('~').strip('@').strip('+').strip('&').strip('%') + n = n.strip("~").strip("@").strip("+").strip("&").strip("%") item = QtWidgets.QListWidgetItem(n) - item.setForeground(QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"]))) + item.setForeground( + QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) + ) self.userarea.addItem(item) self.userarea.sortItems() + @QtCore.pyqtSlot(QString, QString, QString) def updateUserPresent(self, handle, channel, update): h = str(handle) @@ -1658,13 +1844,20 @@ class PesterUserlist(QtWidgets.QDialog): elif update == "left" and c == "#pesterchum": self.delUser(h) elif update == "join" and c == "#pesterchum": - if str(self.searchbox.text()) == "" or h.lower().find(str(self.searchbox.text()).lower()) != -1: + if ( + str(self.searchbox.text()) == "" + or h.lower().find(str(self.searchbox.text()).lower()) != -1 + ): self.addUser(h) + def addUser(self, name): item = QtWidgets.QListWidgetItem(name) - item.setForeground(QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"]))) + item.setForeground( + QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) + ) self.userarea.addItem(item) self.userarea.sortItems() + def delUser(self, name): matches = self.userarea.findItems(name, QtCore.Qt.MatchFlag.MatchExactly) for m in matches: @@ -1676,7 +1869,9 @@ class PesterUserlist(QtWidgets.QDialog): self.userarea.setStyleSheet(theme["main/chums/style"]) self.addChumAction.setText(theme["main/menus/rclickchumlist/addchum"]) for item in [self.userarea.item(i) for i in range(0, self.userarea.count())]: - item.setForeground(0, QtGui.QBrush(QtGui.QColor(theme["main/chums/userlistcolor"]))) + item.setForeground( + 0, QtGui.QBrush(QtGui.QColor(theme["main/chums/userlistcolor"])) + ) @QtCore.pyqtSlot() def addChumSlot(self): @@ -1684,6 +1879,7 @@ class PesterUserlist(QtWidgets.QDialog): if not cur: return self.addChum.emit(cur.text()) + @QtCore.pyqtSlot() def pesterChumSlot(self): cur = self.userarea.currentItem() @@ -1691,20 +1887,22 @@ class PesterUserlist(QtWidgets.QDialog): return self.pesterChum.emit(cur.text()) - addChum = QtCore.pyqtSignal('QString') - pesterChum = QtCore.pyqtSignal('QString') + addChum = QtCore.pyqtSignal("QString") + pesterChum = QtCore.pyqtSignal("QString") class MemoListItem(QtWidgets.QTreeWidgetItem): def __init__(self, channel, usercount): QtWidgets.QTreeWidgetItem.__init__(self, [channel, str(usercount)]) self.target = channel + def __lt__(self, other): column = self.treeWidget().sortColumn() if str(self.text(column)).isdigit() and str(other.text(column)).isdigit(): return int(self.text(column)) < int(other.text(column)) return self.text(column) < other.text(column) + class PesterMemoList(QtWidgets.QDialog): def __init__(self, parent, channel=""): QtWidgets.QDialog.__init__(self, parent) @@ -1716,17 +1914,21 @@ class PesterMemoList(QtWidgets.QDialog): self.label = QtWidgets.QLabel("MEMOS") self.channelarea = RightClickTree(self) - self.channelarea.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) + self.channelarea.setSelectionMode( + QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection + ) self.channelarea.setStyleSheet(self.theme["main/chums/style"]) self.channelarea.optionsMenu = QtWidgets.QMenu(self) self.channelarea.setColumnCount(2) self.channelarea.setHeaderLabels(["Memo", "Users"]) self.channelarea.setIndentation(0) - self.channelarea.setColumnWidth(0,200) - self.channelarea.setColumnWidth(1,10) + self.channelarea.setColumnWidth(0, 200) + self.channelarea.setColumnWidth(1, 10) self.channelarea.setSortingEnabled(True) self.channelarea.sortByColumn(0, QtCore.Qt.SortOrder.AscendingOrder) - self.channelarea.itemDoubleClicked[QtWidgets.QTreeWidgetItem, int].connect(self.AcceptSelection) + self.channelarea.itemDoubleClicked[QtWidgets.QTreeWidgetItem, int].connect( + self.AcceptSelection + ) self.orjoinlabel = QtWidgets.QLabel("OR MAKE A NEW MEMO:") self.newmemo = QtWidgets.QLineEdit(channel, self) @@ -1746,7 +1948,7 @@ class PesterMemoList(QtWidgets.QDialog): layout_ok.addWidget(self.cancel) layout_ok.addWidget(self.join) - layout_left = QtWidgets.QVBoxLayout() + layout_left = QtWidgets.QVBoxLayout() layout_right = QtWidgets.QVBoxLayout() layout_right.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) layout_0 = QtWidgets.QVBoxLayout() @@ -1778,9 +1980,13 @@ class PesterMemoList(QtWidgets.QDialog): def updateChannels(self, channels): for c in channels: - item = MemoListItem(c[0][1:],c[1]) - item.setForeground(0, QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"]))) - item.setForeground(1, QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"]))) + item = MemoListItem(c[0][1:], c[1]) + item.setForeground( + 0, QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) + ) + item.setForeground( + 1, QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])) + ) item.setIcon(0, QtGui.QIcon(self.theme["memos/memoicon"])) self.channelarea.addTopLevelItem(item) @@ -1788,7 +1994,9 @@ class PesterMemoList(QtWidgets.QDialog): self.theme = theme self.setStyleSheet(theme["main/defaultwindow/style"]) for item in [self.userarea.item(i) for i in range(0, self.channelarea.count())]: - item.setForeground(0, QtGui.QBrush(QtGui.QColor(theme["main/chums/userlistcolor"]))) + item.setForeground( + 0, QtGui.QBrush(QtGui.QColor(theme["main/chums/userlistcolor"])) + ) item.setIcon(QtGui.QIcon(theme["memos/memoicon"])) @QtCore.pyqtSlot() @@ -1805,18 +2013,20 @@ class LoadingScreen(QtWidgets.QDialog): tryAgain = QtCore.pyqtSignal() def __init__(self, parent=None): - QtWidgets.QDialog.__init__(self, - parent, - QtCore.Qt.WindowType.CustomizeWindowHint - | QtCore.Qt.WindowType.FramelessWindowHint) + QtWidgets.QDialog.__init__( + self, + parent, + QtCore.Qt.WindowType.CustomizeWindowHint + | QtCore.Qt.WindowType.FramelessWindowHint, + ) 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.setWindowModality(QtCore.Qt.WindowModality.NonModal) # useless + # self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose) # useless self.loadinglabel = QtWidgets.QLabel("CONN3CT1NG", self) - #self.loadinglabel.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links - #self.loadinglabel.setWordWrap(True) # Unusable because of QT clipping bug (QTBUG-92381) + # self.loadinglabel.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links + # self.loadinglabel.setWordWrap(True) # Unusable because of QT clipping bug (QTBUG-92381) self.cancel = QtWidgets.QPushButton("QU1T >:?", self) self.ok = QtWidgets.QPushButton("R3CONN3CT >:]", self) # Help reduce the number of accidental Pesterchum closures... :| @@ -1824,7 +2034,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.finished.connect(self.finishedEvent) self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.loadinglabel) @@ -1838,8 +2048,8 @@ class LoadingScreen(QtWidgets.QDialog): self.ok.setDefault(True) self.ok.setFocus() self.timer = None - - #def finishedEvent(self, result): + + # def finishedEvent(self, result): # self.close() def hideReconnect(self, safe=True): @@ -1859,6 +2069,7 @@ class LoadingScreen(QtWidgets.QDialog): def enableQuit(self): self.cancel.setEnabled(True) + class AboutPesterchum(QtWidgets.QDialog): def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) @@ -1866,26 +2077,28 @@ class AboutPesterchum(QtWidgets.QDialog): self.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) self.title = QtWidgets.QLabel("P3ST3RCHUM %s" % (_pcVersion)) - self.credits = QtWidgets.QLabel("Programming by:" - "\n illuminatedwax (ghostDunk)" - "\n Kiooeht (evacipatedBox)" - "\n Lexi (lexicalNuance)" - "\n oakwhiz" - "\n alGore" - "\n Cerxi (binaryCabalist)" - "\n Arcane (arcaneAgilmente)" - "\n karxi (Midna)" - "\n Shou/Dpeta 🐱" - "\n" - "\nArt by:" - "\n Grimlive (aquaMarinist)" - "\n Cerxi (binaryCabalist)" - "\n cubicSimulation" - "\n" - "\nSpecial Thanks:" - "\n ABT" - "\n gamblingGenocider" - "\n Eco-Mono") + self.credits = QtWidgets.QLabel( + "Programming by:" + "\n illuminatedwax (ghostDunk)" + "\n Kiooeht (evacipatedBox)" + "\n Lexi (lexicalNuance)" + "\n oakwhiz" + "\n alGore" + "\n Cerxi (binaryCabalist)" + "\n Arcane (arcaneAgilmente)" + "\n karxi (Midna)" + "\n Shou/Dpeta 🐱" + "\n" + "\nArt by:" + "\n Grimlive (aquaMarinist)" + "\n Cerxi (binaryCabalist)" + "\n cubicSimulation" + "\n" + "\nSpecial Thanks:" + "\n ABT" + "\n gamblingGenocider" + "\n Eco-Mono" + ) self.ok = QtWidgets.QPushButton("OK", self) self.ok.clicked.connect(self.reject) @@ -1897,6 +2110,7 @@ class AboutPesterchum(QtWidgets.QDialog): self.setLayout(layout_0) + class UpdatePesterchum(QtWidgets.QDialog): def __init__(self, ver, url, parent=None): QtWidgets.QDialog.__init__(self, parent) @@ -1924,6 +2138,7 @@ class UpdatePesterchum(QtWidgets.QDialog): self.setLayout(layout_0) + class AddChumDialog(QtWidgets.QDialog): def __init__(self, avail_groups, parent=None): QtWidgets.QDialog.__init__(self, parent) diff --git a/mispeller.py b/mispeller.py index 5b1cb28..5e38b2f 100644 --- a/mispeller.py +++ b/mispeller.py @@ -1,29 +1,55 @@ import random -kbloc = [[x for x in "1234567890-="], - [x for x in "qwertyuiop[]"], - [x for x in "asdfghjkl:;'"], - [x for x in "zxcvbnm,.>/?"]] +kbloc = [ + [x for x in "1234567890-="], + [x for x in "qwertyuiop[]"], + [x for x in "asdfghjkl:;'"], + [x for x in "zxcvbnm,.>/?"], +] kbdict = {} for (i, l) in enumerate(kbloc): for (j, k) in enumerate(l): kbdict[k] = (i, j) -sounddict = {"a": "e", "b": "d", "c": "k", "d": "g", "e": "eh", - "f": "ph", "g": "j", "h": "h", "i": "ai", "j": "ge", - "k": "c", "l": "ll", "m": "n", "n": "m", "o": "oa", - "p": "b", "q": "kw", "r": "ar", "s": "ss", "t": "d", - "u": "you", "v": "w", "w": "wn", "x": "cks", "y": "uy", "z": "s"} - +sounddict = { + "a": "e", + "b": "d", + "c": "k", + "d": "g", + "e": "eh", + "f": "ph", + "g": "j", + "h": "h", + "i": "ai", + "j": "ge", + "k": "c", + "l": "ll", + "m": "n", + "n": "m", + "o": "oa", + "p": "b", + "q": "kw", + "r": "ar", + "s": "ss", + "t": "d", + "u": "you", + "v": "w", + "w": "wn", + "x": "cks", + "y": "uy", + "z": "s", +} + def mispeller(word): if len(word) <= 6: num = 1 else: - num = random.choice([1,2]) + num = random.choice([1, 2]) wordseq = list(range(0, len(word))) random.shuffle(wordseq) letters = wordseq[0:num] + def mistype(string, i): l = string[i] if l not in kbdict: @@ -31,30 +57,42 @@ def mispeller(word): lpos = kbdict[l] newpos = lpos while newpos == lpos: - newpos = ((lpos[0] + random.choice([-1, 0, 1])) % len(kbloc), - (lpos[1] + random.choice([-1,0,1])) % len(kbloc[0])) - string = string[0:i]+kbloc[newpos[0]][newpos[1]]+string[i+1:] + newpos = ( + (lpos[0] + random.choice([-1, 0, 1])) % len(kbloc), + (lpos[1] + random.choice([-1, 0, 1])) % len(kbloc[0]), + ) + string = string[0:i] + kbloc[newpos[0]][newpos[1]] + string[i + 1 :] return string + def transpose(string, i): - j = (i + random.choice([-1,1])) % len(string) + j = (i + random.choice([-1, 1])) % len(string) l = [c for c in string] l[i], l[j] = l[j], l[i] return "".join(l) + def randomletter(string, i): - string = string[0:i+1]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:] + string = ( + string[0 : i + 1] + + random.choice("abcdefghijklmnopqrstuvwxyz") + + string[i + 1 :] + ) return string + def randomreplace(string, i): - string = string[0:i]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:] + string = ( + string[0:i] + random.choice("abcdefghijklmnopqrstuvwxyz") + string[i + 1 :] + ) return string + def soundalike(string, i): try: c = sounddict[string[i]] except: return string - string = string[0:i]+c+string[i+1:] + string = string[0:i] + c + string[i + 1 :] return string - func = random.choice([mistype, transpose, randomletter, randomreplace, - soundalike]) + + func = random.choice([mistype, transpose, randomletter, randomreplace, soundalike]) for i in letters: word = func(word, i) return word diff --git a/mood.py b/mood.py index e5c05dc..e6de84d 100644 --- a/mood.py +++ b/mood.py @@ -6,28 +6,76 @@ except ImportError: from generic import PesterIcon + class Mood(object): - moods = ["chummy", "rancorous", "offline", "pleasant", "distraught", - "pranky", "smooth", "ecstatic", "relaxed", "discontent", - "devious", "sleek", "detestful", "mirthful", "manipulative", - "vigorous", "perky", "acceptant", "protective", "mystified", - "amazed", "insolent", "bemused" ] + moods = [ + "chummy", + "rancorous", + "offline", + "pleasant", + "distraught", + "pranky", + "smooth", + "ecstatic", + "relaxed", + "discontent", + "devious", + "sleek", + "detestful", + "mirthful", + "manipulative", + "vigorous", + "perky", + "acceptant", + "protective", + "mystified", + "amazed", + "insolent", + "bemused", + ] moodcats = ["chums", "trolls", "other"] - revmoodcats = {'discontent': 'trolls', 'insolent': 'chums', 'rancorous': 'chums', 'sleek': 'trolls', 'bemused': 'chums', 'mystified': 'chums', 'pranky': 'chums', 'distraught': 'chums', 'offline': 'chums', 'chummy': 'chums', 'protective': 'other', 'vigorous': 'trolls', 'ecstatic': 'trolls', 'relaxed': 'trolls', 'pleasant': 'chums', 'manipulative': 'trolls', 'detestful': 'trolls', 'smooth': 'chums', 'mirthful': 'trolls', 'acceptant': 'trolls', 'perky': 'trolls', 'devious': 'trolls', 'amazed': 'chums'} + revmoodcats = { + "discontent": "trolls", + "insolent": "chums", + "rancorous": "chums", + "sleek": "trolls", + "bemused": "chums", + "mystified": "chums", + "pranky": "chums", + "distraught": "chums", + "offline": "chums", + "chummy": "chums", + "protective": "other", + "vigorous": "trolls", + "ecstatic": "trolls", + "relaxed": "trolls", + "pleasant": "chums", + "manipulative": "trolls", + "detestful": "trolls", + "smooth": "chums", + "mirthful": "trolls", + "acceptant": "trolls", + "perky": "trolls", + "devious": "trolls", + "amazed": "chums", + } def __init__(self, mood): if type(mood) is int: self.mood = mood else: self.mood = self.moods.index(mood) + def value(self): return self.mood + def name(self): try: name = self.moods[self.mood] except IndexError: name = "chummy" return name + def icon(self, theme): try: f = theme["main/chums/moods"][self.name()]["icon"] @@ -35,15 +83,18 @@ class Mood(object): return PesterIcon(theme["main/chums/moods/chummy/icon"]) return PesterIcon(f) + class PesterMoodAction(QtCore.QObject): def __init__(self, m, func): QtCore.QObject.__init__(self) self.mood = m self.func = func + @QtCore.pyqtSlot() def updateMood(self): self.func(self.mood) + class PesterMoodHandler(QtCore.QObject): def __init__(self, parent, *buttons): QtCore.QObject.__init__(self) @@ -55,13 +106,16 @@ class PesterMoodHandler(QtCore.QObject): b.setSelected(True) b.clicked.connect(b.updateMood) b.moodUpdated[int].connect(self.updateMood) + def removeButtons(self): for b in list(self.buttons.values()): b.close() + def showButtons(self): for b in list(self.buttons.values()): b.show() b.raise_() + @QtCore.pyqtSlot(int) def updateMood(self, m): # update MY mood @@ -81,12 +135,15 @@ class PesterMoodHandler(QtCore.QObject): self.mainwindow.userprofile.setLastMood(newmood) if self.mainwindow.currentMoodIcon: moodicon = newmood.icon(self.mainwindow.theme) - self.mainwindow.currentMoodIcon.setPixmap(moodicon.pixmap(moodicon.realsize())) + self.mainwindow.currentMoodIcon.setPixmap( + moodicon.pixmap(moodicon.realsize()) + ) if oldmood.name() != newmood.name(): for c in list(self.mainwindow.convos.values()): c.myUpdateMood(newmood) self.mainwindow.moodUpdated.emit() + class PesterMoodButton(QtWidgets.QPushButton): def __init__(self, parent, **options): icon = PesterIcon(options["icon"]) @@ -100,13 +157,16 @@ class PesterMoodButton(QtWidgets.QPushButton): self.setStyleSheet(self.unselectedSheet) self.mainwindow = parent self.mood = Mood(options["mood"]) + def setSelected(self, selected): if selected: self.setStyleSheet(self.selectedSheet) else: self.setStyleSheet(self.unselectedSheet) + @QtCore.pyqtSlot() def updateMood(self): # updates OUR mood self.moodUpdated.emit(self.mood.value()) + moodUpdated = QtCore.pyqtSignal(int) diff --git a/nickservmsgs.py b/nickservmsgs.py index f03709f..94ed4ab 100644 --- a/nickservmsgs.py +++ b/nickservmsgs.py @@ -1,16 +1,13 @@ # Hardcoded messages that NickServ sends and what to display to the user instead messages = { - "Your nick isn't registered.": - "", # display the same - "Password accepted - you are now recognized.": - "", # display the same - "If you do not change within one minute, I will change your nick.": - "You have 1 minute to identify.", - "If you do not change within 20 seconds, I will change your nick.": - "You have 20 seconds to identify." + "Your nick isn't registered.": "", # display the same + "Password accepted - you are now recognized.": "", # display the same + "If you do not change within one minute, I will change your nick.": "You have 1 minute to identify.", + "If you do not change within 20 seconds, I will change your nick.": "You have 20 seconds to identify.", } + def translate(msg): if msg in messages: if messages[msg] == "": diff --git a/ostools.py b/ostools.py index 1a2e5b3..73a3f52 100644 --- a/ostools.py +++ b/ostools.py @@ -1,37 +1,44 @@ import os import sys import platform - + try: from PyQt6.QtCore import QStandardPaths except ImportError: print("PyQt5 fallback (ostools.py)") from PyQt5.QtCore import QStandardPaths + def isOSX(): return sys.platform == "darwin" + def isWin32(): return sys.platform == "win32" + def isLinux(): return sys.platform.startswith("linux") + def isOSXBundle(): - return isOSX() and (os.path.abspath('.').find(".app") != -1) + return isOSX() and (os.path.abspath(".").find(".app") != -1) + def isOSXLeopard(): return isOSX() and platform.mac_ver()[0].startswith("10.5") + def osVer(): if isWin32(): return " ".join(platform.win32_ver()) elif isOSX(): - ver = platform.mac_ver(); + ver = platform.mac_ver() return " ".join((ver[0], " (", ver[2], ")")) elif isLinux(): return " ".join(platform.linux_distribution()) + def validateDataDir(): """Checks if data directory is present""" # Define paths @@ -42,7 +49,7 @@ def validateDataDir(): errorlogs = os.path.join(datadir, "errorlogs") backup = os.path.join(datadir, "backup") js_pchum = os.path.join(datadir, "pesterchum.js") - + dirs = [datadir, profile, quirks, logs, errorlogs, backup] for d in dirs: if (os.path.isdir(d) == False) or (os.path.exists(d) == False): @@ -50,20 +57,36 @@ def validateDataDir(): # pesterchum.js if not os.path.exists(js_pchum): - with open(js_pchum, 'w') as f: + with open(js_pchum, "w") as f: f.write("{}") + def getDataDir(): # Temporary fix for non-ascii usernames # If username has non-ascii characters, just store userdata # in the Pesterchum install directory (like before) try: if isOSX(): - return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation), "Pesterchum/") + return os.path.join( + QStandardPaths.writableLocation( + QStandardPaths.StandardLocation.AppLocalDataLocation + ), + "Pesterchum/", + ) elif isLinux(): - return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation), ".pesterchum/") + return os.path.join( + QStandardPaths.writableLocation( + QStandardPaths.StandardLocation.HomeLocation + ), + ".pesterchum/", + ) else: - return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation), "pesterchum/") + return os.path.join( + QStandardPaths.writableLocation( + QStandardPaths.StandardLocation.AppLocalDataLocation + ), + "pesterchum/", + ) except UnicodeDecodeError as e: print(e) - return '' + return "" diff --git a/oyoyo/client.py b/oyoyo/client.py index d3406bb..f2e7387 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -16,7 +16,8 @@ # THE SOFTWARE. import logging -PchumLog = logging.getLogger('pchumLogger') + +PchumLog = logging.getLogger("pchumLogger") import logging import socket @@ -29,32 +30,33 @@ from oyoyo.parse import parse_raw_irc_command from oyoyo import helpers from oyoyo.cmdhandler import CommandError + class IRCClientError(Exception): pass class IRCClient: - """ IRC Client class. This handles one connection to a server. + """IRC Client class. This handles one connection to a server. This can be used either with or without IRCApp ( see connect() docs ) """ def __init__(self, cmd_handler, **kwargs): - """ the first argument should be an object with attributes/methods named - as the irc commands. You may subclass from one of the classes in - oyoyo.cmdhandler for convenience but it is not required. The - methods should have arguments (prefix, args). prefix is + """the first argument should be an object with attributes/methods named + as the irc commands. You may subclass from one of the classes in + oyoyo.cmdhandler for convenience but it is not required. The + methods should have arguments (prefix, args). prefix is normally the sender of the command. args is a list of arguments. - Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler, - this class provides defaults for callbacks that are required for + Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler, + this class provides defaults for callbacks that are required for normal IRC operation. all other arguments should be keyword arguments. The most commonly used will be nick, host and port. You can also specify an "on connect" callback. ( check the source for others ) - Warning: By default this class will not block on socket operations, this + Warning: By default this class will not block on socket operations, this means if you use a plain while loop your app will consume 100% cpu. - To enable blocking pass blocking=True. + To enable blocking pass blocking=True. >>> class My_Handler(DefaultCommandHandler): ... def privmsg(self, prefix, command, args): @@ -76,7 +78,7 @@ class IRCClient: """ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - + self.nick = None self.realname = None self.username = None @@ -93,9 +95,9 @@ class IRCClient: self._end = False def send(self, *args, **kwargs): - """ send a message to the connected server. all arguments are joined - with a space for convenience, for example the following are identical - + """send a message to the connected server. all arguments are joined + with a space for convenience, for example the following are identical + >>> cli.send("JOIN %s" % some_room) >>> cli.send("JOIN", some_room) @@ -104,24 +106,29 @@ class IRCClient: the 'encoding' keyword argument (default 'utf8'). In python 3, all args must be of type str or bytes, *BUT* if they are str they will be converted to bytes with the encoding specified by the - 'encoding' keyword argument (default 'utf8'). + 'encoding' keyword argument (default 'utf8'). """ if self._end == True: return # Convert all args to bytes if not already - encoding = kwargs.get('encoding') or 'utf8' + encoding = kwargs.get("encoding") or "utf8" bargs = [] for arg in args: if isinstance(arg, str): bargs.append(bytes(arg, encoding)) elif isinstance(arg, bytes): bargs.append(arg) - elif type(arg).__name__ == 'unicode': + elif type(arg).__name__ == "unicode": bargs.append(arg.encode(encoding)) else: - PchumLog.warning('Refusing to send one of the args from provided: %s'% repr([(type(arg), arg) for arg in args])) - raise IRCClientError('Refusing to send one of the args from provided: %s' - % repr([(type(arg), arg) for arg in args])) + PchumLog.warning( + "Refusing to send one of the args from provided: %s" + % repr([(type(arg), arg) for arg in args]) + ) + raise IRCClientError( + "Refusing to send one of the args from provided: %s" + % repr([(type(arg), arg) for arg in args]) + ) msg = bytes(" ", "UTF-8").join(bargs) PchumLog.info('---> send "%s"' % msg) @@ -135,7 +142,9 @@ class IRCClient: self._end = True break try: - ready_to_read, ready_to_write, in_error = select.select([], [self.socket], []) + ready_to_read, ready_to_write, in_error = select.select( + [], [self.socket], [] + ) for x in ready_to_write: x.sendall(msg + bytes("\r\n", "UTF-8")) break @@ -157,35 +166,34 @@ class IRCClient: # socket.timeout is deprecated in 3.10 PchumLog.warning("TimeoutError in on send, " + str(e)) raise socket.timeout - except (OSError, - IndexError, - ValueError, - Exception) as e: + except (OSError, IndexError, ValueError, Exception) as e: PchumLog.warning("Unkown error on send, " + str(e)) if tries >= 9: raise e tries += 1 PchumLog.warning("Retrying send. (attempt %s)" % str(tries)) time.sleep(0.1) - - PchumLog.debug("ready_to_write (len %s): " % str(len(ready_to_write)) + str(ready_to_write)) + + PchumLog.debug( + "ready_to_write (len %s): " % str(len(ready_to_write)) + + str(ready_to_write) + ) except Exception as se: PchumLog.warning("Send Exception %s" % str(se)) try: if not self.blocking and se.errno == 11: pass else: - #raise se + # raise se self._end = True # This ok? except AttributeError: - #raise se + # raise se self._end = True # This ok? def connect(self, verify_hostname=True): - """ initiates the connection to the server set in self.host:self.port - """ - PchumLog.info('connecting to %s:%s' % (self.host, self.port)) - + """initiates the connection to the server set in self.host:self.port""" + PchumLog.info("connecting to %s:%s" % (self.host, self.port)) + if self.ssl == True: context = ssl.create_default_context() if verify_hostname == False: @@ -193,9 +201,9 @@ class IRCClient: context.verify_mode = ssl.CERT_NONE 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) + self.socket = context.wrap_socket( + bare_sock, server_hostname=self.host, do_handshake_on_connect=False + ) while True: try: self.socket.do_handshake() @@ -208,8 +216,8 @@ class IRCClient: # Disconnect for now self.socket.close() bare_sock.close() - raise e - + raise e + PchumLog.info("secure sockets version is %s" % self.socket.version()) else: @@ -224,7 +232,7 @@ class IRCClient: elif self.blocking: self.socket.setblocking(True) - #try: + # try: # self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # if hasattr(socket, "TCP_KEEPIDLE"): # self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1) @@ -232,16 +240,16 @@ class IRCClient: # self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1) # if hasattr(socket, "TCP_KEEPCNT"): # self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 1) - #except Exception as e: + # except Exception as e: # print(e) - + helpers.nick(self, self.nick) helpers.user(self, self.username, self.realname) if self.connect_cb: self.connect_cb(self) - + def conn(self): - """returns a generator object. """ + """returns a generator object.""" try: buffer = bytes() while not self._end: @@ -256,7 +264,9 @@ class IRCClient: self._end = True break try: - ready_to_read, ready_to_write, in_error = select.select([self.socket], [], []) + ready_to_read, ready_to_write, in_error = select.select( + [self.socket], [], [] + ) for x in ready_to_read: buffer += x.recv(1024) break @@ -278,17 +288,16 @@ class IRCClient: # socket.timeout is deprecated in 3.10 PchumLog.warning("TimeoutError in on send, " + str(e)) raise socket.timeout - except (OSError, - IndexError, - ValueError, - Exception) as e: + except (OSError, IndexError, ValueError, Exception) as e: PchumLog.debug("Miscellaneous exception in conn, " + str(e)) if tries >= 9: raise e tries += 1 - PchumLog.debug("Possibly retrying recv. (attempt %s)" % str(tries)) + PchumLog.debug( + "Possibly retrying recv. (attempt %s)" % str(tries) + ) time.sleep(0.1) - + except socket.timeout as e: PchumLog.warning("timeout in client.py, " + str(e)) if self._end: @@ -303,7 +312,7 @@ class IRCClient: try: # a little dance of compatibility to get the errno errno = e.errno except AttributeError: - errno = e[0] + errno = e[0] if not self.blocking and errno == 11: pass else: @@ -322,7 +331,7 @@ class IRCClient: for el in data: tags, prefix, command, args = parse_raw_irc_command(el) - #print(tags, prefix, command, args) + # print(tags, prefix, command, args) try: # Only need tags with tagmsg if command.upper() == "TAGMSG": @@ -339,7 +348,7 @@ class IRCClient: except (OSError, ssl.SSLEOFError) as se: PchumLog.debug("problem: %s" % (str(se))) if self.socket: - PchumLog.info('error: closing socket') + PchumLog.info("error: closing socket") self.socket.close() raise se except Exception as e: @@ -347,27 +356,33 @@ class IRCClient: raise e else: PchumLog.debug("ending while, end is %s" % self._end) - if self.socket: - PchumLog.info('finished: closing socket') + if self.socket: + PchumLog.info("finished: closing socket") self.socket.close() yield False + def close(self): # with extreme prejudice if self.socket: - PchumLog.info('shutdown socket') - #print("shutdown socket") + PchumLog.info("shutdown socket") + # print("shutdown socket") self._end = True try: self.socket.shutdown(socket.SHUT_RDWR) except OSError as e: - PchumLog.debug("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.debug("Error while closing socket, already broken? %s" % str(e)) - + PchumLog.debug( + "Error while closing socket, already broken? %s" % str(e) + ) + + class IRCApp: - """ This class manages several IRCClient instances without the use of threads. + """This class manages several IRCClient instances without the use of threads. (Non-threaded) Timer functionality is also included. """ @@ -384,27 +399,27 @@ class IRCApp: self.sleep_time = 0.5 def addClient(self, client, autoreconnect=False): - """ add a client object to the application. setting autoreconnect + """add a client object to the application. setting autoreconnect to true will mean the application will attempt to reconnect the client - after every disconnect. you can also set autoreconnect to a number + after every disconnect. you can also set autoreconnect to a number to specify how many reconnects should happen. warning: if you add a client that has blocking set to true, - timers will no longer function properly """ - PchumLog.info('added client %s (ar=%s)' % (client, autoreconnect)) + timers will no longer function properly""" + PchumLog.info("added client %s (ar=%s)" % (client, autoreconnect)) self._clients[client] = self._ClientDesc(autoreconnect=autoreconnect) def addTimer(self, seconds, cb): - """ add a timed callback. accuracy is not specified, you can only + """add a timed callback. accuracy is not specified, you can only garuntee the callback will be called after seconds has passed. ( the only advantage to these timers is they dont use threads ) """ assert callable(cb) - PchumLog.info('added timer to call %s in %ss' % (cb, seconds)) + PchumLog.info("added timer to call %s in %ss" % (cb, seconds)) self._timers.append((time.time() + seconds, cb)) def run(self): - """ run the application. this will block until stop() is called """ + """run the application. this will block until stop() is called""" # TODO: convert this to use generators too? self.running = True while self.running: @@ -413,42 +428,38 @@ class IRCApp: for client, clientdesc in self._clients.items(): if clientdesc.con is None: clientdesc.con = client.connect() - + try: next(clientdesc.con) except Exception as e: - PchumLog.error('client error %s' % str(e)) + PchumLog.error("client error %s" % str(e)) PchumLog.error(traceback.format_exc()) if clientdesc.autoreconnect: - clientdesc.con = None + clientdesc.con = None if isinstance(clientdesc.autoreconnect, (int, float)): clientdesc.autoreconnect -= 1 found_one_alive = True else: - clientdesc.con = False + clientdesc.con = False else: found_one_alive = True - + if not found_one_alive: - PchumLog.info('nothing left alive... quiting') - self.stop() + PchumLog.info("nothing left alive... quiting") + self.stop() now = time.time() timers = self._timers[:] self._timers = [] for target_time, cb in timers: if now > target_time: - PchumLog.info('calling timer cb %s' % cb) + PchumLog.info("calling timer cb %s" % cb) cb() - else: + else: self._timers.append((target_time, cb)) time.sleep(self.sleep_time) def stop(self): - """ stop the application """ + """stop the application""" self.running = False - - - - diff --git a/oyoyo/cmdhandler.py b/oyoyo/cmdhandler.py index bb3526d..3ead782 100644 --- a/oyoyo/cmdhandler.py +++ b/oyoyo/cmdhandler.py @@ -20,10 +20,11 @@ import inspect from oyoyo import helpers from oyoyo.parse import parse_nick -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + def protected(func): - """ decorator to protect functions from being called """ + """decorator to protect functions from being called""" func.protected = True return func @@ -32,17 +33,19 @@ class CommandError(Exception): def __init__(self, cmd): self.cmd = cmd + class NoSuchCommandError(CommandError): def __str__(self): return 'No such command "%s"' % ".".join(self.cmd) + class ProtectedCommandError(CommandError): def __str__(self): return 'Command "%s" is protected' % ".".join(self.cmd) - + class CommandHandler(object): - """ The most basic CommandHandler """ + """The most basic CommandHandler""" def __init__(self, client): self.client = client @@ -59,13 +62,13 @@ class CommandHandler(object): ["command", "sub", "func"]. """ if isinstance(in_command_parts, (str, bytes)): - in_command_parts = in_command_parts.split('.') + in_command_parts = in_command_parts.split(".") command_parts = in_command_parts[:] p = self while command_parts: cmd = command_parts.pop(0) - if cmd.startswith('_'): + if cmd.startswith("_"): raise ProtectedCommandError(in_command_parts) try: @@ -73,7 +76,7 @@ class CommandHandler(object): except AttributeError: raise NoSuchCommandError(in_command_parts) - if hasattr(f, 'protected'): + if hasattr(f, "protected"): raise ProtectedCommandError(in_command_parts) if isinstance(f, CommandHandler) and command_parts: @@ -84,10 +87,10 @@ class CommandHandler(object): @protected def run(self, command, *args): - """ finds and runs a command """ - arguments_str = '' + """finds and runs a command""" + arguments_str = "" for x in args: - arguments_str += str(x) + ' ' + arguments_str += str(x) + " " PchumLog.debug("processCommand %s(%s)" % (command, arguments_str.strip())) try: @@ -97,14 +100,17 @@ class CommandHandler(object): self.__unhandled__(command, *args) return - PchumLog.debug('f %s' % f) + PchumLog.debug("f %s" % f) try: f(*args) except TypeError as e: - PchumLog.info("Failed to pass command, did the server pass an unsupported paramater? " + str(e)) + PchumLog.info( + "Failed to pass command, did the server pass an unsupported paramater? " + + str(e) + ) except Exception as e: - #logging.info("Failed to pass command, %s" % str(e)) + # logging.info("Failed to pass command, %s" % str(e)) PchumLog.exception("Failed to pass command") @protected @@ -112,46 +118,52 @@ class CommandHandler(object): """The default handler for commands. Override this method to apply custom behavior (example, printing) unhandled commands. """ - PchumLog.debug('unhandled command %s(%s)' % (cmd, args)) + PchumLog.debug("unhandled command %s(%s)" % (cmd, args)) class DefaultCommandHandler(CommandHandler): - """ CommandHandler that provides methods for the normal operation of IRC. + """CommandHandler that provides methods for the normal operation of IRC. If you want your bot to properly respond to pings, etc, you should subclass this. """ def ping(self, prefix, server): - self.client.send('PONG', server) + self.client.send("PONG", server) class DefaultBotCommandHandler(CommandHandler): - """ default command handler for bots. methods/attributes are made - available as commands """ + """default command handler for bots. methods/attributes are made + available as commands""" @protected def getVisibleCommands(self, obj=None): - test = (lambda x: isinstance(x, CommandHandler) or \ - inspect.ismethod(x) or inspect.isfunction(x)) - members = inspect.getmembers(obj or self, test) - return [m for m, _ in members - if (not m.startswith('_') and - not hasattr(getattr(obj, m), 'protected'))] + test = ( + lambda x: isinstance(x, CommandHandler) + or inspect.ismethod(x) + or inspect.isfunction(x) + ) + members = inspect.getmembers(obj or self, test) + return [ + m + for m, _ in members + if (not m.startswith("_") and not hasattr(getattr(obj, m), "protected")) + ] def help(self, sender, dest, arg=None): """list all available commands or get help on a specific command""" - PchumLog.info('help sender=%s dest=%s arg=%s' % (sender, dest, arg)) + PchumLog.info("help sender=%s dest=%s arg=%s" % (sender, dest, arg)) if not arg: commands = self.getVisibleCommands() commands.sort() - helpers.msg(self.client, dest, - "available commands: %s" % " ".join(commands)) + helpers.msg( + self.client, dest, "available commands: %s" % " ".join(commands) + ) else: try: f = self.get(arg) except CommandError as e: helpers.msg(self.client, dest, str(e)) return - + doc = f.__doc__.strip() if f.__doc__ else "No help available" if not inspect.ismethod(f): @@ -159,11 +171,11 @@ class DefaultBotCommandHandler(CommandHandler): if subcommands: doc += " [sub commands: %s]" % " ".join(subcommands) - helpers.msg(self.client, dest, "%s: %s" % (arg, doc)) + helpers.msg(self.client, dest, "%s: %s" % (arg, doc)) class BotCommandHandler(DefaultCommandHandler): - """ complete command handler for bots """ + """complete command handler for bots""" def __init__(self, client, command_handler): DefaultCommandHandler.__init__(self, client) @@ -174,22 +186,22 @@ class BotCommandHandler(DefaultCommandHandler): @protected def tryBotCommand(self, prefix, dest, msg): - """ tests a command to see if its a command for the bot, returns True + """tests a command to see if its a command for the bot, returns True and calls self.processBotCommand(cmd, sender) if its is. """ - + PchumLog.debug("tryBotCommand('%s' '%s' '%s')" % (prefix, dest, msg)) if dest == self.client.nick: dest = parse_nick(prefix)[0] elif msg.startswith(self.client.nick): - msg = msg[len(self.client.nick)+1:] - else: + msg = msg[len(self.client.nick) + 1 :] + else: return False msg = msg.strip() - parts = msg.split(' ', 1) + parts = msg.split(" ", 1) command = parts[0] arg = parts[1:] @@ -198,13 +210,3 @@ class BotCommandHandler(DefaultCommandHandler): except CommandError as e: helpers.msg(self.client, dest, str(e)) return True - - - - - - - - - - diff --git a/oyoyo/helpers.py b/oyoyo/helpers.py index 4513366..6dbb897 100644 --- a/oyoyo/helpers.py +++ b/oyoyo/helpers.py @@ -20,14 +20,16 @@ import logging import random -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + def msg(cli, user, msg): - for line in msg.split('\n'): + for line in msg.split("\n"): cli.send("PRIVMSG", user, ":%s" % line) + def names(cli, *channels): - tmp = __builtins__['list'](channels) + tmp = __builtins__["list"](channels) msglist = [] while len(tmp) > 0: msglist.append(tmp.pop()) @@ -38,12 +40,15 @@ def names(cli, *channels): if len(msglist) > 0: cli.send("NAMES %s" % (",".join(msglist))) + def channel_list(cli): cli.send("LIST") + def kick(cli, handle, channel, reason=""): cli.send("KICK %s %s %s" % (channel, handle, reason)) + def mode(cli, channel, mode, options=None): PchumLog.debug("mode = " + str(mode)) PchumLog.debug("options = " + str(options)) @@ -52,9 +57,10 @@ def mode(cli, channel, mode, options=None): cmd += " %s" % (options) cli.send(cmd) + def ctcp(cli, handle, cmd, msg=""): - # Space breaks protocol if msg is absent - if msg=="": + # Space breaks protocol if msg is absent + if msg == "": cli.send("PRIVMSG", handle, "\x01%s\x01" % (cmd)) else: cli.send("PRIVMSG", handle, "\x01%s %s\x01" % (cmd, msg)) @@ -63,77 +69,96 @@ def ctcp(cli, handle, cmd, msg=""): def ctcp_reply(cli, handle, cmd, msg=""): notice(cli, str(handle), "\x01%s %s\x01" % (cmd.upper(), msg)) + def metadata(cli, target, subcommand, *params): # IRC metadata draft specification # https://gist.github.com/k4bek4be/92c2937cefd49990fbebd001faf2b237 cli.send("METADATA", target, subcommand, *params) + def cap(cli, subcommand, *params): # Capability Negotiation # https://ircv3.net/specs/extensions/capability-negotiation.html cli.send("CAP", subcommand, *params) + def msgrandom(cli, choices, dest, user=None): o = "%s: " % user if user else "" o += random.choice(choices) msg(cli, dest, o) + def _makeMsgRandomFunc(choices): def func(cli, dest, user=None): msgrandom(cli, choices, dest, user) + return func -msgYes = _makeMsgRandomFunc(['yes', 'alright', 'ok']) -msgOK = _makeMsgRandomFunc(['ok', 'done']) -msgNo = _makeMsgRandomFunc(['no', 'no-way']) + +msgYes = _makeMsgRandomFunc(["yes", "alright", "ok"]) +msgOK = _makeMsgRandomFunc(["ok", "done"]) +msgNo = _makeMsgRandomFunc(["no", "no-way"]) def ns(cli, *args): msg(cli, "NickServ", " ".join(args)) + def cs(cli, *args): msg(cli, "ChanServ", " ".join(args)) + def identify(cli, passwd, authuser="NickServ"): msg(cli, authuser, "IDENTIFY %s" % passwd) + def quit(cli, msg): cli.send("QUIT %s" % (msg)) + def user(cli, username, realname): - cli.send("USER", - username, - '0', - '*', - ':' + realname) + cli.send("USER", username, "0", "*", ":" + realname) + _simple = ( - 'join', - 'part', - 'nick', - 'notice', - 'invite', + "join", + "part", + "nick", + "notice", + "invite", ) + + def _addsimple(): import sys + def simplecmd(cmd_name): def f(cli, *args): cli.send(cmd_name, *args) + return f + m = sys.modules[__name__] for t in _simple: setattr(m, t, simplecmd(t.upper())) + + _addsimple() + def _addNumerics(): import sys from oyoyo import ircevents + def numericcmd(cmd_num, cmd_name): def f(cli, *args): cli.send(cmd_num, *args) + return f + m = sys.modules[__name__] for num, name in ircevents.numeric_events.items(): setattr(m, name, numericcmd(num, name)) + _addNumerics() diff --git a/oyoyo/ircevents.py b/oyoyo/ircevents.py index 9041f82..1ed1b85 100644 --- a/oyoyo/ircevents.py +++ b/oyoyo/ircevents.py @@ -116,7 +116,7 @@ numeric_events = { "374": "endofinfo", "375": "motdstart", "376": "endofmotd", - "377": "motd2", # 1997-10-16 -- tkil + "377": "motd2", # 1997-10-16 -- tkil "381": "youreoper", "382": "rehashing", "384": "myportis", @@ -143,7 +143,7 @@ numeric_events = { "423": "noadmininfo", "424": "fileerror", "431": "nonicknamegiven", - "432": "erroneusnickname", # Thiss iz how its speld in thee RFC. + "432": "erroneusnickname", # Thiss iz how its speld in thee RFC. "433": "nicknameinuse", "436": "nickcollision", "437": "unavailresource", # "Nick temporally unavailable" @@ -158,7 +158,7 @@ numeric_events = { "462": "alreadyregistered", "463": "nopermforhost", "464": "passwdmismatch", - "465": "yourebannedcreep", # I love this one... + "465": "yourebannedcreep", # I love this one... "466": "youwillbebanned", "467": "keyset", "471": "channelisfull", @@ -172,7 +172,7 @@ numeric_events = { "481": "noprivileges", "482": "chanoprivsneeded", "483": "cantkillserver", - "484": "restricted", # Connection is restricted + "484": "restricted", # Connection is restricted "485": "uniqopprivsneeded", "491": "nooperhost", "492": "noservicehost", @@ -195,7 +195,7 @@ metadata_numeric_events = { "768": "keynotset", "769": "keynopermission", "770": "metadatasubok", - } +} numeric_events.update(metadata_numeric_events) generated_events = [ @@ -226,10 +226,7 @@ protocol_events = [ "nick", # We can get svsnicked "metadata", # Metadata specification "tagmsg", # IRCv3 message tags extension - "cap" # IRCv3 Client Capability Negotiation + "cap", # IRCv3 Client Capability Negotiation ] -all_events = (generated_events - + protocol_events - + list(numeric_events.values())) - +all_events = generated_events + protocol_events + list(numeric_events.values()) diff --git a/oyoyo/parse.py b/oyoyo/parse.py index 8cdbaa3..e35c15c 100644 --- a/oyoyo/parse.py +++ b/oyoyo/parse.py @@ -19,7 +19,8 @@ import logging from oyoyo.ircevents import numeric_events -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + def parse_raw_irc_command(element): """ @@ -55,20 +56,20 @@ def parse_raw_irc_command(element): """ - + try: element = element.decode("utf-8") except UnicodeDecodeError as e: PchumLog.debug("utf-8 error %s" % str(e)) - element = element.decode("latin-1", 'replace') - + element = element.decode("latin-1", "replace") + parts = element.strip().split(" ") - if parts[0].startswith(':'): + if parts[0].startswith(":"): tags = None prefix = parts[0][1:] command = parts[1] args = parts[2:] - elif parts[0].startswith('@'): + elif parts[0].startswith("@"): # Message tag tags = parts[0] prefix = parts[1][1:] @@ -84,14 +85,14 @@ def parse_raw_irc_command(element): try: command = numeric_events[command] except KeyError: - PchumLog.info('unknown numeric event %s' % command) + PchumLog.info("unknown numeric event %s" % command) command = command.lower() - if args[0].startswith(':'): + if args[0].startswith(":"): args = [" ".join(args)[1:]] else: for idx, arg in enumerate(args): - if arg.startswith(':'): + if arg.startswith(":"): args = args[:idx] + [" ".join(args[idx:])[1:]] break @@ -99,23 +100,22 @@ def parse_raw_irc_command(element): def parse_nick(name): - """ parse a nickname and return a tuple of (nick, mode, user, host) + """parse a nickname and return a tuple of (nick, mode, user, host) [ '!' [ = ] ] [ '@' ] """ try: - nick, rest = name.split('!') + nick, rest = name.split("!") except ValueError: return (name, None, None, None) try: - mode, rest = rest.split('=') + mode, rest = rest.split("=") except ValueError: mode, rest = None, rest try: - user, host = rest.split('@') + user, host = rest.split("@") except ValueError: return (name, mode, rest, None) return (name, mode, user, host) - diff --git a/oyoyo/services.py b/oyoyo/services.py index c0fc5e6..c0c4848 100644 --- a/oyoyo/services.py +++ b/oyoyo/services.py @@ -1,117 +1,123 @@ # NickServ basic functions _nickservfuncs = ( - 'register', - 'group', - 'glist', - 'identify', - 'access', - 'drop', - 'recover', - 'release', - 'sendpass', - 'ghost', - 'alist', - 'info', - 'list', - 'logout', - 'status', - 'update' + "register", + "group", + "glist", + "identify", + "access", + "drop", + "recover", + "release", + "sendpass", + "ghost", + "alist", + "info", + "list", + "logout", + "status", + "update", ) # NickServ SET functions _nickservsetfuncs = ( - 'display', - 'password', - 'language', - 'url', - 'email', - 'icq', - 'greet', - 'kill', - 'secure', - 'private', - 'hide', - 'msg', - 'autoop' + "display", + "password", + "language", + "url", + "email", + "icq", + "greet", + "kill", + "secure", + "private", + "hide", + "msg", + "autoop", ) # ChanServ basic functions _chanservfuncs = ( - 'register', - 'identify', - 'sop', - 'aop', - 'hop', - 'vop', - 'access', - 'levels', - 'akick', - 'drop', - 'sendpass', - 'ban', - 'unban', - 'clear', - 'owner', - 'deowner', - 'protect', - 'deprotect', - 'op', - 'deop', - 'halfop', - 'dehalfop', - 'voice', - 'devoice', - 'getkey', - 'invite', - 'kick', - 'list', - 'logout', - 'topic', - 'info', - 'appendtopic', - 'enforce' + "register", + "identify", + "sop", + "aop", + "hop", + "vop", + "access", + "levels", + "akick", + "drop", + "sendpass", + "ban", + "unban", + "clear", + "owner", + "deowner", + "protect", + "deprotect", + "op", + "deop", + "halfop", + "dehalfop", + "voice", + "devoice", + "getkey", + "invite", + "kick", + "list", + "logout", + "topic", + "info", + "appendtopic", + "enforce", ) _chanservsetfuncs = ( - 'founder', - 'successor', - 'password', - 'desc', - 'url', - 'email', - 'entrymsg', - 'bantype', - 'mlock', - 'keeptopic', - 'opnotice', - 'peace', - 'private', - 'restricted', - 'secure', - 'secureops', - 'securefounder', - 'signkick', - 'topiclock', - 'xop' + "founder", + "successor", + "password", + "desc", + "url", + "email", + "entrymsg", + "bantype", + "mlock", + "keeptopic", + "opnotice", + "peace", + "private", + "restricted", + "secure", + "secureops", + "securefounder", + "signkick", + "topiclock", + "xop", ) + def _addServ(serv, funcs, prefix=""): def simplecmd(cmd_name): if prefix: cmd_name = prefix.upper() + " " + cmd_name + def f(cli, *args): print(cmd_name, " ".join(args)) - #cli.send(cmd_name, serv.name, *args) + # cli.send(cmd_name, serv.name, *args) + return f + for t in funcs: setattr(serv, t, simplecmd(t.upper())) + class NickServ(object): def __init__(self, nick="NickServ"): self.name = nick _addServ(self, _nickservfuncs) _addServ(self, _nickservsetfuncs, "set") + class ChanServ(object): def __init__(self, nick="ChanServ"): self.name = nick diff --git a/parsetools.py b/parsetools.py index d7a46bd..bbc264f 100644 --- a/parsetools.py +++ b/parsetools.py @@ -12,51 +12,56 @@ except ImportError: import dataobjs import ostools + # karxi: My own contribution to this - a proper lexer. import pnc.lexercon as lexercon from generic import mysteryTime from quirks import ScriptQuirks from pyquirks import PythonQuirks -#from luaquirks import LuaQuirks -PchumLog = logging.getLogger('pchumLogger') +# from luaquirks import LuaQuirks + +PchumLog = logging.getLogger("pchumLogger") # I'll clean up the things that are no longer needed once the transition is # actually finished. QString = str -_ctag_begin = re.compile(r'(?i)') -_gtag_begin = re.compile(r'(?i)') -_ctag_end = re.compile(r'(?i)') -_ctag_rgb = re.compile(r'\d+,\d+,\d+') +_ctag_begin = re.compile(r"(?i)") +_gtag_begin = re.compile(r"(?i)") +_ctag_end = re.compile(r"(?i)") +_ctag_rgb = re.compile(r"\d+,\d+,\d+") _urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+") -#_url2re = re.compile(r"(?i)(?""") _mecmdre = re.compile(r"^(/me|PESTERCHUM:ME)(\S*)") _oocre = re.compile(r"([\[(\{])\1.*([\])\}])\2") -_format_begin = re.compile(r'(?i)<([ibu])>') -_format_end = re.compile(r'(?i)') +_format_begin = re.compile(r"(?i)<([ibu])>") +_format_end = re.compile(r"(?i)") _honk = re.compile(r"(?i)\bhonk\b") _groupre = re.compile(r"\\([0-9]+)") quirkloader = ScriptQuirks() _functionre = None + def loadQuirks(): global quirkloader, _functionre quirkloader.add(PythonQuirks()) - #quirkloader.add(LuaQuirks()) + # quirkloader.add(LuaQuirks()) quirkloader.loadAll() quirkloader.funcre() _functionre = re.compile(r"%s" % quirkloader.funcre()) + def reloadQuirkFunctions(): quirkloader.loadAll() global _functionre _functionre = re.compile(r"%s" % quirkloader.funcre()) + def lexer(string, objlist): """objlist is a list: [(objecttype, re),...] list is in order of preference""" stringlist = [string] @@ -80,6 +85,7 @@ def lexer(string, objlist): stringlist = copy(newstringlist) return stringlist + # karxi: All of these were derived from object before. I changed them to # lexercon.Chunk so that I'd have an easier way to match against them until # they're redone/removed. @@ -87,12 +93,13 @@ class colorBegin(lexercon.Chunk): def __init__(self, string, color): self.string = string self.color = color + def convert(self, format): color = self.color if format == "text": return "" if _ctag_rgb.match(color) is not None: - if format=='ctag': + if format == "ctag": return "" % (color) try: qc = QtGui.QColor(*[int(c) for c in color.split(",")]) @@ -105,14 +112,16 @@ class colorBegin(lexercon.Chunk): if format == "html": return '' % (qc.name()) elif format == "bbcode": - return '[color=%s]' % (qc.name()) + return "[color=%s]" % (qc.name()) elif format == "ctag": - (r,g,b,a) = qc.getRgb() - return '' % (r,g,b) + (r, g, b, a) = qc.getRgb() + return "" % (r, g, b) + class colorEnd(lexercon.Chunk): def __init__(self, string): self.string = string + def convert(self, format): if format == "html": return "" @@ -123,10 +132,12 @@ class colorEnd(lexercon.Chunk): else: return self.string + class formatBegin(lexercon.Chunk): def __init__(self, string, ftype): self.string = string self.ftype = ftype + def convert(self, format): if format == "html": return "<%s>" % (self.ftype) @@ -137,10 +148,12 @@ class formatBegin(lexercon.Chunk): else: return self.string + class formatEnd(lexercon.Chunk): def __init__(self, string, ftype): self.string = string self.ftype = ftype + def convert(self, format): if format == "html": return "" % (self.ftype) @@ -151,9 +164,11 @@ class formatEnd(lexercon.Chunk): else: return self.string + class hyperlink(lexercon.Chunk): def __init__(self, string): self.string = string + def convert(self, format): if format == "html": return "%s" % (self.string, self.string) @@ -162,16 +177,20 @@ class hyperlink(lexercon.Chunk): else: return self.string + class hyperlink_lazy(hyperlink): """Deprecated since it doesn't seem to turn the full url into a link, probably not required anyway, best to require a protocol prefix.""" + def __init__(self, string): self.string = "http://" + string + class imagelink(lexercon.Chunk): def __init__(self, string, img): self.string = string self.img = img + def convert(self, format): if format == "html": return self.string @@ -183,84 +202,104 @@ class imagelink(lexercon.Chunk): else: return "" + class memolex(lexercon.Chunk): def __init__(self, string, space, channel): self.string = string self.space = space self.channel = channel + def convert(self, format): if format == "html": return "%s%s" % (self.space, self.channel, self.channel) else: return self.string + class chumhandlelex(lexercon.Chunk): def __init__(self, string, space, handle): self.string = string self.space = space self.handle = handle + def convert(self, format): if format == "html": return "%s%s" % (self.space, self.handle, self.handle) else: return self.string + class smiley(lexercon.Chunk): def __init__(self, string): self.string = string + def convert(self, format): if format == "html": - return "%s" % (smiledict[self.string], self.string, self.string) + return "%s" % ( + smiledict[self.string], + self.string, + self.string, + ) else: return self.string + class honker(lexercon.Chunk): def __init__(self, string): self.string = string + def convert(self, format): # No more 'honk' turning into an emote because everyone hated that :') - #if format == "html": + # if format == "html": # return "" - #else: + # else: return self.string + class mecmd(lexercon.Chunk): def __init__(self, string, mecmd, suffix): self.string = string self.suffix = suffix + def convert(self, format): return self.string + kxpclexer = lexercon.Pesterchum() + def kxlexMsg(string): # Do a bit of sanitization. msg = str(string) # TODO: Let people paste line-by-line normally. Maybe have a mass-paste # right-click option? - msg = msg.replace('\n', ' ').replace('\r', ' ') + msg = msg.replace("\n", " ").replace("\r", " ") # Something the original doesn't seem to have accounted for. # Replace tabs with 4 spaces. - msg = msg.replace('\t', ' ' * 4) + msg = msg.replace("\t", " " * 4) # Begin lexing. msg = kxpclexer.lex(msg) # ...and that's it for this. return msg + def lexMessage(string): - lexlist = [(mecmd, _mecmdre), - (colorBegin, _ctag_begin), (colorBegin, _gtag_begin), - (colorEnd, _ctag_end), - # karxi: Disabled this for now. No common versions of Pesterchum - # actually use it, save for Chumdroid...which shouldn't. - # When I change out parsers, I might add it back in. - ##(formatBegin, _format_begin), (formatEnd, _format_end), - (imagelink, _imgre), - (hyperlink, _urlre), - (memolex, _memore), - (chumhandlelex, _handlere), - (smiley, _smilere), - (honker, _honk)] + lexlist = [ + (mecmd, _mecmdre), + (colorBegin, _ctag_begin), + (colorBegin, _gtag_begin), + (colorEnd, _ctag_end), + # karxi: Disabled this for now. No common versions of Pesterchum + # actually use it, save for Chumdroid...which shouldn't. + # When I change out parsers, I might add it back in. + ##(formatBegin, _format_begin), (formatEnd, _format_end), + (imagelink, _imgre), + (hyperlink, _urlre), + (memolex, _memore), + (chumhandlelex, _handlere), + (smiley, _smilere), + (honker, _honk), + ] string = str(string) string = string.replace("\n", " ").replace("\r", " ") @@ -282,14 +321,15 @@ def lexMessage(string): else: balanced.append(o) if beginc > endc: - for i in range(0, beginc-endc): + for i in range(0, beginc - endc): balanced.append(colorEnd("")) if len(balanced) == 0: balanced.append("") - if type(balanced[len(balanced)-1]) not in [str, str]: + if type(balanced[len(balanced) - 1]) not in [str, str]: balanced.append("") return balanced + def convertTags(lexed, format="html"): if format not in ["html", "bbcode", "ctag", "text"]: raise ValueError("Color format not recognized") @@ -297,11 +337,13 @@ def convertTags(lexed, format="html"): if type(lexed) in [str, str]: lexed = lexMessage(lexed) escaped = "" - #firststr = True + # firststr = True for (i, o) in enumerate(lexed): if type(o) in [str, str]: if format == "html": - escaped += o.replace("&", "&").replace(">", ">").replace("<","<") + escaped += ( + o.replace("&", "&").replace(">", ">").replace("<", "<") + ) else: escaped += o else: @@ -309,6 +351,7 @@ def convertTags(lexed, format="html"): return escaped + def _max_msg_len(mask=None, target=None, nick=None, ident=None): # karxi: Copied from another file of mine, and modified to work with # Pesterchum. @@ -330,7 +373,7 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None): # Since we should always be able to fetch this # karxi: ... Which we can't, right now, unlike in the old script. # TODO: Resolve this issue, give it the necessary information. - + # If we CAN'T, stick with a length of 30, since that seems to be # the average maximum nowadays limit -= len(nick) if nick is not None else 30 @@ -339,7 +382,7 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None): # ident length limit -= len(ident) if nick is not None else 10 # Maximum (?) host length - limit -= 63 # RFC 2812 + limit -= 63 # RFC 2812 # The target is the place this is getting sent to - a channel or a nick if target is not None: limit -= len(target) @@ -351,10 +394,11 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None): return limit + def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False): """Split messages so that they don't go over the length limit. Returns a list of the messages, neatly split. - + Keep in mind that there's a little bit of magic involved in this at the moment; some unsafe assumptions are made.""" @@ -384,11 +428,21 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False): curlen = 0 # Maximum number of characters *to* use. if not maxlen: - maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname) + maxlen = _max_msg_len( + None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname + ) elif maxlen < 0: # Subtract the (negative) length, giving us less leeway in this # function. - maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname) + maxlen + maxlen = ( + _max_msg_len( + None, + None, + ctx.mainwindow.profile().handle, + ctx.mainwindow.irc.cli.realname, + ) + + maxlen + ) # Defined here, but modified in the loop. msglen = 0 @@ -398,7 +452,7 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False): tags that will be needed.""" return maxlen - curlen - (len(open_ctags) * 4) - #safekeeping = lexed[:] + # safekeeping = lexed[:] lexed = collections.deque(lexed) rounds = 0 # NOTE: This entire mess is due for a rewrite. I'll start splitting it into @@ -448,14 +502,14 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False): # instead? subround += 1 if debug: - PchumLog.info("[Splitting round {}-{}...]".format( - rounds, subround - )) - point = msg.rfind(' ', 0, lenl) + PchumLog.info( + "[Splitting round {}-{}...]".format(rounds, subround) + ) + point = msg.rfind(" ", 0, lenl) if point < 0: # No spaces to break on...ugh. Break at the last space # we can instead. - point = lenl ## - 1 + point = lenl ## - 1 # NOTE: The - 1 is for safety (but probably isn't # actually necessary.) # Split and push what we have. @@ -509,16 +563,20 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False): cte = lexercon.CTagEnd("", fmt, None) working.extend([cte] * len(open_ctags)) if debug: - print("\tRound {0} linebreak: Added {1} closing ctags".format( + print( + "\tRound {0} linebreak: Added {1} closing ctags".format( rounds, len(open_ctags) - )) + ) + ) # Run it through the lexer again to render it. - working = ''.join(kxpclexer.list_convert(working)) + working = "".join(kxpclexer.list_convert(working)) if debug: - print("\tRound {0} add: len == {1} (of {2})".format( + print( + "\tRound {0} add: len == {1} (of {2})".format( rounds, len(working), maxlen - )) + ) + ) # Now that it's done the work for us, append and resume. output.append(working) @@ -598,22 +656,19 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False): if len(working) > 0: if debug: print("Adding end trails: {!r}".format(working)) - working = ''.join(working) + working = "".join(working) output.append(working) # We're...done? return output + def _is_ooc(msg, strict=True): """Check if a line is OOC. Note that Pesterchum *is* kind enough to strip trailing spaces for us, even in the older versions, but we don't do that in this function. (It's handled by the calling one.)""" # Define the matching braces. - braces = ( - ('(', ')'), - ('[', ']'), - ('{', '}') - ) + braces = (("(", ")"), ("[", "]"), ("{", "}")) oocDetected = _oocre.match(msg) # Somewhat-improved matching. @@ -630,6 +685,7 @@ def _is_ooc(msg, strict=True): return True return False + def kxhandleInput(ctx, text=None, flavor=None): """The function that user input that should be sent to the server is routed through. Handles lexing, splitting, and quirk application, as well as @@ -680,7 +736,7 @@ def kxhandleInput(ctx, text=None, flavor=None): # I'm pretty sure that putting a space before a /me *should* break the # /me, but in practice, that's not the case. is_action = msg.startswith("/me") - + # Begin message processing. # We use 'text' despite its lack of processing because it's simpler. if should_quirk and not (is_action or is_ooc): @@ -706,8 +762,8 @@ def kxhandleInput(ctx, text=None, flavor=None): msgbox.setInformativeText(err_info) msgbox.exec() return - - PchumLog.info("--> recv \"%s\"" % msg) + + 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): @@ -718,23 +774,21 @@ def kxhandleInput(ctx, text=None, flavor=None): # an object type I provided - just so I could pluck them out # later. msg[i] = m.convert(format="ctag") - msg = ''.join(msg) + msg = "".join(msg) # Quirks have been applied. Lex the messages (finally). msg = kxlexMsg(msg) # Debug output. - #try: + # try: # print(repr(msg)) - #except Exception as err: + # except Exception as err: # print("(Couldn't print lexed message: {!s})".format(err)) # Remove coloring if this is a /me! if is_action: # Filter out formatting specifiers (just ctags, at the moment). - msg = [m for m in msg if not isinstance(m, - (lexercon.CTag, lexercon.CTagEnd) - )] + msg = [m for m in msg if not isinstance(m, (lexercon.CTag, lexercon.CTagEnd))] # We'll also add /me to the beginning of any new messages, later. # Put what's necessary in before splitting. @@ -752,9 +806,11 @@ def kxhandleInput(ctx, text=None, flavor=None): # We'll use those later. # Split the messages so we don't go over the buffer and lose text. - maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname) - # ctx.mainwindow.profile().handle ==> Get handle - # ctx.mainwindow.irc.cli.realname ==> Get ident (Same as realname in this case.) + maxlen = _max_msg_len( + None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname + ) + # ctx.mainwindow.profile().handle ==> Get handle + # ctx.mainwindow.irc.cli.realname ==> Get ident (Same as realname in this case.) # Since we have to do some post-processing, we need to adjust the maximum # length we can use. if flavor == "convo": @@ -775,10 +831,8 @@ def kxhandleInput(ctx, text=None, flavor=None): # Pester message handling. if flavor == "convo": # if ceased, rebegin - if hasattr(ctx, 'chumopen') and not ctx.chumopen: - ctx.mainwindow.newConvoStarted.emit( - QString(ctx.title()), True - ) + if hasattr(ctx, "chumopen") and not ctx.chumopen: + ctx.mainwindow.newConvoStarted.emit(QString(ctx.title()), True) ctx.setChumOpen(True) # Post-process and send the messages. @@ -804,11 +858,10 @@ def kxhandleInput(ctx, text=None, flavor=None): # construct the messages. clientMsg = "{2}{3}{4}: {0}".format( - clientMsg, colorcmd, grammar.pcf, initials, grammar.number - ) + clientMsg, colorcmd, grammar.pcf, initials, grammar.number + ) # Not sure if this needs a space at the end...? - serverMsg = "{2}: {0}".format( - serverMsg, colorcmd, initials) + serverMsg = "{2}: {0}".format(serverMsg, colorcmd, initials) ctx.addMessage(clientMsg, True) if flavor != "menus": @@ -825,7 +878,14 @@ def addTimeInitial(string, grammar): # support Doc Scratch mode if (endoftag < 0 or endoftag > 16) or (endofi < 0 or endofi > 17): return string - return string[0:endoftag+1]+grammar.pcf+string[endoftag+1:endofi]+grammar.number+string[endofi:] + return ( + string[0 : endoftag + 1] + + grammar.pcf + + string[endoftag + 1 : endofi] + + grammar.number + + string[endofi:] + ) + def timeProtocol(cmd): dir = cmd[0] @@ -836,31 +896,32 @@ def timeProtocol(cmd): try: l = [int(x) for x in cmd.split(":")] except ValueError: - l = [0,0] - timed = timedelta(0, l[0]*3600+l[1]*60) + l = [0, 0] + timed = timedelta(0, l[0] * 3600 + l[1] * 60) if dir == "P": - timed = timed*-1 + timed = timed * -1 return timed + def timeDifference(td): - if td == timedelta(microseconds=1): # mysteryTime replacement :( + if td == timedelta(microseconds=1): # mysteryTime replacement :( return "??:?? FROM ????" if td < timedelta(0): when = "AGO" else: when = "FROM NOW" atd = abs(td) - minutes = (atd.days*86400 + atd.seconds) // 60 + minutes = (atd.days * 86400 + atd.seconds) // 60 hours = minutes // 60 leftoverminutes = minutes % 60 if atd == timedelta(0): timetext = "RIGHT NOW" - elif atd < timedelta(0,3600): + elif atd < timedelta(0, 3600): if minutes == 1: timetext = "%d MINUTE %s" % (minutes, when) else: timetext = "%d MINUTES %s" % (minutes, when) - elif atd < timedelta(0,3600*100): + elif atd < timedelta(0, 3600 * 100): if hours == 1 and leftoverminutes == 0: timetext = "%d:%02d HOUR %s" % (hours, leftoverminutes, when) else: @@ -869,16 +930,20 @@ def timeDifference(td): timetext = "%d HOURS %s" % (hours, when) return timetext + def nonerep(text): return text + class parseLeaf(object): def __init__(self, function, parent): self.nodes = [] self.function = function self.parent = parent + def append(self, node): self.nodes.append(node) + def expand(self, mo): out = "" for n in self.nodes: @@ -891,12 +956,15 @@ class parseLeaf(object): out = self.function(out) return out + class backreference(object): def __init__(self, number): self.number = number + def __str__(self): return self.number + def parseRegexpFunctions(to): parsed = parseLeaf(nonerep, None) current = parsed @@ -907,7 +975,7 @@ def parseRegexpFunctions(to): mo = _functionre.search(tmp) if mo is not None: if mo.start() > 0: - current.append(to[curi:curi+mo.start()]) + current.append(to[curi : curi + mo.start()]) backr = _groupre.search(mo.group()) if backr is not None: current.append(backreference(backr.group(1))) @@ -920,7 +988,7 @@ def parseRegexpFunctions(to): current = current.parent else: current.append(")") - curi = mo.end()+curi + curi = mo.end() + curi else: current.append(to[curi:]) curi = len(to) @@ -929,11 +997,14 @@ def parseRegexpFunctions(to): def img2smiley(string): string = str(string) + def imagerep(mo): return reverse_smiley[mo.group(1)] + string = re.sub(r'', imagerep, string) return string + smiledict = { ":rancorous:": "pc_rancorous.png", ":apple:": "apple.png", @@ -998,78 +1069,166 @@ smiledict = { ":scorpio:": "scorpio.gif", ":shades:": "shades.png", ":honk:": "honk.png", - } +} -reverse_smiley = dict((v,k) for k, v in smiledict.items()) +reverse_smiley = dict((v, k) for k, v in smiledict.items()) _smilere = re.compile("|".join(list(smiledict.keys()))) + class ThemeException(Exception): def __init__(self, value): self.parameter = value + def __str__(self): return repr(self.parameter) + def themeChecker(theme): - needs = ["main/size", "main/icon", "main/windowtitle", "main/style", \ - "main/background-image", "main/menubar/style", "main/menu/menuitem", \ - "main/menu/style", "main/menu/selected", "main/close/image", \ - "main/close/loc", "main/minimize/image", "main/minimize/loc", \ - "main/menu/loc", "main/menus/client/logviewer", \ - "main/menus/client/addgroup", "main/menus/client/options", \ - "main/menus/client/exit", "main/menus/client/userlist", \ - "main/menus/client/memos", "main/menus/client/import", \ - "main/menus/client/idle", "main/menus/client/reconnect", \ - "main/menus/client/_name", "main/menus/profile/quirks", \ - "main/menus/profile/block", "main/menus/profile/color", \ - "main/menus/profile/switch", "main/menus/profile/_name", \ - "main/menus/help/about", "main/menus/help/_name", "main/moodlabel/text", \ - "main/moodlabel/loc", "main/moodlabel/style", "main/moods", \ - "main/addchum/style", "main/addchum/text", "main/addchum/size", \ - "main/addchum/loc", "main/pester/text", "main/pester/size", \ - "main/pester/loc", "main/block/text", "main/block/size", "main/block/loc", \ - "main/mychumhandle/label/text", "main/mychumhandle/label/loc", \ - "main/mychumhandle/label/style", "main/mychumhandle/handle/loc", \ - "main/mychumhandle/handle/size", "main/mychumhandle/handle/style", \ - "main/mychumhandle/colorswatch/size", "main/mychumhandle/colorswatch/loc", \ - "main/defaultmood", "main/chums/size", "main/chums/loc", \ - "main/chums/style", "main/menus/rclickchumlist/pester", \ - "main/menus/rclickchumlist/removechum", \ - "main/menus/rclickchumlist/blockchum", "main/menus/rclickchumlist/viewlog", \ - "main/menus/rclickchumlist/removegroup", \ - "main/menus/rclickchumlist/renamegroup", \ - "main/menus/rclickchumlist/movechum", "convo/size", \ - "convo/tabwindow/style", "convo/tabs/tabstyle", "convo/tabs/style", \ - "convo/tabs/selectedstyle", "convo/style", "convo/margins", \ - "convo/chumlabel/text", "convo/chumlabel/style", "convo/chumlabel/align/h", \ - "convo/chumlabel/align/v", "convo/chumlabel/maxheight", \ - "convo/chumlabel/minheight", "main/menus/rclickchumlist/quirksoff", \ - "main/menus/rclickchumlist/addchum", "main/menus/rclickchumlist/blockchum", \ - "main/menus/rclickchumlist/unblockchum", \ - "main/menus/rclickchumlist/viewlog", "main/trollslum/size", \ - "main/trollslum/style", "main/trollslum/label/text", \ - "main/trollslum/label/style", "main/menus/profile/block", \ - "main/chums/moods/blocked/icon", "convo/systemMsgColor", \ - "convo/textarea/style", "convo/text/beganpester", "convo/text/ceasepester", \ - "convo/text/blocked", "convo/text/unblocked", "convo/text/blockedmsg", \ - "convo/text/idle", "convo/input/style", "memos/memoicon", \ - "memos/textarea/style", "memos/systemMsgColor", "convo/text/joinmemo", \ - "memos/input/style", "main/menus/rclickchumlist/banuser", \ - "main/menus/rclickchumlist/opuser", "main/menus/rclickchumlist/voiceuser", \ - "memos/margins", "convo/text/openmemo", "memos/size", "memos/style", \ - "memos/label/text", "memos/label/style", "memos/label/align/h", \ - "memos/label/align/v", "memos/label/maxheight", "memos/label/minheight", \ - "memos/userlist/style", "memos/userlist/width", "memos/time/text/width", \ - "memos/time/text/style", "memos/time/arrows/left", \ - "memos/time/arrows/style", "memos/time/buttons/style", \ - "memos/time/arrows/right", "memos/op/icon", "memos/voice/icon", \ - "convo/text/closememo", "convo/text/kickedmemo", \ - "main/chums/userlistcolor", "main/defaultwindow/style", \ - "main/chums/moods", "main/chums/moods/chummy/icon", "main/menus/help/help", \ - "main/menus/help/calsprite", "main/menus/help/nickserv", "main/menus/help/chanserv", \ - "main/menus/rclickchumlist/invitechum", "main/menus/client/randen", \ - "main/menus/rclickchumlist/memosetting", "main/menus/rclickchumlist/memonoquirk", \ - "main/menus/rclickchumlist/memohidden", "main/menus/rclickchumlist/memoinvite", \ - "main/menus/rclickchumlist/memomute", "main/menus/rclickchumlist/notes"] + needs = [ + "main/size", + "main/icon", + "main/windowtitle", + "main/style", + "main/background-image", + "main/menubar/style", + "main/menu/menuitem", + "main/menu/style", + "main/menu/selected", + "main/close/image", + "main/close/loc", + "main/minimize/image", + "main/minimize/loc", + "main/menu/loc", + "main/menus/client/logviewer", + "main/menus/client/addgroup", + "main/menus/client/options", + "main/menus/client/exit", + "main/menus/client/userlist", + "main/menus/client/memos", + "main/menus/client/import", + "main/menus/client/idle", + "main/menus/client/reconnect", + "main/menus/client/_name", + "main/menus/profile/quirks", + "main/menus/profile/block", + "main/menus/profile/color", + "main/menus/profile/switch", + "main/menus/profile/_name", + "main/menus/help/about", + "main/menus/help/_name", + "main/moodlabel/text", + "main/moodlabel/loc", + "main/moodlabel/style", + "main/moods", + "main/addchum/style", + "main/addchum/text", + "main/addchum/size", + "main/addchum/loc", + "main/pester/text", + "main/pester/size", + "main/pester/loc", + "main/block/text", + "main/block/size", + "main/block/loc", + "main/mychumhandle/label/text", + "main/mychumhandle/label/loc", + "main/mychumhandle/label/style", + "main/mychumhandle/handle/loc", + "main/mychumhandle/handle/size", + "main/mychumhandle/handle/style", + "main/mychumhandle/colorswatch/size", + "main/mychumhandle/colorswatch/loc", + "main/defaultmood", + "main/chums/size", + "main/chums/loc", + "main/chums/style", + "main/menus/rclickchumlist/pester", + "main/menus/rclickchumlist/removechum", + "main/menus/rclickchumlist/blockchum", + "main/menus/rclickchumlist/viewlog", + "main/menus/rclickchumlist/removegroup", + "main/menus/rclickchumlist/renamegroup", + "main/menus/rclickchumlist/movechum", + "convo/size", + "convo/tabwindow/style", + "convo/tabs/tabstyle", + "convo/tabs/style", + "convo/tabs/selectedstyle", + "convo/style", + "convo/margins", + "convo/chumlabel/text", + "convo/chumlabel/style", + "convo/chumlabel/align/h", + "convo/chumlabel/align/v", + "convo/chumlabel/maxheight", + "convo/chumlabel/minheight", + "main/menus/rclickchumlist/quirksoff", + "main/menus/rclickchumlist/addchum", + "main/menus/rclickchumlist/blockchum", + "main/menus/rclickchumlist/unblockchum", + "main/menus/rclickchumlist/viewlog", + "main/trollslum/size", + "main/trollslum/style", + "main/trollslum/label/text", + "main/trollslum/label/style", + "main/menus/profile/block", + "main/chums/moods/blocked/icon", + "convo/systemMsgColor", + "convo/textarea/style", + "convo/text/beganpester", + "convo/text/ceasepester", + "convo/text/blocked", + "convo/text/unblocked", + "convo/text/blockedmsg", + "convo/text/idle", + "convo/input/style", + "memos/memoicon", + "memos/textarea/style", + "memos/systemMsgColor", + "convo/text/joinmemo", + "memos/input/style", + "main/menus/rclickchumlist/banuser", + "main/menus/rclickchumlist/opuser", + "main/menus/rclickchumlist/voiceuser", + "memos/margins", + "convo/text/openmemo", + "memos/size", + "memos/style", + "memos/label/text", + "memos/label/style", + "memos/label/align/h", + "memos/label/align/v", + "memos/label/maxheight", + "memos/label/minheight", + "memos/userlist/style", + "memos/userlist/width", + "memos/time/text/width", + "memos/time/text/style", + "memos/time/arrows/left", + "memos/time/arrows/style", + "memos/time/buttons/style", + "memos/time/arrows/right", + "memos/op/icon", + "memos/voice/icon", + "convo/text/closememo", + "convo/text/kickedmemo", + "main/chums/userlistcolor", + "main/defaultwindow/style", + "main/chums/moods", + "main/chums/moods/chummy/icon", + "main/menus/help/help", + "main/menus/help/calsprite", + "main/menus/help/nickserv", + "main/menus/help/chanserv", + "main/menus/rclickchumlist/invitechum", + "main/menus/client/randen", + "main/menus/rclickchumlist/memosetting", + "main/menus/rclickchumlist/memonoquirk", + "main/menus/rclickchumlist/memohidden", + "main/menus/rclickchumlist/memoinvite", + "main/menus/rclickchumlist/memomute", + "main/menus/rclickchumlist/notes", + ] for n in needs: try: diff --git a/pesterchum.py b/pesterchum.py index 30b301e..1c96e7d 100755 --- a/pesterchum.py +++ b/pesterchum.py @@ -18,42 +18,41 @@ if os.path.dirname(sys.argv[0]): import ostools import nickservmsgs import pytwmn -#import console + +# import console from pnc.dep.attrdict import AttrDict -from profile import (userConfig, - userProfile, - pesterTheme, - PesterLog, - PesterProfileDB) -from menus import (PesterChooseQuirks, - PesterChooseTheme, - PesterChooseProfile, - PesterOptions, - PesterUserlist, - PesterMemoList, - LoadingScreen, - AboutPesterchum, - UpdatePesterchum, - AddChumDialog) -from mood import (Mood, - PesterMoodAction, - PesterMoodHandler, - PesterMoodButton) +from profile import userConfig, userProfile, pesterTheme, PesterLog, PesterProfileDB +from menus import ( + PesterChooseQuirks, + PesterChooseTheme, + PesterChooseProfile, + PesterOptions, + PesterUserlist, + PesterMemoList, + LoadingScreen, + AboutPesterchum, + UpdatePesterchum, + AddChumDialog, +) +from mood import Mood, PesterMoodAction, PesterMoodHandler, PesterMoodButton from dataobjs import PesterProfile, pesterQuirk, pesterQuirks -from generic import (PesterIcon, - RightClickTree, - PesterList, - CaseInsensitiveDict, - MovingWindow, - NoneSound, - WMButton) -from convo import (PesterTabWindow, - PesterConvo) -from parsetools import (convertTags, - addTimeInitial, - themeChecker, - ThemeException, - loadQuirks) +from generic import ( + PesterIcon, + RightClickTree, + PesterList, + CaseInsensitiveDict, + MovingWindow, + NoneSound, + WMButton, +) +from convo import PesterTabWindow, PesterConvo +from parsetools import ( + convertTags, + addTimeInitial, + themeChecker, + ThemeException, + loadQuirks, +) from memos import PesterMemo, MemoTabWindow, TimeTracker from irc import PesterIRC from logviewer import PesterLogUserSelect, PesterLogViewer @@ -77,39 +76,45 @@ loadQuirks() # Command line options parser = argparse.ArgumentParser() -parser.add_argument("--server", - "-s", - metavar="ADDRESS", - help="Specify server override. (legacy)") -parser.add_argument("--port", - "-p", - metavar="PORT", - help="Specify port override. (legacy)") -parser.add_argument("--logging", - "-l", - metavar="LEVEL", - default="WARNING", - help=("Specify level of logging, possible values are:" - " CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET." - " (https://docs.python.org/3/library/logging.html)")) -parser.add_argument("--advanced", - action="store_true", - help=("Enable 'advanced' mode. Adds an 'advanced' tab" - " to settings for setting user mode and adds" - " channel modes in memo titles." - " This feature is currently not maintained.")) -parser.add_argument("--nohonk", - action="store_true", - help="Disables the honk soundeffect 🤡📣") +parser.add_argument( + "--server", "-s", metavar="ADDRESS", help="Specify server override. (legacy)" +) +parser.add_argument( + "--port", "-p", metavar="PORT", help="Specify port override. (legacy)" +) +parser.add_argument( + "--logging", + "-l", + metavar="LEVEL", + default="WARNING", + help=( + "Specify level of logging, possible values are:" + " CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET." + " (https://docs.python.org/3/library/logging.html)" + ), +) +parser.add_argument( + "--advanced", + action="store_true", + help=( + "Enable 'advanced' mode. Adds an 'advanced' tab" + " to settings for setting user mode and adds" + " channel modes in memo titles." + " This feature is currently not maintained." + ), +) +parser.add_argument( + "--nohonk", action="store_true", help="Disables the honk soundeffect 🤡📣" +) # Set logging config section, log level is in oppts. # Logger -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") # Handlers -file_handler = logging.FileHandler(os.path.join(_datadir, 'pesterchum.log')) +file_handler = logging.FileHandler(os.path.join(_datadir, "pesterchum.log")) stream_handler = logging.StreamHandler() # Format -formatter = logging.Formatter('%(asctime)s - %(module)s - %(levelname)s - %(message)s') +formatter = logging.Formatter("%(asctime)s - %(module)s - %(levelname)s - %(message)s") file_handler.setFormatter(formatter) stream_handler.setFormatter(formatter) # Add handlers @@ -119,7 +124,15 @@ PchumLog.addHandler(stream_handler) # Global variables BOTNAMES = [] CUSTOMBOTS = ["CALSPRITE", RANDNICK.upper()] -SERVICES = ["NICKSERV", "CHANSERV", "MEMOSERV", "OPERSERV", "HELPSERV", "HOSTSERV", "BOTSERV"] +SERVICES = [ + "NICKSERV", + "CHANSERV", + "MEMOSERV", + "OPERSERV", + "HELPSERV", + "HOSTSERV", + "BOTSERV", +] BOTNAMES.extend(CUSTOMBOTS) BOTNAMES.extend(SERVICES) # Save the main app. From here, we should be able to get everything else in @@ -137,44 +150,64 @@ _ARGUMENTS = parser.parse_args() try: # PyQt6, QtMultimedia is prefered. from PyQt6 import QtMultimedia - #print("Audio module is PyQt6 QtMultimedia.") + + # print("Audio module is PyQt6 QtMultimedia.") except ImportError: if ostools.isWin32() or ostools.isOSX(): # PyQt5 QtMultimedia has native backends for MacOS and Windows try: from PyQt5 import QtMultimedia - print("Using PyQt5 QtMultimedia as sound module. (fallback, PyQt6 QtMultimedia not availible)") + + print( + "Using PyQt5 QtMultimedia as sound module. (fallback, PyQt6 QtMultimedia not availible)" + ) except ImportError: try: try: # Mute pygame support print - os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" + os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide" except: pass import pygame - print("Using pygame as sound module. (fallback, PyQt6 QtMultimedia and PyQt5 QtMultimedia not availible)") + + print( + "Using pygame as sound module. (fallback, PyQt6 QtMultimedia and PyQt5 QtMultimedia not availible)" + ) except ImportError: - print("All possible audio modules failed to import." - "\nPossible audio modules in order of preference (Windows/MacOS): PyQt6 QtMultimedia > PyQt5 QtMultimedia > pygame") + print( + "All possible audio modules failed to import." + "\nPossible audio modules in order of preference (Windows/MacOS): PyQt6 QtMultimedia > PyQt5 QtMultimedia > pygame" + ) elif ostools.isLinux() or True: # PyQt5 QtMultimedia needs gstreamer on linux, so pygame is prefered. try: try: # Mute pygame support print - os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" + os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide" except: pass import pygame - print("Using pygame as sound module. (fallback, PyQt6 QtMultimedia not availible)") + + print( + "Using pygame as sound module. (fallback, PyQt6 QtMultimedia not availible)" + ) except ImportError: try: from PyQt5 import QtMultimedia - print("Using PyQt5 QtMultimedia as sound module. (fallback, PyQt6 QtMultimedia and pygame not availible)") - print("PyQt5 Multimedia will silently fail without GStreamer with relevant" - " plugins on Linux, pygame is prefered when using PyQt5.") + + print( + "Using PyQt5 QtMultimedia as sound module. (fallback, PyQt6 QtMultimedia and pygame not availible)" + ) + print( + "PyQt5 Multimedia will silently fail without GStreamer with relevant" + " plugins on Linux, pygame is prefered when using PyQt5." + ) except ImportError: - print("All possible audio modules failed to import." - "\nLPossible audio modules in order of preference (Linux/Unknown): PyQt6 QtMultimedia > pygame > PyQt5 QtMultimedia") + print( + "All possible audio modules failed to import." + "\nLPossible audio modules in order of preference (Linux/Unknown): PyQt6 QtMultimedia > pygame > PyQt5 QtMultimedia" + ) + class waitingMessageHolder(object): def __init__(self, mainwindow, **msgfuncs): @@ -183,11 +216,14 @@ class waitingMessageHolder(object): self.queue = list(msgfuncs.keys()) if len(self.queue) > 0: self.mainwindow.updateSystemTray() + def waitingHandles(self): return self.queue + def answerMessage(self): func = self.funcs[self.queue[0]] func() + def messageAnswered(self, handle): if handle not in self.queue: return @@ -195,15 +231,18 @@ class waitingMessageHolder(object): del self.funcs[handle] if len(self.queue) == 0: self.mainwindow.updateSystemTray() + def addMessage(self, handle, func): if handle not in self.funcs: self.queue.append(handle) self.funcs[handle] = func if len(self.queue) > 0: self.mainwindow.updateSystemTray() + def __len__(self): return len(self.queue) + class chumListing(QtWidgets.QTreeWidgetItem): def __init__(self, chum, window): super(chumListing, self).__init__([chum.handle]) @@ -212,23 +251,34 @@ class chumListing(QtWidgets.QTreeWidgetItem): self.handle = chum.handle self.setMood(Mood("offline")) self.status = None - self.setToolTip(0, "%s: %s" % (chum.handle, window.chumdb.getNotes(chum.handle))) + self.setToolTip( + 0, "%s: %s" % (chum.handle, window.chumdb.getNotes(chum.handle)) + ) + def setMood(self, mood): if hasattr(self.mainwindow, "chumList") and self.mainwindow.chumList.notify: - #print "%s -> %s" % (self.chum.mood.name(), mood.name()) - if self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNOUT and \ - mood.name() == "offline" and self.chum.mood.name() != "offline": - #print "OFFLINE NOTIFY: " + self.handle + # print "%s -> %s" % (self.chum.mood.name(), mood.name()) + if ( + self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNOUT + and mood.name() == "offline" + and self.chum.mood.name() != "offline" + ): + # print "OFFLINE NOTIFY: " + self.handle uri = self.mainwindow.theme["toasts/icon/signout"] - n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName, - "%s is Offline" % (self.handle), uri) + n = self.mainwindow.tm.Toast( + self.mainwindow.tm.appName, "%s is Offline" % (self.handle), uri + ) n.show() - elif self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNIN and \ - mood.name() != "offline" and self.chum.mood.name() == "offline": - #print "ONLINE NOTIFY: " + self.handle + elif ( + self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNIN + and mood.name() != "offline" + and self.chum.mood.name() == "offline" + ): + # print "ONLINE NOTIFY: " + self.handle uri = self.mainwindow.theme["toasts/icon/signin"] - n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName, - "%s is Online" % (self.handle), uri) + n = self.mainwindow.tm.Toast( + self.mainwindow.tm.appName, "%s is Online" % (self.handle), uri + ) n.show() login = False logout = False @@ -238,8 +288,10 @@ class chumListing(QtWidgets.QTreeWidgetItem): login = True self.chum.mood = mood self.updateMood(login=login, logout=logout) + def setColor(self, color): self.chum.color = color + def updateMood(self, unblock=False, login=False, logout=False): mood = self.chum.mood self.mood = mood @@ -251,37 +303,76 @@ class chumListing(QtWidgets.QTreeWidgetItem): else: self.setIcon(0, icon) try: - self.setForeground(0, QtGui.QBrush(QtGui.QColor(self.mainwindow.theme["main/chums/moods"][self.mood.name()]["color"]))) + self.setForeground( + 0, + QtGui.QBrush( + QtGui.QColor( + self.mainwindow.theme["main/chums/moods"][self.mood.name()][ + "color" + ] + ) + ), + ) except KeyError: - self.setForeground(0, QtGui.QBrush(QtGui.QColor(self.mainwindow.theme["main/chums/moods/chummy/color"]))) + self.setForeground( + 0, + QtGui.QBrush( + QtGui.QColor(self.mainwindow.theme["main/chums/moods/chummy/color"]) + ), + ) + def changeTheme(self, theme): icon = self.mood.icon(theme) self.setIcon(0, icon) try: - self.setForeground(0, QtGui.QBrush(QtGui.QColor((self.mainwindow.theme["main/chums/moods"][self.mood.name()]["color"])))) + self.setForeground( + 0, + QtGui.QBrush( + QtGui.QColor( + ( + self.mainwindow.theme["main/chums/moods"][self.mood.name()][ + "color" + ] + ) + ) + ), + ) except KeyError: - self.setForeground(0, QtGui.QBrush(QtGui.QColor((self.mainwindow.theme["main/chums/moods/chummy/color"])))) + self.setForeground( + 0, + QtGui.QBrush( + QtGui.QColor( + (self.mainwindow.theme["main/chums/moods/chummy/color"]) + ) + ), + ) + def login(self): self.setIcon(0, PesterIcon("themes/arrow_right.png")) self.status = "in" QtCore.QTimer.singleShot(5000, self.doneLogin) + def doneLogin(self): icon = self.mood.icon(self.mainwindow.theme) self.setIcon(0, icon) + def logout(self): self.setIcon(0, PesterIcon("themes/arrow_left.png")) self.status = "out" QtCore.QTimer.singleShot(5000, self.doneLogout) + def doneLogout(self): hideoff = self.mainwindow.config.hideOfflineChums() icon = self.mood.icon(self.mainwindow.theme) self.setIcon(0, icon) if hideoff and self.status and self.status == "out": self.mainwindow.chumList.takeItem(self) + def __lt__(self, cl): h1 = self.handle.lower() h2 = cl.handle.lower() - return (h1 < h2) + return h1 < h2 + class chumArea(RightClickTree): # This is the class that controls the actual main chumlist, I think. @@ -304,23 +395,39 @@ class chumArea(RightClickTree): self.groupMenu = QtWidgets.QMenu(self) self.canonMenu = QtWidgets.QMenu(self) self.optionsMenu = QtWidgets.QMenu(self) - self.pester = QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self) + self.pester = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/pester"], self + ) self.pester.triggered.connect(self.activateChum) - self.removechum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/removechum"], self) + self.removechum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/removechum"], self + ) self.removechum.triggered.connect(self.removeChum) - self.blockchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self) + self.blockchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self + ) self.blockchum.triggered.connect(self.blockChum) - self.logchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self) + self.logchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self + ) self.logchum.triggered.connect(self.openChumLogs) - self.reportchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/report"], self) + self.reportchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/report"], self + ) self.reportchum.triggered.connect(self.reportChum) self.findalts = QAction("Find Alts", self) self.findalts.triggered.connect(self.findAlts) - self.removegroup = QAction(self.mainwindow.theme["main/menus/rclickchumlist/removegroup"], self) + self.removegroup = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/removegroup"], self + ) self.removegroup.triggered.connect(self.removeGroup) - self.renamegroup = QAction(self.mainwindow.theme["main/menus/rclickchumlist/renamegroup"], self) + self.renamegroup = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/renamegroup"], self + ) self.renamegroup.triggered.connect(self.renameGroup) - self.notes = QAction(self.mainwindow.theme["main/menus/rclickchumlist/notes"], self) + self.notes = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/notes"], self + ) self.notes.triggered.connect(self.editNotes) self.optionsMenu.addAction(self.pester) @@ -328,7 +435,9 @@ class chumArea(RightClickTree): self.optionsMenu.addAction(self.notes) self.optionsMenu.addAction(self.blockchum) self.optionsMenu.addAction(self.removechum) - self.moveMenu = QtWidgets.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/movechum"], self) + self.moveMenu = QtWidgets.QMenu( + self.mainwindow.theme["main/menus/rclickchumlist/movechum"], self + ) self.optionsMenu.addMenu(self.moveMenu) self.optionsMenu.addAction(self.reportchum) self.moveGroupMenu() @@ -345,8 +454,8 @@ class chumArea(RightClickTree): self.canonMenu.addAction(self.findalts) self.initTheme(theme) - #self.sortItems() - #self.sortItems(1, QtCore.Qt.SortOrder.AscendingOrder) + # self.sortItems() + # self.sortItems(1, QtCore.Qt.SortOrder.AscendingOrder) self.setSortingEnabled(False) self.header().hide() self.setDropIndicatorShown(True) @@ -368,34 +477,36 @@ class chumArea(RightClickTree): return None text = str(self.currentItem().text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] if text == "Chums": return None elif text in self.groups: return self.groupMenu else: - #currenthandle = self.currentItem().chum.handle - - #if currenthandle in canon_handles: + # currenthandle = self.currentItem().chum.handle + + # if currenthandle in canon_handles: # return self.canonMenu - #else: + # else: return self.optionsMenu def startDrag(self, dropAction): -## Traceback (most recent call last): -## File "pesterchum.py", line 355, in startDrag -## mime.setData('application/x-item', '???') -## TypeErroreError: setData(self, str, Union[QByteArray, bytes, bytearray]): argument 2 has unexpected type 'str' + ## Traceback (most recent call last): + ## File "pesterchum.py", line 355, in startDrag + ## mime.setData('application/x-item', '???') + ## TypeErroreError: setData(self, str, Union[QByteArray, bytes, bytearray]): argument 2 has unexpected type 'str' try: # create mime data object mime = QtCore.QMimeData() - mime.setData('application/x-item', QtCore.QByteArray()) # Voodoo programming :"3 + mime.setData( + "application/x-item", QtCore.QByteArray() + ) # Voodoo programming :"3 # start drag drag = QtGui.QDrag(self) drag.setMimeData(mime) drag.exec(QtCore.Qt.DropAction.MoveAction) except: - logging.exception('') + logging.exception("") def dragMoveEvent(self, event): if event.mimeData().hasFormat("application/x-item"): @@ -405,24 +516,24 @@ class chumArea(RightClickTree): event.ignore() def dragEnterEvent(self, event): - if (event.mimeData().hasFormat('application/x-item')): + if event.mimeData().hasFormat("application/x-item"): event.accept() else: event.ignore() def dropEvent(self, event): - if (event.mimeData().hasFormat('application/x-item')): + if event.mimeData().hasFormat("application/x-item"): event.acceptProposedAction() else: event.ignore() return thisitem = str(event.source().currentItem().text(0)) if thisitem.rfind(" (") != -1: - thisitem = thisitem[0:thisitem.rfind(" (")] + thisitem = thisitem[0 : thisitem.rfind(" (")] # Drop item is a group thisitem = str(event.source().currentItem().text(0)) if thisitem.rfind(" (") != -1: - thisitem = thisitem[0:thisitem.rfind(" (")] + thisitem = thisitem[0 : thisitem.rfind(" (")] if thisitem == "Chums" or thisitem in self.groups: try: # PyQt6 @@ -430,10 +541,11 @@ class chumArea(RightClickTree): except AttributeError: # PyQt5 droppos = self.itemAt(event.pos()) - if not droppos: return + if not droppos: + return droppos = str(droppos.text(0)) if droppos.rfind(" ") != -1: - droppos = droppos[0:droppos.rfind(" ")] + droppos = droppos[0 : droppos.rfind(" ")] if droppos == "Chums" or droppos in self.groups: saveOpen = event.source().currentItem().isExpanded() try: @@ -442,8 +554,12 @@ class chumArea(RightClickTree): except AttributeError: # PyQt5 saveDrop = self.itemAt(event.pos()) - saveItem = self.takeTopLevelItem(self.indexOfTopLevelItem(event.source().currentItem())) - self.insertTopLevelItems(self.indexOfTopLevelItem(saveDrop)+1, [saveItem]) + saveItem = self.takeTopLevelItem( + self.indexOfTopLevelItem(event.source().currentItem()) + ) + self.insertTopLevelItems( + self.indexOfTopLevelItem(saveDrop) + 1, [saveItem] + ) if saveOpen: saveItem.setExpanded(True) @@ -451,7 +567,7 @@ class chumArea(RightClickTree): for i in range(self.topLevelItemCount()): text = str(self.topLevelItem(i).text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] gTemp.append([str(text), self.topLevelItem(i).isExpanded()]) self.mainwindow.config.saveGroups(gTemp) # Drop item is a chum @@ -467,14 +583,14 @@ class chumArea(RightClickTree): text = str(item.text(0)) # Figure out which group to drop into if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] if text == "Chums" or text in self.groups: group = text gitem = item else: ptext = str(item.parent().text(0)) if ptext.rfind(" ") != -1: - ptext = ptext[0:ptext.rfind(" ")] + ptext = ptext[0 : ptext.rfind(" ")] group = ptext gitem = item.parent() @@ -511,7 +627,7 @@ class chumArea(RightClickTree): else: text = str(currentGroup.text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] currentGroup = text self.moveMenu.clear() actGroup = QActionGroup(self) @@ -528,28 +644,36 @@ class chumArea(RightClickTree): if len([c for c in self.chums if c.handle == chum.handle]) != 0: return self.chums.append(chum) - if not (self.mainwindow.config.hideOfflineChums() and - chum.mood.name() == "offline"): + if not ( + self.mainwindow.config.hideOfflineChums() and chum.mood.name() == "offline" + ): chumLabel = chumListing(chum, self.mainwindow) self.addItem(chumLabel) - #self.topLevelItem(0).addChild(chumLabel) - #self.topLevelItem(0).sortChildren(0, QtCore.Qt.SortOrder.AscendingOrder) + # self.topLevelItem(0).addChild(chumLabel) + # self.topLevelItem(0).sortChildren(0, QtCore.Qt.SortOrder.AscendingOrder) def getChums(self, handle): - chums = self.findItems(handle, QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive) + chums = self.findItems( + handle, + QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, + ) return chums def showAllChums(self): for c in self.chums: chandle = c.handle - if not len(self.findItems(chandle, - QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive)): - #if True:# For if it doesn't work at all :/ + if not len( + self.findItems( + chandle, + QtCore.Qt.MatchFlag.MatchExactly + | QtCore.Qt.MatchFlag.MatchRecursive, + ) + ): + # if True:# For if it doesn't work at all :/ chumLabel = chumListing(c, self.mainwindow) self.addItem(chumLabel) self.sort() + def hideOfflineChums(self): for j in range(self.topLevelItemCount()): i = 0 @@ -561,9 +685,10 @@ class chumArea(RightClickTree): i += 1 listing = self.topLevelItem(j).child(i) self.sort() + def showAllGroups(self, first=False): if first: - for i,g in enumerate(self.groups): + for i, g in enumerate(self.groups): child_1 = QtWidgets.QTreeWidgetItem(["%s" % (g)]) self.addTopLevelItem(child_1) if self.openGroups[i]: @@ -573,9 +698,9 @@ class chumArea(RightClickTree): for i in range(self.topLevelItemCount()): text = str(self.topLevelItem(i).text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] curgroups.append(text) - for i,g in enumerate(self.groups): + for i, g in enumerate(self.groups): if g not in curgroups: child_1 = QtWidgets.QTreeWidgetItem(["%s" % (g)]) j = 0 @@ -589,40 +714,45 @@ class chumArea(RightClickTree): child_1.setExpanded(True) if self.mainwindow.config.showOnlineNumbers(): self.showOnlineNumbers() + def showOnlineNumbers(self): - if hasattr(self, 'groups'): + if hasattr(self, "groups"): self.hideOnlineNumbers() - totals = {'Chums': 0} - online = {'Chums': 0} + totals = {"Chums": 0} + online = {"Chums": 0} for g in self.groups: totals[str(g)] = 0 online[str(g)] = 0 for c in self.chums: yes = c.mood.name() != "offline" if c.group == "Chums": - totals[str(c.group)] = totals[str(c.group)]+1 + totals[str(c.group)] = totals[str(c.group)] + 1 if yes: - online[str(c.group)] = online[str(c.group)]+1 + online[str(c.group)] = online[str(c.group)] + 1 elif c.group in totals: - totals[str(c.group)] = totals[str(c.group)]+1 + totals[str(c.group)] = totals[str(c.group)] + 1 if yes: - online[str(c.group)] = online[str(c.group)]+1 + online[str(c.group)] = online[str(c.group)] + 1 else: - totals["Chums"] = totals["Chums"]+1 + totals["Chums"] = totals["Chums"] + 1 if yes: - online["Chums"] = online["Chums"]+1 + online["Chums"] = online["Chums"] + 1 for i in range(self.topLevelItemCount()): text = str(self.topLevelItem(i).text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] if text in online: - self.topLevelItem(i).setText(0, "%s (%i/%i)" % (text, online[text], totals[text])) + self.topLevelItem(i).setText( + 0, "%s (%i/%i)" % (text, online[text], totals[text]) + ) + def hideOnlineNumbers(self): for i in range(self.topLevelItemCount()): text = str(self.topLevelItem(i).text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] self.topLevelItem(i).setText(0, "%s" % (text)) + def hideEmptyGroups(self): i = 0 listing = self.topLevelItem(i) @@ -632,18 +762,20 @@ class chumArea(RightClickTree): else: i += 1 listing = self.topLevelItem(i) + @QtCore.pyqtSlot() def expandGroup(self): item = self.currentItem() text = str(item.text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] if text in self.groups: expand = item.isExpanded() self.mainwindow.config.expandGroup(text, not expand) + def addItem(self, chumLabel): - if hasattr(self, 'groups'): + if hasattr(self, "groups"): if chumLabel.chum.group not in self.groups: chumLabel.chum.group = "Chums" if "Chums" not in self.groups: @@ -652,12 +784,13 @@ class chumArea(RightClickTree): for i in range(self.topLevelItemCount()): text = str(self.topLevelItem(i).text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] curgroups.append(text) - if not self.findItems(chumLabel.handle, - QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive): - #if True:# For if it doesn't work at all :/ + if not self.findItems( + chumLabel.handle, + QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, + ): + # if True:# For if it doesn't work at all :/ if chumLabel.chum.group not in curgroups: child_1 = QtWidgets.QTreeWidgetItem(["%s" % (chumLabel.chum.group)]) i = 0 @@ -667,12 +800,14 @@ class chumArea(RightClickTree): break if g in curgroups: i += 1 - if self.openGroups[self.groups.index("%s" % (chumLabel.chum.group))]: + if self.openGroups[ + self.groups.index("%s" % (chumLabel.chum.group)) + ]: child_1.setExpanded(True) for i in range(self.topLevelItemCount()): text = str(self.topLevelItem(i).text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] if text == chumLabel.chum.group: break # Manual sorting @@ -690,34 +825,38 @@ class chumArea(RightClickTree): if fi > 0: while not bestj: for j in range(self.topLevelItem(i).childCount()): - if chums[fi-c] == str(self.topLevelItem(i).child(j).text(0)): + if chums[fi - c] == str( + self.topLevelItem(i).child(j).text(0) + ): bestj = j - bestname = chums[fi-c] + bestname = chums[fi - c] break c += 1 - if fi-c < 0: + if fi - c < 0: break if bestname: - self.topLevelItem(i).insertChild(bestj+1, chumLabel) + self.topLevelItem(i).insertChild(bestj + 1, chumLabel) else: self.topLevelItem(i).insertChild(bestj, chumLabel) - #sys.exit(0) + # sys.exit(0) self.topLevelItem(i).addChild(chumLabel) - else: # All other sorting + else: # All other sorting self.topLevelItem(i).addChild(chumLabel) self.sort() if self.mainwindow.config.showOnlineNumbers(): self.showOnlineNumbers() - else: # usually means this is now the trollslum - if not self.findItems(chumLabel.handle, - QtCore.Qt.MatchFlag.MatchExactly - | QtCore.Qt.MatchFlag.MatchRecursive): - #if True:# For if it doesn't work at all :/ + else: # usually means this is now the trollslum + if not self.findItems( + chumLabel.handle, + QtCore.Qt.MatchFlag.MatchExactly | QtCore.Qt.MatchFlag.MatchRecursive, + ): + # if True:# For if it doesn't work at all :/ self.topLevelItem(0).addChild(chumLabel) self.topLevelItem(0).sortChildren(0, QtCore.Qt.SortOrder.AscendingOrder) + def takeItem(self, chumLabel): r = None - if not hasattr(chumLabel, 'chum'): + if not hasattr(chumLabel, "chum"): return r for i in range(self.topLevelItemCount()): for j in range(self.topLevelItem(i).childCount()): @@ -729,27 +868,31 @@ class chumArea(RightClickTree): if self.mainwindow.config.showOnlineNumbers(): self.showOnlineNumbers() return r + def updateMood(self, handle, mood): hideoff = self.mainwindow.config.hideOfflineChums() chums = self.getChums(handle) oldmood = None if hideoff: - if mood.name() != "offline" and \ - len(chums) == 0 and \ - handle in [p.handle for p in self.chums]: - newLabel = chumListing([p for p in self.chums if p.handle == handle][0], self.mainwindow) + if ( + mood.name() != "offline" + and len(chums) == 0 + and handle in [p.handle for p in self.chums] + ): + newLabel = chumListing( + [p for p in self.chums if p.handle == handle][0], self.mainwindow + ) self.addItem(newLabel) - #self.sortItems() + # self.sortItems() chums = [newLabel] - elif mood.name() == "offline" and \ - len(chums) > 0: + elif mood.name() == "offline" and len(chums) > 0: for c in chums: - if (hasattr(c, 'mood')): + if hasattr(c, "mood"): c.setMood(mood) - #self.takeItem(c) + # self.takeItem(c) chums = [] for c in chums: - if (hasattr(c, 'mood')): + if hasattr(c, "mood"): oldmood = c.mood c.setMood(mood) if self.mainwindow.config.sortMethod() == 1: @@ -760,28 +903,34 @@ class chumArea(RightClickTree): if self.mainwindow.config.showOnlineNumbers(): self.showOnlineNumbers() return oldmood + def updateColor(self, handle, color): chums = self.findItems(handle, QtCore.Qt.MatchFlag.MatchExactly) for c in chums: c.setColor(color) + def initTheme(self, theme): self.resize(*theme["main/chums/size"]) self.move(*theme["main/chums/loc"]) if "main/chums/scrollbar" in theme: - self.setStyleSheet("QListWidget { %s } \ + self.setStyleSheet( + "QListWidget { %s } \ QScrollBar { %s } \ QScrollBar::handle { %s } \ QScrollBar::add-line { %s } \ QScrollBar::sub-line { %s } \ QScrollBar:up-arrow { %s } \ - QScrollBar:down-arrow { %s }" % \ - (theme["main/chums/style"], \ - theme["main/chums/scrollbar/style"], \ - theme["main/chums/scrollbar/handle"], \ - theme["main/chums/scrollbar/downarrow"], \ - theme["main/chums/scrollbar/uparrow"], \ - theme["main/chums/scrollbar/uarrowstyle"], \ - theme["main/chums/scrollbar/darrowstyle"] )) + QScrollBar:down-arrow { %s }" + % ( + theme["main/chums/style"], + theme["main/chums/scrollbar/style"], + theme["main/chums/scrollbar/handle"], + theme["main/chums/scrollbar/downarrow"], + theme["main/chums/scrollbar/uparrow"], + theme["main/chums/scrollbar/uarrowstyle"], + theme["main/chums/scrollbar/darrowstyle"], + ) + ) else: self.setStyleSheet(theme["main/chums/style"]) self.pester.setText(theme["main/menus/rclickchumlist/pester"]) @@ -793,13 +942,14 @@ class chumArea(RightClickTree): self.removegroup.setText(theme["main/menus/rclickchumlist/removegroup"]) self.renamegroup.setText(theme["main/menus/rclickchumlist/renamegroup"]) self.moveMenu.setTitle(theme["main/menus/rclickchumlist/movechum"]) + def changeTheme(self, theme): self.initTheme(theme) chumlistings = [] for i in range(self.topLevelItemCount()): for j in range(self.topLevelItem(i).childCount()): chumlistings.append(self.topLevelItem(i).child(j)) - #chumlistings = [self.item(i) for i in range(0, self.count())] + # chumlistings = [self.item(i) for i in range(0, self.count())] for c in chumlistings: c.changeTheme(theme) @@ -811,13 +961,14 @@ class chumArea(RightClickTree): def sort(self): if self.mainwindow.config.sortMethod() == 2: - pass # Do nothing!!!!! :OOOOOOO It's manual, bitches + pass # Do nothing!!!!! :OOOOOOO It's manual, bitches elif self.mainwindow.config.sortMethod() == 1: for i in range(self.topLevelItemCount()): self.moodSort(i) else: for i in range(self.topLevelItemCount()): self.topLevelItem(i).sortChildren(0, QtCore.Qt.SortOrder.AscendingOrder) + def moodSort(self, group): scrollPos = self.verticalScrollBar().sliderPosition() chums = [] @@ -825,7 +976,13 @@ class chumArea(RightClickTree): while listing is not None: chums.append(self.topLevelItem(group).takeChild(0)) listing = self.topLevelItem(group).child(0) - chums.sort(key=lambda x: ((999 if x.chum.mood.value() == 2 else x.chum.mood.value()), x.chum.handle), reverse=False) + chums.sort( + key=lambda x: ( + (999 if x.chum.mood.value() == 2 else x.chum.mood.value()), + x.chum.handle, + ), + reverse=False, + ) for c in chums: self.topLevelItem(group).addChild(c) self.verticalScrollBar().setSliderPosition(scrollPos) @@ -833,11 +990,13 @@ class chumArea(RightClickTree): @QtCore.pyqtSlot() def activateChum(self): self.itemActivated.emit(self.currentItem(), 0) + @QtCore.pyqtSlot() - def removeChum(self, handle = None): + def removeChum(self, handle=None): if handle: clistings = self.getChums(handle) - if len(clistings) <= 0: return + if len(clistings) <= 0: + return for c in clistings: self.setCurrentItem(c) if not self.currentItem(): @@ -847,68 +1006,78 @@ class chumArea(RightClickTree): self.removeChumSignal.emit(self.currentItem().chum.handle) oldlist = self.takeItem(self.currentItem()) del oldlist + @QtCore.pyqtSlot() def blockChum(self): currentChum = self.currentItem() if not currentChum: return self.blockChumSignal.emit(self.currentItem().chum.handle) + @QtCore.pyqtSlot() def reportChum(self): currentChum = self.currentItem() if not currentChum: return self.mainwindow.reportChum(self.currentItem().chum.handle) + @QtCore.pyqtSlot() def findAlts(self): currentChum = self.currentItem() if not currentChum: return - self.mainwindow.sendMessage.emit("ALT %s" % (currentChum.chum.handle) , "calSprite") + self.mainwindow.sendMessage.emit( + "ALT %s" % (currentChum.chum.handle), "calSprite" + ) + @QtCore.pyqtSlot() def openChumLogs(self): currentChum = self.currentItem() if not currentChum: return currentChum = currentChum.text(0) - self.pesterlogviewer = PesterLogViewer(currentChum, - self.mainwindow.config, - self.mainwindow.theme, - self.mainwindow) + self.pesterlogviewer = PesterLogViewer( + currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow + ) self.pesterlogviewer.rejected.connect(self.closeActiveLog) self.pesterlogviewer.show() self.pesterlogviewer.raise_() self.pesterlogviewer.activateWindow() + @QtCore.pyqtSlot() def closeActiveLog(self): self.pesterlogviewer.close() self.pesterlogviewer = None + @QtCore.pyqtSlot() def editNotes(self): currentChum = self.currentItem() if not currentChum: return - (notes, ok) = QtWidgets.QInputDialog.getText(self, - "Notes", - "Enter your notes...") + (notes, ok) = QtWidgets.QInputDialog.getText( + self, "Notes", "Enter your notes..." + ) if ok: notes = str(notes) self.mainwindow.chumdb.setNotes(currentChum.handle, notes) currentChum.setToolTip(0, "%s: %s" % (currentChum.handle, notes)) + @QtCore.pyqtSlot() def renameGroup(self): - if not hasattr(self, 'renamegroupdialog'): + if not hasattr(self, "renamegroupdialog"): self.renamegroupdialog = None if not self.renamegroupdialog: - (gname, ok) = QtWidgets.QInputDialog.getText(self, - "Rename Group", - "Enter a new name for the group:") + (gname, ok) = QtWidgets.QInputDialog.getText( + self, "Rename Group", "Enter a new name for the group:" + ) if ok: gname = str(gname) if re.search("[^A-Za-z0-9_\s]", gname) is not None: msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.mainwindow.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" + % self.mainwindow.theme["main/defaultwindow/style"] + ) msgbox.setInformativeText("THIS IS NOT A VALID GROUP NAME") msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msgbox.exec() @@ -922,7 +1091,7 @@ class chumArea(RightClickTree): expanded = currentGroup.isExpanded() text = str(currentGroup.text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] self.mainwindow.config.delGroup(text) self.mainwindow.config.addGroup(gname, expanded) gTemp = self.mainwindow.config.getGroups() @@ -930,11 +1099,14 @@ class chumArea(RightClickTree): self.openGroups = [g[1] for g in gTemp] for i in range(currentGroup.childCount()): currentGroup.child(i).chum.group = gname - self.mainwindow.chumdb.setGroup(currentGroup.child(i).chum.handle, gname) + self.mainwindow.chumdb.setGroup( + currentGroup.child(i).chum.handle, gname + ) currentGroup.setText(0, gname) if self.mainwindow.config.showOnlineNumbers(): self.showOnlineNumbers() self.renamegroupdialog = None + @QtCore.pyqtSlot() def removeGroup(self): currentGroup = self.currentItem() @@ -942,7 +1114,7 @@ class chumArea(RightClickTree): return text = str(currentGroup.text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] + text = text[0 : text.rfind(" (")] self.mainwindow.config.delGroup(text) gTemp = self.mainwindow.config.getGroups() self.groups = [g[0] for g in gTemp] @@ -959,6 +1131,7 @@ class chumArea(RightClickTree): self.takeItem(chumLabel) self.addItem(chumLabel) self.takeTopLevelItem(i) + @QtCore.pyqtSlot(QAction) def moveToGroup(self, item): if not item: @@ -972,14 +1145,15 @@ class chumArea(RightClickTree): self.takeItem(chumLabel) self.addItem(chumLabel) - removeChumSignal = QtCore.pyqtSignal('QString') - blockChumSignal = QtCore.pyqtSignal('QString') + removeChumSignal = QtCore.pyqtSignal("QString") + blockChumSignal = QtCore.pyqtSignal("QString") + class trollSlum(chumArea): unblockChumSignal = QtCore.pyqtSignal() def __init__(self, trolls, mainwindow, parent=None): - #~super(trollSlum, self).__init__(parent) + # ~super(trollSlum, self).__init__(parent) # TODO: Rework inheritance here. QtWidgets.QTreeWidgetItem.__init__(self, parent) self.mainwindow = mainwindow @@ -1001,18 +1175,22 @@ class trollSlum(chumArea): self.setIndentation(0) self.optionsMenu = QtWidgets.QMenu(self) - self.unblockchum = QAction(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self) + self.unblockchum = QAction( + self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self + ) self.unblockchum.triggered.connect(self.unblockChumSignal) self.optionsMenu.addAction(self.unblockchum) - #self.sortItems() + # self.sortItems() + def contextMenuEvent(self, event): - #fuckin Qt + # fuckin Qt if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse: listing = self.itemAt(event.pos()) self.setCurrentItem(listing) if self.currentItem().text(0) != "": self.optionsMenu.popup(event.globalPos()) + def changeTheme(self, theme): self.setStyleSheet(theme["main/trollslum/chumroll/style"]) self.removechum.setText(theme["main/menus/rclickchumlist/removechum"]) @@ -1026,7 +1204,8 @@ class trollSlum(chumArea): # TypeError: connect() failed between triggered(bool) and unblockChumSignal() # I'm not sure why this was here in the first place- # Does removing it break anything else...? - #unblockChumSignal = QtCore.pyqtSignal('QString') + # unblockChumSignal = QtCore.pyqtSignal('QString') + class TrollSlumWindow(QtWidgets.QFrame): def __init__(self, trolls, mainwindow, parent=None): @@ -1060,39 +1239,47 @@ class TrollSlumWindow(QtWidgets.QFrame): if not self.parent(): self.setWindowTitle(theme["main/menus/profile/block"]) self.setWindowIcon(self.mainwindow.windowIcon()) + def changeTheme(self, theme): self.initTheme(theme) self.trollslum.changeTheme(theme) # move unblocked trolls from slum to chumarea + def closeEvent(self, event): self.mainwindow.closeTrollSlum() def updateMood(self, handle, mood): self.trollslum.updateMood(handle, mood) + def addTroll(self, chum): self.trollslum.addChum(chum) + def removeTroll(self, handle): self.trollslum.removeChum(handle) + @QtCore.pyqtSlot() def removeCurrentTroll(self): currentListing = self.trollslum.currentItem() - if not currentListing or not hasattr(currentListing, 'chum'): + if not currentListing or not hasattr(currentListing, "chum"): return self.unblockChumSignal.emit(currentListing.chum.handle) + @QtCore.pyqtSlot() def addTrollWindow(self): - if not hasattr(self, 'addtrolldialog'): + if not hasattr(self, "addtrolldialog"): self.addtrolldialog = None if self.addtrolldialog: return self.addtrolldialog = QtWidgets.QInputDialog(self) - (handle, ok) = self.addtrolldialog.getText(self, - "Add Troll", - "Enter Troll Handle:") + (handle, ok) = self.addtrolldialog.getText( + self, "Add Troll", "Enter Troll Handle:" + ) if ok: handle = str(handle) - if not (PesterProfile.checkLength(handle) and - PesterProfile.checkValid(handle)[0]): + if not ( + PesterProfile.checkLength(handle) + and PesterProfile.checkValid(handle)[0] + ): errormsg = QtWidgets.QErrorMessage(self) errormsg.showMessage("THIS IS NOT A VALID CHUMTAG!") self.addchumdialog = None @@ -1101,17 +1288,22 @@ class TrollSlumWindow(QtWidgets.QFrame): self.blockChumSignal.emit(handle) self.addtrolldialog = None - blockChumSignal = QtCore.pyqtSignal('QString') - unblockChumSignal = QtCore.pyqtSignal('QString') + blockChumSignal = QtCore.pyqtSignal("QString") + unblockChumSignal = QtCore.pyqtSignal("QString") + class PesterWindow(MovingWindow): disconnectIRC = QtCore.pyqtSignal() - sendMessage = QtCore.pyqtSignal('QString', 'QString') + sendMessage = QtCore.pyqtSignal("QString", "QString") def __init__(self, options, parent=None, app=None): - super(PesterWindow, self).__init__(None, - (QtCore.Qt.WindowType.CustomizeWindowHint - | QtCore.Qt.WindowType.FramelessWindowHint)) + super(PesterWindow, self).__init__( + None, + ( + QtCore.Qt.WindowType.CustomizeWindowHint + | QtCore.Qt.WindowType.FramelessWindowHint + ), + ) # For debugging _CONSOLE_ENV.PAPP = self @@ -1148,28 +1340,36 @@ class PesterWindow(MovingWindow): self.userprofile = userProfile(self.config.defaultprofile()) self.theme = self.userprofile.getTheme() else: - self.userprofile = userProfile(PesterProfile("pesterClient%d" - % (random.randint(100,999)), - QtGui.QColor("black"), - Mood(0))) + self.userprofile = userProfile( + PesterProfile( + "pesterClient%d" % (random.randint(100, 999)), + QtGui.QColor("black"), + Mood(0), + ) + ) self.theme = self.userprofile.getTheme() except Exception as e: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Icon.Information) msgBox.setWindowTitle(":(") - msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links - self.filename = _datadir+"pesterchum.js" - msgBox.setText("

A profile error occured, " - "trying to switch to default pesterClient profile." - "

%s<\h3><\html>" % e) + msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links + self.filename = _datadir + "pesterchum.js" + msgBox.setText( + "

A profile error occured, " + "trying to switch to default pesterClient profile." + "

%s<\h3><\html>" % e + ) PchumLog.critical(e) msgBox.exec() - self.userprofile = userProfile(PesterProfile("pesterClient%d" - % (random.randint(100,999)), - QtGui.QColor("black"), - Mood(0))) + self.userprofile = userProfile( + PesterProfile( + "pesterClient%d" % (random.randint(100, 999)), + QtGui.QColor("black"), + Mood(0), + ) + ) self.theme = self.userprofile.getTheme() - + # karxi: For the record, these are set via commandline arguments. By # default, they aren't usable any other way - you can't set them via # the config files. @@ -1178,7 +1378,7 @@ class PesterWindow(MovingWindow): # # This was almost certainly intentional. if "advanced" in options: - self.advanced = options["advanced"] + self.advanced = options["advanced"] else: self.advanced = False if "server" in options: @@ -1186,7 +1386,7 @@ class PesterWindow(MovingWindow): if "port" in options: self.portOverride = options["port"] if "honk" in options: - self.honk = options["honk"] + self.honk = options["honk"] else: self.honk = True self.modes = "" @@ -1204,11 +1404,16 @@ class PesterWindow(MovingWindow): themeWarning.exec() self.theme = pesterTheme("pesterchum") - extraToasts = {'default': PesterToast} + extraToasts = {"default": PesterToast} if pytwmn.confExists(): - extraToasts['twmn'] = pytwmn.Notification - self.tm = PesterToastMachine(self, lambda: self.theme["main/windowtitle"], on=self.config.notify(), - type=self.config.notifyType(), extras=extraToasts) + extraToasts["twmn"] = pytwmn.Notification + self.tm = PesterToastMachine( + self, + lambda: self.theme["main/windowtitle"], + on=self.config.notify(), + type=self.config.notifyType(), + extras=extraToasts, + ) self.tm.run() self.chatlog = PesterLog(self.profile().handle, self) @@ -1231,7 +1436,9 @@ class PesterWindow(MovingWindow): opts.triggered.connect(self.openOpts) exitaction = QAction(self.theme["main/menus/client/exit"], self) self.exitaction = exitaction - exitaction.triggered.connect(self.killApp, QtCore.Qt.ConnectionType.QueuedConnection) + exitaction.triggered.connect( + self.killApp, QtCore.Qt.ConnectionType.QueuedConnection + ) userlistaction = QAction(self.theme["main/menus/client/userlist"], self) self.userlistaction = userlistaction userlistaction.triggered.connect(self.showAllUsers) @@ -1251,33 +1458,41 @@ class PesterWindow(MovingWindow): self.menu.setObjectName("mainmenu") if self.theme.has_key("main/menus/client/console"): - self.console = AttrDict(dict( - window = None, - action = QAction(self.theme["main/menus/client/console"], self), - is_open = False - )) + self.console = AttrDict( + dict( + window=None, + action=QAction(self.theme["main/menus/client/console"], self), + is_open=False, + ) + ) else: - self.console = AttrDict(dict( - window = None, - action = QAction("Console", self), - is_open = False - )) - self.console.shortcuts = AttrDict(dict( - conkey = QShortcut(QtGui.QKeySequence("Ctrl+`"), self, - context=QtCore.Qt.ShortcutContext.ApplicationShortcut), - curwgt = QShortcut(QtGui.QKeySequence("Ctrl+Alt+w"), self, - context=QtCore.Qt.ShortcutContext.ApplicationShortcut) - )) + self.console = AttrDict( + dict(window=None, action=QAction("Console", self), is_open=False) + ) + self.console.shortcuts = AttrDict( + dict( + conkey=QShortcut( + QtGui.QKeySequence("Ctrl+`"), + self, + context=QtCore.Qt.ShortcutContext.ApplicationShortcut, + ), + curwgt=QShortcut( + QtGui.QKeySequence("Ctrl+Alt+w"), + self, + context=QtCore.Qt.ShortcutContext.ApplicationShortcut, + ), + ) + ) self.console.action.triggered.connect(self.toggleConsole) # Make sure the shortcut works anywhere. # karxi: There's something wrong with the inheritance scheme here. - #~self.console.shortcuts.conkey.setContext(QtCore.Qt.ShortcutContext.ApplicationShortcut) + # ~self.console.shortcuts.conkey.setContext(QtCore.Qt.ShortcutContext.ApplicationShortcut) self.console.shortcuts.conkey.activated.connect(self.toggleConsole) - #~# Use new-style connections - #~self.console.shortcut.activated.connect(self.toggleConsole) + # ~# Use new-style connections + # ~self.console.shortcut.activated.connect(self.toggleConsole) # Apparently those can crash sometimes...c'est la vie. Can't use 'em. - #~self.connect(self.console.shortcuts.curwgt, - #~ QtCore.SIGNAL('activate()'), self.console. + # ~self.connect(self.console.shortcuts.curwgt, + # ~ QtCore.SIGNAL('activate()'), self.console. self.console.is_open = False filemenu = self.menu.addMenu(self.theme["main/menus/client/_name"]) @@ -1332,16 +1547,18 @@ class PesterWindow(MovingWindow): self.aboutAction.triggered.connect(self.aboutPesterchum) # Because I can't expect all themes to have this included. - #if self.theme.has_key("main/menus/help/reportbug"): + # if self.theme.has_key("main/menus/help/reportbug"): try: - self.reportBugAction = QAction(self.theme["main/menus/help/reportbug"], self) + self.reportBugAction = QAction( + self.theme["main/menus/help/reportbug"], self + ) except: self.reportBugAction = QAction("REPORT BUG", self) try: self.xyzRulesAction = QAction(self.theme["main/menus/help/rules"], self) except: self.xyzRulesAction = QAction("RULES", self) - + self.reportBugAction.triggered.connect(self.reportBug) self.xyzRulesAction.triggered.connect(self.xyzRules) helpmenu = self.menu.addMenu(self.theme["main/menus/help/_name"]) @@ -1364,11 +1581,15 @@ class PesterWindow(MovingWindow): chums = [PesterProfile(c, chumdb=self.chumdb) for c in set(self.config.chums())] self.chumList = chumArea(chums, self) - self.chumList.itemActivated[QtWidgets.QTreeWidgetItem, int].connect(self.pesterSelectedChum) - self.chumList.removeChumSignal['QString'].connect(self.removeChum) - self.chumList.blockChumSignal['QString'].connect(self.blockChum) + self.chumList.itemActivated[QtWidgets.QTreeWidgetItem, int].connect( + self.pesterSelectedChum + ) + self.chumList.removeChumSignal["QString"].connect(self.removeChum) + self.chumList.blockChumSignal["QString"].connect(self.blockChum) - self.addChumButton = QtWidgets.QPushButton(self.theme["main/addchum/text"], self) + self.addChumButton = QtWidgets.QPushButton( + self.theme["main/addchum/text"], self + ) self.addChumButton.setObjectName("addchumbtn") self.addChumButton.clicked.connect(self.addChumWindow) self.pesterButton = QtWidgets.QPushButton(self.theme["main/pester/text"], self) @@ -1380,7 +1601,9 @@ class PesterWindow(MovingWindow): self.moodsLabel = QtWidgets.QLabel(self.theme["main/moodlabel/text"], self) self.moodsLabel.setObjectName("moodlabel") - self.mychumhandleLabel = QtWidgets.QLabel(self.theme["main/mychumhandle/label/text"], self) + self.mychumhandleLabel = QtWidgets.QLabel( + self.theme["main/mychumhandle/label/text"], self + ) self.mychumhandleLabel.setObjectName("myhandlelabel") self.mychumhandle = QtWidgets.QPushButton(self.profile().handle, self) self.mychumhandle.setFlat(True) @@ -1395,28 +1618,30 @@ class PesterWindow(MovingWindow): self.initTheme(self.theme) - #self.mychumhandleLabel.setStyleSheet("QLabel {);};") + # self.mychumhandleLabel.setStyleSheet("QLabel {);};") self.hide() - + self.waitingMessages = waitingMessageHolder(self) - self.idler = AttrDict(dict( + self.idler = AttrDict( + dict( # autoidle - auto = False, + auto=False, # setidle - manual = False, + manual=False, # idlethreshold - threshold = 60*self.config.idleTime(), + threshold=60 * self.config.idleTime(), # idleaction - action = self.idleaction, + action=self.idleaction, # idletimer - timer = QtCore.QTimer(self), + timer=QtCore.QTimer(self), # idleposition - pos = QtGui.QCursor.pos(), + pos=QtGui.QCursor.pos(), # idletime - time = 0 - )) + time=0, + ) + ) self.idler.timer.timeout.connect(self.checkIdle) self.idler.timer.start(1000) @@ -1424,13 +1649,13 @@ class PesterWindow(MovingWindow): self.changeProfile() # Fuck you some more OSX leopard! >:( - #if not ostools.isOSXLeopard(): + # if not ostools.isOSXLeopard(): # QtCore.QTimer.singleShot(1000, self.mspacheck) - self.pcUpdate['QString', 'QString'].connect(self.updateMsg) + self.pcUpdate["QString", "QString"].connect(self.updateMsg) - self.mychumhandleLabel.adjustSize() # Required so "CHUMHANDLE:" regardless of style-sheet. - self.moodsLabel.adjustSize() # Required so "MOOD:" regardless of style-sheet. + self.mychumhandleLabel.adjustSize() # Required so "CHUMHANDLE:" regardless of style-sheet. + self.moodsLabel.adjustSize() # Required so "MOOD:" regardless of style-sheet. self.chooseServerAskedToReset = False self.chooseServer() @@ -1448,12 +1673,12 @@ class PesterWindow(MovingWindow): # Client --> Server pings self.pingtimer = QtCore.QTimer() self.pingtimer.timeout.connect(self.checkPing) - self.sincerecv = 0 # Time since last recv + self.sincerecv = 0 # Time since last recv self.lastCheckPing = None - + @QtCore.pyqtSlot(QString, QString) def updateMsg(self, ver, url): - if not hasattr(self, 'updatemenu'): + if not hasattr(self, "updatemenu"): self.updatemenu = None if not self.updatemenu: self.updatemenu = UpdatePesterchum(ver, url, self) @@ -1477,10 +1702,10 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot() def checkPing(self): - '''Check if server is alive on app level, - this function is called every 15sec''' + """Check if server is alive on app level, + this function is called every 15sec""" # Return without irc - if hasattr(self.parent, 'irc') == False: + if hasattr(self.parent, "irc") == False: self.lastCheckPing = None self.sincerecv = 0 return @@ -1490,12 +1715,16 @@ class PesterWindow(MovingWindow): timeDif = abs(currentTime - self.lastCheckPing) if timeDif > 180: # default UnrealIRCd ping timeout time. # 180 is the default UnrealIRCd ping timeout time. - PchumLog.warning(("Possible desync, system time changed by %s " - "seconds since last check. abs(%s - %s)") - % (timeDif, currentTime, self.lastCheckPing)) + PchumLog.warning( + ( + "Possible desync, system time changed by %s " + "seconds since last check. abs(%s - %s)" + ) + % (timeDif, currentTime, self.lastCheckPing) + ) self.sincerecv = 85 # Allows one more ping attempt before disconnect. self.lastCheckPing = time.time() - + # Presume connection is dead after 90 seconds of silence. if self.sincerecv >= 90: self.disconnectIRC.emit() @@ -1504,28 +1733,28 @@ class PesterWindow(MovingWindow): if self.sincerecv >= 45: if self.parent.irc.unresponsive == False: self.parent.irc.unresponsive = True - self.parent.showLoading(self.parent.widget, - "S3RV3R NOT R3SPOND1NG >:?") + self.parent.showLoading(self.parent.widget, "S3RV3R NOT R3SPOND1NG >:?") self.show() self.setFocus() else: self.parent.irc.unresponsive = False - if hasattr(self, 'loadingscreen'): + if hasattr(self, "loadingscreen"): if self.loadingscreen != None: PchumLog.info("Server alive !! :O") self.loadingscreen.done(QtWidgets.QDialog.DialogCode.Accepted) self.loadingscreen = None - + # Send a ping if it's been 30 seconds since we've heard from the server. if self.sincerecv >= 30: self.pingServer.emit() - self.sincerecv += 5 # Not updating too frequently is better for performance. + self.sincerecv += 5 # Not updating too frequently is better for performance. def profile(self): return self.userprofile.chat + def closeConversations(self, switch=False): - if not hasattr(self, 'tabconvo'): + if not hasattr(self, "tabconvo"): self.tabconvo = None if self.tabconvo: self.tabconvo.close() @@ -1544,6 +1773,7 @@ class PesterWindow(MovingWindow): m.close() else: m.sendtime() + def paintEvent(self, event): try: self.backgroundImage @@ -1558,30 +1788,31 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot() def closeToTray(self): # I'm just gonna include a toast here to make sure people don't get confused. :'3 - t = self.tm.Toast("Notice:", - "Pesterchum has been minimized to your tray.") + t = self.tm.Toast("Notice:", "Pesterchum has been minimized to your tray.") t.show() self.hide() self.closeToTraySignal.emit() + def closeEvent(self, event): - if hasattr(self, 'trollslum') and self.trollslum: + if hasattr(self, "trollslum") and self.trollslum: self.trollslum.close() try: setting = self.config.closeAction() except: - logging.exception('') + logging.exception("") setting = 0 - if setting == 0: # minimize to taskbar + if setting == 0: # minimize to taskbar self.showMinimized() - elif setting == 1: # minimize to tray + elif setting == 1: # minimize to tray self.closeToTray() - elif setting == 2: # quit + elif setting == 2: # quit self.closeConversations() self.closeSignal.emit() event.accept() + def newMessage(self, handle, msg): if handle in self.config.getBlocklist(): - #yeah suck on this + # yeah suck on this self.sendMessage.emit("PESTERCHUM:BLOCKED", handle) return # notify @@ -1592,10 +1823,9 @@ class PesterWindow(MovingWindow): elif not self.config.notifyOptions() & self.config.NEWCONVO: if msg[:11] != "PESTERCHUM:": if handle.upper() not in BOTNAMES: - t = self.tm.Toast("From: %s" % handle, - re.sub("", - "", - msg)) + t = self.tm.Toast( + "From: %s" % handle, re.sub("", "", msg) + ) t.show() else: if msg == "PESTERCHUM:CEASE": @@ -1608,7 +1838,7 @@ class PesterWindow(MovingWindow): t = self.tm.Toast("Unblocked", handle) t.show() if handle not in self.convos: - if msg == "PESTERCHUM:CEASE": # ignore cease after we hang up + if msg == "PESTERCHUM:CEASE": # ignore cease after we hang up return matchingChums = [c for c in self.chumList.chums if c.handle == handle] if len(matchingChums) > 0: @@ -1628,6 +1858,7 @@ class PesterWindow(MovingWindow): self.ceasesound.play() else: self.alarm.play() + def newMemoMsg(self, chan, handle, msg): if chan not in self.memos: # silently ignore in case we forgot to /part @@ -1649,7 +1880,7 @@ class PesterWindow(MovingWindow): mentioned = False m = convertTags(msg, "text") if m.find(":") <= 3: - m = m[m.find(":"):] + m = m[m.find(":") :] for search in self.userprofile.getMentions(): if re.search(search, m): mentioned = True @@ -1666,9 +1897,9 @@ class PesterWindow(MovingWindow): self.namesound.play() return if not memo.notifications_muted: - if self.honk and re.search(r"\bhonk\b", - convertTags(msg, "text"), - re.I): + if self.honk and re.search( + r"\bhonk\b", convertTags(msg, "text"), re.I + ): # TODO: I've got my eye on you, Gamzee. self.honksound.play() elif self.config.memoPing() or memo.always_beep: @@ -1686,8 +1917,9 @@ class PesterWindow(MovingWindow): oldmood = self.chumList.updateMood(handle, mood) if handle in self.convos: self.convos[handle].updateMood(mood, old=oldmood) - if hasattr(self, 'trollslum') and self.trollslum: + if hasattr(self, "trollslum") and self.trollslum: self.trollslum.updateMood(handle, mood) + def newConversation(self, chum, initiated=True): if type(chum) in [str, str]: matchingChums = [c for c in self.chumList.chums if c.handle == chum] @@ -1709,8 +1941,10 @@ class PesterWindow(MovingWindow): self.tabconvo.show() else: convoWindow = PesterConvo(chum, initiated, self) - convoWindow.messageSent['QString', 'QString'].connect(self.sendMessage['QString', 'QString']) - convoWindow.windowClosed['QString'].connect(self.closeConvo) + convoWindow.messageSent["QString", "QString"].connect( + self.sendMessage["QString", "QString"] + ) + convoWindow.windowClosed["QString"].connect(self.closeConvo) self.convos[chum.handle] = convoWindow if str(chum.handle).upper() in BOTNAMES: convoWindow.toggleQuirks(True) @@ -1724,6 +1958,7 @@ class PesterWindow(MovingWindow): def createTabWindow(self): self.tabconvo = PesterTabWindow(self) self.tabconvo.windowClosed.connect(self.tabsClosed) + def createMemoTabWindow(self): self.tabmemo = MemoTabWindow(self) self.tabmemo.windowClosed.connect(self.memoTabsClosed) @@ -1756,9 +1991,10 @@ class PesterWindow(MovingWindow): # No error, let's see if we're actually focused. if focused: PchumLog.debug( - "{0!r} is in focus (parent: {1!r}); hiding".format( - wgt, wgt.parent()) - ) + "{0!r} is in focus (parent: {1!r}); hiding".format( + wgt, wgt.parent() + ) + ) # This widget, a child of our console, has focus. # So...the console has focus. # Set focus to the text input just for good measure. @@ -1810,16 +2046,20 @@ class PesterWindow(MovingWindow): else: memoWindow = PesterMemo(channel, timestr, self, None) # connect signals - self.inviteOnlyChan['QString'].connect(memoWindow.closeInviteOnly) - self.forbiddenChan['QString', 'QString'].connect(memoWindow.closeForbidden) - memoWindow.messageSent['QString', 'QString'].connect(self.sendMessage['QString', 'QString']) - memoWindow.windowClosed['QString'].connect(self.closeMemo) - self.namesUpdated['QString'].connect(memoWindow.namesUpdated) - self.modesUpdated['QString', 'QString'].connect(memoWindow.modesUpdated) - self.userPresentSignal['QString', 'QString', 'QString'].connect(memoWindow.userPresentChange) + self.inviteOnlyChan["QString"].connect(memoWindow.closeInviteOnly) + self.forbiddenChan["QString", "QString"].connect(memoWindow.closeForbidden) + memoWindow.messageSent["QString", "QString"].connect( + self.sendMessage["QString", "QString"] + ) + memoWindow.windowClosed["QString"].connect(self.closeMemo) + self.namesUpdated["QString"].connect(memoWindow.namesUpdated) + self.modesUpdated["QString", "QString"].connect(memoWindow.modesUpdated) + self.userPresentSignal["QString", "QString", "QString"].connect( + memoWindow.userPresentChange + ) # chat client send memo open self.memos[channel] = memoWindow - self.joinChannel.emit(channel) # race condition? + self.joinChannel.emit(channel) # race condition? self.secret = secret if self.secret: self.secret = True @@ -1846,44 +2086,53 @@ class PesterWindow(MovingWindow): if self.config.showOnlineNumbers(): self.chumList.showOnlineNumbers() - def changeProfile(self, collision=None, svsnick=None): - if not hasattr(self, 'chooseprofile'): + if not hasattr(self, "chooseprofile"): self.chooseprofile = None if not self.chooseprofile: - self.chooseprofile = PesterChooseProfile(self.userprofile, - self.config, - self.theme, - self, - collision=collision, - svsnick=svsnick) + self.chooseprofile = PesterChooseProfile( + self.userprofile, + self.config, + self.theme, + self, + collision=collision, + svsnick=svsnick, + ) self.chooseprofile.exec() def themePicker(self): - if not hasattr(self, 'choosetheme'): + if not hasattr(self, "choosetheme"): self.choosetheme = None if not self.choosetheme: self.choosetheme = PesterChooseTheme(self.config, self.theme, self) self.choosetheme.exec() + def initTheme(self, theme): self.resize(*theme["main/size"]) self.setWindowIcon(PesterIcon(theme["main/icon"])) self.setWindowTitle(theme["main/windowtitle"]) self.setStyleSheet("QtWidgets.QFrame#main { %s }" % (theme["main/style"])) - + self.backgroundImage = QtGui.QPixmap(theme["main/background-image"]) self.setMask(self.backgroundImage.mask()) - - self.menu.setStyleSheet(("QMenuBar { background: transparent; %s }" - "QMenuBar::item { background: transparent; %s }") - % (theme["main/menubar/style"], - theme["main/menu/menuitem"]) - + ("QMenu { background: transparent; %s }" - "QMenu::item::selected { %s }" - "QMenu::item::disabled { %s }") - % (theme["main/menu/style"], - theme["main/menu/selected"], - theme["main/menu/disabled"])) + + self.menu.setStyleSheet( + ( + "QMenuBar { background: transparent; %s }" + "QMenuBar::item { background: transparent; %s }" + ) + % (theme["main/menubar/style"], theme["main/menu/menuitem"]) + + ( + "QMenu { background: transparent; %s }" + "QMenu::item::selected { %s }" + "QMenu::item::disabled { %s }" + ) + % ( + theme["main/menu/style"], + theme["main/menu/selected"], + theme["main/menu/disabled"], + ) + ) newcloseicon = PesterIcon(theme["main/close/image"]) self.closeButton.setIcon(newcloseicon) self.closeButton.setIconSize(newcloseicon.realsize()) @@ -1921,10 +2170,10 @@ class PesterWindow(MovingWindow): self.helpmenu.setTitle(self.theme["main/menus/help/_name"]) # Console -## if self.theme.has_key("main/menus/client/console"): -## self.console.action.setText(self.theme["main/menus/client/console"]) -## else: -## self.console.action.setText("Console") + ## if self.theme.has_key("main/menus/client/console"): + ## self.console.action.setText(self.theme["main/menus/client/console"]) + ## else: + ## self.console.action.setText("Console") # has_key doesn't work out here for some reason, possibly because of inherits? try: self.console.action.setText(self.theme["main/menus/client/console"]) @@ -1940,29 +2189,36 @@ class PesterWindow(MovingWindow): self.xyzRulesAction.setText(self.theme["main/menus/help/rules"]) except: self.xyzRulesAction.setText("RULES") - + # moods self.moodsLabel.setText(theme["main/moodlabel/text"]) self.moodsLabel.move(*theme["main/moodlabel/loc"]) self.moodsLabel.setStyleSheet(theme["main/moodlabel/style"]) - if hasattr(self, 'moods'): + if hasattr(self, "moods"): self.moods.removeButtons() mood_list = theme["main/moods"] - mood_list = [dict([(str(k),v) for (k,v) in d.items()]) - for d in mood_list] - self.moods = PesterMoodHandler(self, *[PesterMoodButton(self, **d) for d in mood_list]) + mood_list = [dict([(str(k), v) for (k, v) in d.items()]) for d in mood_list] + self.moods = PesterMoodHandler( + self, *[PesterMoodButton(self, **d) for d in mood_list] + ) self.moods.showButtons() # chum addChumStyle = "QPushButton { %s }" % (theme["main/addchum/style"]) if "main/addchum/pressed" in theme: - addChumStyle += "QPushButton:pressed { %s }" % (theme["main/addchum/pressed"]) + addChumStyle += "QPushButton:pressed { %s }" % ( + theme["main/addchum/pressed"] + ) pesterButtonStyle = "QPushButton { %s }" % (theme["main/pester/style"]) if "main/pester/pressed" in theme: - pesterButtonStyle += "QPushButton:pressed { %s }" % (theme["main/pester/pressed"]) + pesterButtonStyle += "QPushButton:pressed { %s }" % ( + theme["main/pester/pressed"] + ) blockButtonStyle = "QPushButton { %s }" % (theme["main/block/style"]) if "main/block/pressed" in theme: - pesterButtonStyle += "QPushButton:pressed { %s }" % (theme["main/block/pressed"]) + pesterButtonStyle += "QPushButton:pressed { %s }" % ( + theme["main/block/pressed"] + ) self.addChumButton.setText(theme["main/addchum/text"]) self.addChumButton.resize(*theme["main/addchum/size"]) self.addChumButton.move(*theme["main/addchum/loc"]) @@ -1990,10 +2246,10 @@ class PesterWindow(MovingWindow): # But this seems to work just as well :3c # GWAHH why does inheriting not work with this server pings - if hasattr(self, 'pingtimer'): - self.pingtimer.start(1000*5) # time in ms + if hasattr(self, "pingtimer"): + self.pingtimer.start(1000 * 5) # time in ms else: PchumLog.warning("No ping timer.") # Desync check - if hasattr(self, 'lastCheckPing'): + if hasattr(self, "lastCheckPing"): self.lastCheckPing = time.time() else: PchumLog.warning("No ping timer.") - + @QtCore.pyqtSlot() def blockSelectedChum(self): curChumListing = self.chumList.currentItem() if curChumListing: curChum = curChumListing.chum self.blockChum(curChum.handle) + @QtCore.pyqtSlot() def pesterSelectedChum(self): curChum = self.chumList.currentItem() if curChum: text = str(curChum.text(0)) if text.rfind(" (") != -1: - text = text[0:text.rfind(" (")] - if text not in self.chumList.groups and \ - text != "Chums": + text = text[0 : text.rfind(" (")] + if text not in self.chumList.groups and text != "Chums": self.newConversationWindow(curChum) + @QtCore.pyqtSlot(QtWidgets.QListWidgetItem) def newConversationWindow(self, chumlisting): # check chumdb @@ -2229,6 +2502,7 @@ class PesterWindow(MovingWindow): if color: chum.color = color self.newConversation(chum) + @QtCore.pyqtSlot(QString) def closeConvo(self, handle): h = str(handle) @@ -2241,13 +2515,18 @@ class PesterWindow(MovingWindow): except KeyError: chumopen = self.convos[h.lower()].chumopen if chumopen: - self.chatlog.log(chum.handle, - self.profile().pestermsg(chum, - QtGui.QColor(self.theme["convo/systemMsgColor"]), - self.theme["convo/text/ceasepester"])) + self.chatlog.log( + chum.handle, + self.profile().pestermsg( + chum, + QtGui.QColor(self.theme["convo/systemMsgColor"]), + self.theme["convo/text/ceasepester"], + ), + ) self.convoClosed.emit(handle) self.chatlog.finish(h) del self.convos[h] + @QtCore.pyqtSlot(QString) def closeMemo(self, channel): c = str(channel) @@ -2260,10 +2539,12 @@ class PesterWindow(MovingWindow): del self.memos[c.lower()] except KeyError: pass + @QtCore.pyqtSlot() def tabsClosed(self): del self.tabconvo self.tabconvo = None + @QtCore.pyqtSlot() def memoTabsClosed(self): del self.tabmemo @@ -2276,7 +2557,7 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot(QString, QtGui.QColor) def updateColorSlot(self, handle, color): - PchumLog.debug("updateColorSlot, "+ str(handle) +", " + str(color)) + PchumLog.debug("updateColorSlot, " + str(handle) + ", " + str(color)) h = str(handle) self.changeColor(h, color) @@ -2285,23 +2566,30 @@ class PesterWindow(MovingWindow): h = str(handle) m = str(msg) self.newMessage(h, m) + @QtCore.pyqtSlot(QString, QString, QString) def deliverMemo(self, chan, handle, msg): (c, h, m) = (str(chan), str(handle), str(msg)) - self.newMemoMsg(c,h,m) + self.newMemoMsg(c, h, m) + @QtCore.pyqtSlot(QString, QString) def deliverNotice(self, handle, msg): h = str(handle) m = str(msg) - if h.upper() == "NICKSERV" and m.startswith("Your nickname is now being changed to"): + if h.upper() == "NICKSERV" and m.startswith( + "Your nickname is now being changed to" + ): changedto = m[39:-1] msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.theme["main/defaultwindow/style"]) - msgbox.setText("This chumhandle has been registered; " - "you may not use it.") - msgbox.setInformativeText("Your handle is now being changed to %s." - % (changedto)) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.theme["main/defaultwindow/style"] + ) + msgbox.setText( + "This chumhandle has been registered; " "you may not use it." + ) + msgbox.setInformativeText( + "Your handle is now being changed to %s." % (changedto) + ) msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msgbox.exec() elif h == self.randhandler.randNick: @@ -2318,17 +2606,22 @@ class PesterWindow(MovingWindow): # "Your VHOST is actived", "You have one new memo", etc. t = self.tm.Toast("%s:" % h, m) t.show() - + @QtCore.pyqtSlot(QString, QString) def deliverInvite(self, handle, channel): msgbox = QtWidgets.QMessageBox() msgbox.setText("You're invited!") - msgbox.setStyleSheet("QMessageBox{" + self.theme["main/defaultwindow/style"] + "}") - msgbox.setInformativeText(("%s has invited you to the memo: %s" - "\nWould you like to join them?") - % (handle, channel)) - msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok - | QtWidgets.QMessageBox.StandardButton.Cancel) + msgbox.setStyleSheet( + "QMessageBox{" + self.theme["main/defaultwindow/style"] + "}" + ) + msgbox.setInformativeText( + ("%s has invited you to the memo: %s" "\nWould you like to join them?") + % (handle, channel) + ) + msgbox.setStandardButtons( + QtWidgets.QMessageBox.StandardButton.Ok + | QtWidgets.QMessageBox.StandardButton.Cancel + ) # Find the Cancel button and make it default for b in msgbox.buttons(): if msgbox.buttonRole(b) == QtWidgets.QMessageBox.ButtonRole.RejectRole: @@ -2342,15 +2635,18 @@ class PesterWindow(MovingWindow): ret = msgbox.exec() if ret == QtWidgets.QMessageBox.StandardButton.Ok: self.newMemo(str(channel), "+0:00") + @QtCore.pyqtSlot(QString) def chanInviteOnly(self, channel): self.inviteOnlyChan.emit(channel) + @QtCore.pyqtSlot(QString, QString) def cannotSendToChan(self, channel, msg): self.deliverMemo(channel, "ChanServ", msg) + # Unused and redefined. - #@QtCore.pyqtSlot(QString, QString) - #def modesUpdated(self, channel, modes): + # @QtCore.pyqtSlot(QString, QString) + # def modesUpdated(self, channel, modes): # self.modesUpdated.emit(channel, modes) @QtCore.pyqtSlot(QString, QString, QString) def timeCommand(self, chan, handle, command): @@ -2373,11 +2669,12 @@ class PesterWindow(MovingWindow): self.namesdb[c] = names # warn interested party of names self.namesUpdated.emit(c) + @QtCore.pyqtSlot(QString, QString, QString) 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] @@ -2420,12 +2717,12 @@ class PesterWindow(MovingWindow): except KeyError: self.namesdb[c] = [n] - #PchumLog.debug("handle=%s\nchannel=%s\nupdate=%s\n" % (handle, channel, update)) + # PchumLog.debug("handle=%s\nchannel=%s\nupdate=%s\n" % (handle, channel, update)) self.userPresentSignal.emit(handle, channel, update) @QtCore.pyqtSlot() def addChumWindow(self): - if not hasattr(self, 'addchumdialog'): + if not hasattr(self, "addchumdialog"): self.addchumdialog = None if not self.addchumdialog: available_groups = [g[0] for g in self.config.getGroups()] @@ -2440,8 +2737,10 @@ class PesterWindow(MovingWindow): if handle in [h.handle for h in self.chumList.chums]: self.addchumdialog = None return - if not (PesterProfile.checkLength(handle) and - PesterProfile.checkValid(handle)[0]): + if not ( + PesterProfile.checkLength(handle) + and PesterProfile.checkValid(handle)[0] + ): errormsg = QtWidgets.QErrorMessage(self) errormsg.showMessage("THIS IS NOT A VALID CHUMTAG!") self.addchumdialog = None @@ -2458,17 +2757,19 @@ class PesterWindow(MovingWindow): self.chumdb.setGroup(handle, group) self.addChum(chum) self.addchumdialog = None + @QtCore.pyqtSlot(QString) def removeChum(self, chumlisting): self.config.removeChum(chumlisting) + def reportChum(self, handle): - (reason, ok) = QtWidgets.QInputDialog.getText(self, - "Report User", - "Enter the reason you are reporting this user (optional):") + (reason, ok) = QtWidgets.QInputDialog.getText( + self, + "Report User", + "Enter the reason you are reporting this user (optional):", + ) if ok: - self.sendMessage.emit("REPORT %s %s" - % (handle, reason), - "calSprite") + self.sendMessage.emit("REPORT %s %s" % (handle, reason), "calSprite") @QtCore.pyqtSlot(QString) def blockChum(self, handle): @@ -2477,14 +2778,16 @@ class PesterWindow(MovingWindow): self.config.removeChum(h) if h in self.convos: convo = self.convos[h] - msg = self.profile().pestermsg(convo.chum, - QtGui.QColor(self.theme["convo/systemMsgColor"]), - self.theme["convo/text/blocked"]) + msg = self.profile().pestermsg( + convo.chum, + QtGui.QColor(self.theme["convo/systemMsgColor"]), + self.theme["convo/text/blocked"], + ) convo.textArea.append(convertTags(msg)) self.chatlog.log(convo.chum.handle, msg) convo.updateBlocked() self.chumList.removeChum(h) - if hasattr(self, 'trollslum') and self.trollslum: + if hasattr(self, "trollslum") and self.trollslum: newtroll = PesterProfile(h) self.trollslum.addTroll(newtroll) self.moodRequest.emit(newtroll) @@ -2496,14 +2799,16 @@ class PesterWindow(MovingWindow): self.config.delBlocklist(h) if h in self.convos: convo = self.convos[h] - msg = self.profile().pestermsg(convo.chum, - QtGui.QColor(self.theme["convo/systemMsgColor"]), - self.theme["convo/text/unblocked"]) + msg = self.profile().pestermsg( + convo.chum, + QtGui.QColor(self.theme["convo/systemMsgColor"]), + self.theme["convo/text/unblocked"], + ) convo.textArea.append(convertTags(msg)) self.chatlog.log(convo.chum.handle, msg) convo.updateMood(convo.chum.mood, unblocked=True) chum = PesterProfile(h, chumdb=self.chumdb) - if hasattr(self, 'trollslum') and self.trollslum: + if hasattr(self, "trollslum") and self.trollslum: self.trollslum.removeTroll(handle) self.config.addChum(chum) self.chumList.addChum(chum) @@ -2523,6 +2828,7 @@ class PesterWindow(MovingWindow): self.setAway.emit(False) self.randhandler.setIdle(False) self.idler.time = 0 + # karxi: TODO: Need to consider sticking an idle-setter here. @QtCore.pyqtSlot() def checkIdle(self): @@ -2592,7 +2898,7 @@ class PesterWindow(MovingWindow): f = QtWidgets.QFileDialog.getOpenFileName(self)[0] if f == "": return - fp = open(f, 'r') + fp = open(f, "r") regexp_state = None for l in fp: # import chumlist @@ -2610,9 +2916,9 @@ class PesterWindow(MovingWindow): re.compile(regexp_state) except re.error: continue - newquirk = pesterQuirk({"type": "regexp", - "from": regexp_state, - "to": replace}) + newquirk = pesterQuirk( + {"type": "regexp", "from": regexp_state, "to": replace} + ) qs = self.userprofile.quirks qs.addQuirk(newquirk) self.userprofile.setQuirks(qs) @@ -2624,15 +2930,16 @@ class PesterWindow(MovingWindow): continue other_mo = re.match("(prefix|suffix): (.+)", l) if other_mo is not None: - newquirk = pesterQuirk({"type": other_mo.group(1), - "value": other_mo.group(2)}) + newquirk = pesterQuirk( + {"type": other_mo.group(1), "value": other_mo.group(2)} + ) qs = self.userprofile.quirks qs.addQuirk(newquirk) self.userprofile.setQuirks(qs) @QtCore.pyqtSlot() def showMemos(self, channel=""): - if not hasattr(self, 'memochooser'): + if not hasattr(self, "memochooser"): self.memochooser = None if self.memochooser: return @@ -2641,6 +2948,7 @@ class PesterWindow(MovingWindow): self.memochooser.rejected.connect(self.memoChooserClose) self.requestChannelList.emit() self.memochooser.show() + @QtCore.pyqtSlot() def joinSelectedMemo(self): @@ -2650,7 +2958,7 @@ class PesterWindow(MovingWindow): # Join the ones on the list first for SelectedMemo in self.memochooser.SelectedMemos(): - channel = "#"+str(SelectedMemo.target) + channel = "#" + str(SelectedMemo.target) self.newMemo(channel, time) if self.memochooser.newmemoname(): @@ -2658,34 +2966,36 @@ class PesterWindow(MovingWindow): channel = str(newmemo).replace(" ", "_") channel = re.sub(r"[^A-Za-z0-9#_\,]", "", channel) # Allow us to join more than one with this. - chans = channel.split(',') + chans = channel.split(",") # Filter out empty entries. chans = [_f for _f in chans if _f] for c in chans: - c = '#' + c + c = "#" + c # We should really change this code to only make the memo once # the server has confirmed that we've joined.... self.newMemo(c, time, secret=secret, invite=invite) self.memochooser = None + @QtCore.pyqtSlot() def memoChooserClose(self): self.memochooser = None @QtCore.pyqtSlot(PesterList) def updateChannelList(self, channels): - if hasattr(self, 'memochooser') and self.memochooser: + if hasattr(self, "memochooser") and self.memochooser: self.memochooser.updateChannels(channels) + @QtCore.pyqtSlot() def showAllUsers(self): - if not hasattr(self, 'allusers'): + if not hasattr(self, "allusers"): self.allusers = None if not self.allusers: self.allusers = PesterUserlist(self.config, self.theme, self) self.allusers.accepted.connect(self.userListClose) self.allusers.rejected.connect(self.userListClose) - self.allusers.addChum['QString'].connect(self.userListAdd) - self.allusers.pesterChum['QString'].connect(self.userListPester) + self.allusers.addChum["QString"].connect(self.userListAdd) + self.allusers.pesterChum["QString"].connect(self.userListPester) self.requestNames.emit("#pesterchum") self.allusers.show() @@ -2694,17 +3004,19 @@ class PesterWindow(MovingWindow): h = str(handle) chum = PesterProfile(h, chumdb=self.chumdb) self.addChum(chum) + @QtCore.pyqtSlot(QString) def userListPester(self, handle): h = str(handle) self.newConversation(h) + @QtCore.pyqtSlot() def userListClose(self): self.allusers = None @QtCore.pyqtSlot() def openQuirks(self): - if not hasattr(self, 'quirkmenu'): + if not hasattr(self, "quirkmenu"): self.quirkmenu = None if not self.quirkmenu: self.quirkmenu = PesterChooseQuirks(self.config, self.theme, self) @@ -2713,30 +3025,37 @@ class PesterWindow(MovingWindow): self.quirkmenu.show() self.quirkmenu.raise_() self.quirkmenu.activateWindow() + @QtCore.pyqtSlot() def updateQuirks(self): for i in range(self.quirkmenu.quirkList.topLevelItemCount()): curgroup = str(self.quirkmenu.quirkList.topLevelItem(i).text(0)) for j in range(self.quirkmenu.quirkList.topLevelItem(i).childCount()): item = self.quirkmenu.quirkList.topLevelItem(i).child(j) - item.quirk.quirk["on"] = item.quirk.on = (item.checkState(0) == QtCore.Qt.CheckState.Checked) + item.quirk.quirk["on"] = item.quirk.on = ( + item.checkState(0) == QtCore.Qt.CheckState.Checked + ) item.quirk.quirk["group"] = item.quirk.group = curgroup quirks = pesterQuirks(self.quirkmenu.quirks()) self.userprofile.setQuirks(quirks) - if hasattr(self.quirkmenu, 'quirktester') and self.quirkmenu.quirktester: + if hasattr(self.quirkmenu, "quirktester") and self.quirkmenu.quirktester: self.quirkmenu.quirktester.close() self.quirkmenu = None + @QtCore.pyqtSlot() def closeQuirks(self): - if hasattr(self.quirkmenu, 'quirktester') and self.quirkmenu.quirktester: + if hasattr(self.quirkmenu, "quirktester") and self.quirkmenu.quirktester: self.quirkmenu.quirktester.close() self.quirkmenu = None + @QtCore.pyqtSlot() def openChat(self): if not hasattr(self, "openchatdialog"): self.openchatdialog = None if not self.openchatdialog: - (chum, ok) = QtWidgets.QInputDialog.getText(self, "Pester Chum", "Enter a handle to pester:") + (chum, ok) = QtWidgets.QInputDialog.getText( + self, "Pester Chum", "Enter a handle to pester:" + ) try: if ok: self.newConversation(str(chum)) @@ -2744,9 +3063,10 @@ class PesterWindow(MovingWindow): pass finally: self.openchatdialog = None + @QtCore.pyqtSlot() def openLogv(self): - if not hasattr(self, 'logusermenu'): + if not hasattr(self, "logusermenu"): self.logusermenu = None if not self.logusermenu: self.logusermenu = PesterLogUserSelect(self.config, self.theme, self) @@ -2755,6 +3075,7 @@ class PesterWindow(MovingWindow): self.logusermenu.show() self.logusermenu.raise_() self.logusermenu.activateWindow() + @QtCore.pyqtSlot() def closeLogUsers(self): self.logusermenu.close() @@ -2762,19 +3083,22 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot() def addGroupWindow(self): - if not hasattr(self, 'addgroupdialog'): + if not hasattr(self, "addgroupdialog"): self.addgroupdialog = None if not self.addgroupdialog: - (gname, ok) = QtWidgets.QInputDialog.getText(self, "Add Group", "Enter a name for the new group:") + (gname, ok) = QtWidgets.QInputDialog.getText( + self, "Add Group", "Enter a name for the new group:" + ) if ok: gname = str(gname) if re.search("[^A-Za-z0-9_\s]", gname) is not None: msgbox = QtWidgets.QMessageBox() msgbox.setInformativeText("THIS IS NOT A VALID GROUP NAME") msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) - #Style :) (memos/style or convo/style works :3 ) - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.theme["main/defaultwindow/style"]) + # Style :) (memos/style or convo/style works :3 ) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.theme["main/defaultwindow/style"] + ) msgbox.exec() self.addgroupdialog = None return @@ -2783,7 +3107,7 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot() def openOpts(self): - if not hasattr(self, 'optionmenu'): + if not hasattr(self, "optionmenu"): self.optionmenu = None if not self.optionmenu: self.optionmenu = PesterOptions(self.config, self.theme, self) @@ -2792,10 +3116,12 @@ class PesterWindow(MovingWindow): self.optionmenu.show() self.optionmenu.raise_() self.optionmenu.activateWindow() + @QtCore.pyqtSlot() def closeOptions(self): self.optionmenu.close() self.optionmenu = None + @QtCore.pyqtSlot() def updateOptions(self): try: @@ -2820,7 +3146,7 @@ class PesterWindow(MovingWindow): # combine self.createTabWindow() newconvos = {} - for (h,c) in self.convos.items(): + for (h, c) in self.convos.items(): c.setParent(self.tabconvo) self.tabconvo.addChat(c) self.tabconvo.show() @@ -2850,7 +3176,7 @@ class PesterWindow(MovingWindow): # combine newmemos = {} self.createMemoTabWindow() - for (h,m) in self.memos.items(): + for (h, m) in self.memos.items(): m.setParent(self.tabmemo) self.tabmemo.addChat(m) self.tabmemo.show() @@ -2878,23 +3204,23 @@ class PesterWindow(MovingWindow): chatsoundsetting = self.optionmenu.chatsoundcheck.isChecked() curchatsound = self.config.chatSound() if chatsoundsetting != curchatsound: - self.config.set('chatSound', chatsoundsetting) + self.config.set("chatSound", chatsoundsetting) memosoundsetting = self.optionmenu.memosoundcheck.isChecked() curmemosound = self.config.memoSound() if memosoundsetting != curmemosound: - self.config.set('memoSound', memosoundsetting) + self.config.set("memoSound", memosoundsetting) memopingsetting = self.optionmenu.memopingcheck.isChecked() curmemoping = self.config.memoPing() if memopingsetting != curmemoping: - self.config.set('pingSound', memopingsetting) + self.config.set("pingSound", memopingsetting) namesoundsetting = self.optionmenu.namesoundcheck.isChecked() curnamesound = self.config.nameSound() if namesoundsetting != curnamesound: - self.config.set('nameSound', namesoundsetting) + self.config.set("nameSound", namesoundsetting) volumesetting = self.optionmenu.volume.value() curvolume = self.config.volume() if volumesetting != curvolume: - self.config.set('volume', volumesetting) + self.config.set("volume", volumesetting) self.setVolume(volumesetting) # timestamps timestampsetting = self.optionmenu.timestampcheck.isChecked() @@ -2907,8 +3233,8 @@ class PesterWindow(MovingWindow): secondssetting = self.optionmenu.secondscheck.isChecked() self.config.set("showSeconds", secondssetting) # groups - #groupssetting = self.optionmenu.groupscheck.isChecked() - #self.config.set("useGroups", groupssetting) + # groupssetting = self.optionmenu.groupscheck.isChecked() + # self.config.set("useGroups", groupssetting) emptygroupssetting = self.optionmenu.showemptycheck.isChecked() curemptygroup = self.config.showEmptyGroups() if curemptygroup and not emptygroupssetting: @@ -2932,7 +3258,7 @@ class PesterWindow(MovingWindow): logpesterssetting = logpesterssetting | self.config.STAMP curlogpesters = self.config.logPesters() if logpesterssetting != curlogpesters: - self.config.set('logPesters', logpesterssetting) + self.config.set("logPesters", logpesterssetting) logmemossetting = 0 if self.optionmenu.logmemoscheck.isChecked(): logmemossetting = logmemossetting | self.config.LOG @@ -2940,22 +3266,22 @@ class PesterWindow(MovingWindow): logmemossetting = logmemossetting | self.config.STAMP curlogmemos = self.config.logMemos() if logmemossetting != curlogmemos: - self.config.set('logMemos', logmemossetting) + self.config.set("logMemos", logmemossetting) # memo and user links linkssetting = self.optionmenu.userlinkscheck.isChecked() curlinks = self.config.disableUserLinks() if linkssetting != curlinks: - self.config.set('userLinks', not linkssetting) + self.config.set("userLinks", not linkssetting) # idle time idlesetting = self.optionmenu.idleBox.value() curidle = self.config.idleTime() if idlesetting != curidle: - self.config.set('idleTime', idlesetting) - self.idler.threshold = 60*idlesetting + self.config.set("idleTime", idlesetting) + self.idler.threshold = 60 * idlesetting # theme ghostchumsetting = self.optionmenu.ghostchum.isChecked() curghostchum = self.config.ghostchum() - self.config.set('ghostchum', ghostchumsetting) + self.config.set("ghostchum", ghostchumsetting) self.themeSelected(ghostchumsetting != curghostchum) # randoms if self.randhandler.running: @@ -2964,36 +3290,36 @@ class PesterWindow(MovingWindow): minisetting = self.optionmenu.miniBox.currentIndex() curmini = self.config.minimizeAction() if minisetting != curmini: - self.config.set('miniAction', minisetting) + self.config.set("miniAction", minisetting) self.setButtonAction(self.miniButton, minisetting, curmini) closesetting = self.optionmenu.closeBox.currentIndex() curclose = self.config.closeAction() if closesetting != curclose: - self.config.set('closeAction', closesetting) + self.config.set("closeAction", closesetting) self.setButtonAction(self.closeButton, closesetting, curclose) # op and voice messages opvmesssetting = self.optionmenu.memomessagecheck.isChecked() curopvmess = self.config.opvoiceMessages() if opvmesssetting != curopvmess: - self.config.set('opvMessages', opvmesssetting) + self.config.set("opvMessages", opvmesssetting) # animated smiles animatesetting = self.optionmenu.animationscheck.isChecked() curanimate = self.config.animations() if animatesetting != curanimate: - self.config.set('animations', animatesetting) + self.config.set("animations", animatesetting) self.animationSetting.emit(animatesetting) # update checked - #updatechecksetting = self.optionmenu.updateBox.currentIndex() - #curupdatecheck = self.config.checkForUpdates() - #if updatechecksetting != curupdatecheck: + # updatechecksetting = self.optionmenu.updateBox.currentIndex() + # curupdatecheck = self.config.checkForUpdates() + # if updatechecksetting != curupdatecheck: # self.config.set('checkUpdates', updatechecksetting) # mspa update check - #if ostools.isOSXLeopard(): + # if ostools.isOSXLeopard(): # mspachecksetting = false - #else: + # else: # mspachecksetting = self.optionmenu.mspaCheck.isChecked() - #curmspacheck = self.config.checkMSPA() - #if mspachecksetting != curmspacheck: + # curmspacheck = self.config.checkMSPA() + # if mspachecksetting != curmspacheck: # self.config.set('mspa', mspachecksetting) # Taskbar blink blinksetting = 0 @@ -3003,7 +3329,7 @@ class PesterWindow(MovingWindow): blinksetting |= self.config.MBLINK curblink = self.config.blink() if blinksetting != curblink: - self.config.set('blink', blinksetting) + self.config.set("blink", blinksetting) # toast notifications self.tm.setEnabled(self.optionmenu.notifycheck.isChecked()) self.tm.setCurrentType(str(self.optionmenu.notifyOptions.currentText())) @@ -3020,12 +3346,12 @@ class PesterWindow(MovingWindow): notifysetting |= self.config.INITIALS curnotify = self.config.notifyOptions() if notifysetting != curnotify: - self.config.set('notifyOptions', notifysetting) + self.config.set("notifyOptions", notifysetting) # low bandwidth bandwidthsetting = self.optionmenu.bandwidthcheck.isChecked() curbandwidth = self.config.lowBandwidth() if bandwidthsetting != curbandwidth: - self.config.set('lowBandwidth', bandwidthsetting) + self.config.set("lowBandwidth", bandwidthsetting) if bandwidthsetting: self.leftChannel.emit("#pesterchum") else: @@ -3052,18 +3378,18 @@ class PesterWindow(MovingWindow): self.optionmenu = None def setButtonAction(self, button, setting, old): - if old == 0: # minimize to taskbar + if old == 0: # minimize to taskbar button.clicked.disconnect(self.showMinimized) - elif old == 1: # minimize to tray + elif old == 1: # minimize to tray button.clicked.disconnect(self.closeToTray) - elif old == 2: # quit + elif old == 2: # quit button.clicked.disconnect(self.app.quit) - if setting == 0: # minimize to taskbar + if setting == 0: # minimize to taskbar button.clicked.connect(self.showMinimized) - elif setting == 1: # minimize to tray + elif setting == 1: # minimize to tray button.clicked.connect(self.closeToTray) - elif setting == 2: # quit + elif setting == 2: # quit button.clicked.connect(self.app.quit) @QtCore.pyqtSlot() @@ -3088,13 +3414,17 @@ class PesterWindow(MovingWindow): # update profile self.userprofile.setTheme(self.theme) self.choosetheme = None + @QtCore.pyqtSlot() def closeTheme(self): self.choosetheme = None + @QtCore.pyqtSlot() def profileSelected(self): - if self.chooseprofile.profileBox and \ - self.chooseprofile.profileBox.currentIndex() > 0: + if ( + self.chooseprofile.profileBox + and self.chooseprofile.profileBox.currentIndex() > 0 + ): handle = str(self.chooseprofile.profileBox.currentText()) if handle == self.profile().handle: self.chooseprofile = None @@ -3106,27 +3436,33 @@ class PesterWindow(MovingWindow): msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Icon.Warning) msgBox.setWindowTitle(":(") - msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links - self.filename = _datadir+"pesterchum.js" + msgBox.setTextFormat( + QtCore.Qt.TextFormat.RichText + ) # Clickable html links + self.filename = _datadir + "pesterchum.js" try: - msg = ("

Failed to load: " - "%s/%s.js" - "

" - "Try to check for syntax errors if the file exists." - "

" - "If you got this message at launch you may want to " - "change your default profile." - "

%s<\h3><\html>" - % (self.profiledir, self.profiledir, user, e)) - + msg = ( + "

Failed to load: " + "%s/%s.js" + "

" + "Try to check for syntax errors if the file exists." + "

" + "If you got this message at launch you may want to " + "change your default profile." + "

%s<\h3><\html>" + % (self.profiledir, self.profiledir, user, e) + ) + except: # More generic error for if not all variables are available. - msg = ("Unspecified profile error." - "

Try to check for syntax errors if the " - "file exists." - "

If you got this message at launch you may " - "want to change your default profile." - "

%s<\h3><\html>" % e) + msg = ( + "Unspecified profile error." + "

Try to check for syntax errors if the " + "file exists." + "

If you got this message at launch you may " + "want to change your default profile." + "

%s<\h3><\html>" % e + ) PchumLog.critical(e) msgBox.setText(msg) msgBox.exec() @@ -3136,8 +3472,7 @@ class PesterWindow(MovingWindow): if handle == self.profile().handle: self.chooseprofile = None return - profile = PesterProfile(handle, - self.chooseprofile.chumcolor) + profile = PesterProfile(handle, self.chooseprofile.chumcolor) self.userprofile = userProfile.newUserProfile(profile) self.changeTheme(self.userprofile.getTheme()) @@ -3147,28 +3482,31 @@ class PesterWindow(MovingWindow): # is default? if self.chooseprofile.defaultcheck.isChecked(): self.config.set("defaultprofile", self.userprofile.chat.handle) - if hasattr(self, 'trollslum') and self.trollslum: + if hasattr(self, "trollslum") and self.trollslum: self.trollslum.close() self.chooseprofile = None self.profileChanged.emit() + @QtCore.pyqtSlot() def showTrollSlum(self): - if not hasattr(self, 'trollslum'): + if not hasattr(self, "trollslum"): self.trollslum = None if self.trollslum: return trolls = [PesterProfile(h) for h in self.config.getBlocklist()] self.trollslum = TrollSlumWindow(trolls, self) - self.trollslum.blockChumSignal['QString'].connect(self.blockChum) - self.trollslum.unblockChumSignal['QString'].connect(self.unblockChum) + self.trollslum.blockChumSignal["QString"].connect(self.blockChum) + self.trollslum.unblockChumSignal["QString"].connect(self.unblockChum) self.moodsRequest.emit(PesterList(trolls)) self.trollslum.show() + @QtCore.pyqtSlot() def closeTrollSlum(self): self.trollslum = None + @QtCore.pyqtSlot() def changeMyColor(self): - if not hasattr(self, 'colorDialog'): + if not hasattr(self, "colorDialog"): self.colorDialog = None if self.colorDialog: return @@ -3180,17 +3518,25 @@ class PesterWindow(MovingWindow): self.userprofile.setColor(color) self.mycolorUpdated.emit() self.colorDialog = None + @QtCore.pyqtSlot() def closeProfile(self): self.chooseprofile = None + @QtCore.pyqtSlot() def switchProfile(self): if self.convos: closeWarning = QtWidgets.QMessageBox() - closeWarning.setText("WARNING: CHANGING PROFILES WILL CLOSE ALL CONVERSATION WINDOWS!") - closeWarning.setInformativeText("i warned you about windows bro!!!! i told you dog!") - closeWarning.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Cancel - | QtWidgets.QMessageBox.StandardButton.Ok) + closeWarning.setText( + "WARNING: CHANGING PROFILES WILL CLOSE ALL CONVERSATION WINDOWS!" + ) + closeWarning.setInformativeText( + "i warned you about windows bro!!!! i told you dog!" + ) + closeWarning.setStandardButtons( + QtWidgets.QMessageBox.StandardButton.Cancel + | QtWidgets.QMessageBox.StandardButton.Ok + ) closeWarning.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Ok) ret = closeWarning.exec() if ret == QtWidgets.QMessageBox.StandardButton.Cancel: @@ -3206,53 +3552,77 @@ class PesterWindow(MovingWindow): except: PchumLog.warning("No randomEncounter set in userconfig?") self.mycolorUpdated.emit() - + def aboutPesterchum(self): - if hasattr(self, 'aboutwindow') and self.aboutwindow: + if hasattr(self, "aboutwindow") and self.aboutwindow: return self.aboutwindow = AboutPesterchum(self) self.aboutwindow.exec() self.aboutwindow = None + @QtCore.pyqtSlot() def loadCalsprite(self): self.newConversation("calSprite") + @QtCore.pyqtSlot() def loadChanServ(self): self.newConversation("chanServ") + @QtCore.pyqtSlot() def loadNickServ(self): self.newConversation("nickServ") + @QtCore.pyqtSlot() def launchHelp(self): - QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://github.com/Dpeta/pesterchum-alt-servers/issues", - QtCore.QUrl.ParsingMode.TolerantMode)) - QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://forum.homestuck.xyz/viewtopic.php?f=7&t=467", - QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl( + "https://github.com/Dpeta/pesterchum-alt-servers/issues", + QtCore.QUrl.ParsingMode.TolerantMode, + ) + ) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl( + "https://forum.homestuck.xyz/viewtopic.php?f=7&t=467", + QtCore.QUrl.ParsingMode.TolerantMode, + ) + ) + @QtCore.pyqtSlot() def reportBug(self): - QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://github.com/Dpeta/pesterchum-alt-servers/issues", - QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl( + "https://github.com/Dpeta/pesterchum-alt-servers/issues", + QtCore.QUrl.ParsingMode.TolerantMode, + ) + ) @QtCore.pyqtSlot() def xyzRules(self): - QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://www.pesterchum.xyz/pesterchum-rules", - QtCore.QUrl.ParsingMode.TolerantMode)) + QtGui.QDesktopServices.openUrl( + QtCore.QUrl( + "https://www.pesterchum.xyz/pesterchum-rules", + QtCore.QUrl.ParsingMode.TolerantMode, + ) + ) @QtCore.pyqtSlot(QString, QString) def nickCollision(self, handle, tmphandle): - if hasattr(self, 'loadingscreen'): + 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.userprofile = userProfile( + PesterProfile( + "pesterClient%d" % (random.randint(100, 999)), + QtGui.QColor("black"), + Mood(0), + ) + ) self.changeTheme(self.userprofile.getTheme()) - if not hasattr(self, 'chooseprofile'): + if not hasattr(self, "chooseprofile"): self.chooseprofile = None if not self.chooseprofile: h = str(handle) @@ -3260,21 +3630,22 @@ class PesterWindow(MovingWindow): @QtCore.pyqtSlot(QString, QString) def getSvsnickedOn(self, oldhandle, newhandle): - if hasattr(self, 'loadingscreen'): + 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.userprofile = userProfile( + PesterProfile(newhandle, QtGui.QColor("black"), Mood(0)) + ) self.changeTheme(self.userprofile.getTheme()) - if not hasattr(self, 'chooseprofile'): + if not hasattr(self, "chooseprofile"): self.chooseprofile = None if not self.chooseprofile: self.changeProfile(svsnick=(oldhandle, newhandle)) + @QtCore.pyqtSlot(QString) def myHandleChanged(self, handle): # Update nick in channels @@ -3286,6 +3657,7 @@ class PesterWindow(MovingWindow): return else: self.nickCollision(self.profile().handle, handle) + @QtCore.pyqtSlot() def pickTheme(self): self.themePicker() @@ -3294,18 +3666,19 @@ class PesterWindow(MovingWindow): def systemTrayActivated(self, reason): if reason == QtWidgets.QSystemTrayIcon.ActivationReason.Trigger: self.systemTrayFunction() - #elif reason == QtWidgets.QSystemTrayIcon.Context: + # elif reason == QtWidgets.QSystemTrayIcon.Context: # pass - # show context menu i guess - #self.showTrayContext.emit() + # show context menu i guess + # self.showTrayContext.emit() @QtCore.pyqtSlot() def tooManyPeeps(self): msg = QtWidgets.QMessageBox(self) msg.setText("D: TOO MANY PEOPLE!!!") - msg.setInformativeText("The server has hit max capacity." - "Please try again later.") - #msg.setStyleSheet("QMessageBox{" + self.theme["main/defaultwindow/style"] + "}") + msg.setInformativeText( + "The server has hit max capacity." "Please try again later." + ) + # msg.setStyleSheet("QMessageBox{" + self.theme["main/defaultwindow/style"] + "}") msg.exec() @QtCore.pyqtSlot(QString, QString) @@ -3323,18 +3696,21 @@ class PesterWindow(MovingWindow): def updateServerJson(self): PchumLog.info(self.customServerPrompt_qline.text() + " chosen") - server_and_port = self.customServerPrompt_qline.text().split(':') + server_and_port = self.customServerPrompt_qline.text().split(":") try: server = { "server": server_and_port[0], - "port": int(server_and_port[1]),# to make sure port is a valid integer, and raise an exception if it cannot be converted. - "TLS": self.TLS_checkbox.isChecked() - } - PchumLog.info("server: "+str(server)) + "port": int( + server_and_port[1] + ), # to make sure port is a valid integer, and raise an exception if it cannot be converted. + "TLS": self.TLS_checkbox.isChecked(), + } + PchumLog.info("server: " + str(server)) except: msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.theme["main/defaultwindow/style"] + ) msgbox.setWindowIcon(PesterIcon(self.theme["main/icon"])) msgbox.setInformativeText("Incorrect format :(") msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) @@ -3349,44 +3725,51 @@ class PesterWindow(MovingWindow): server_list_obj.append(server) try: with open(_datadir + "serverlist.json", "w") as server_file: - server_file.write(json.dumps(server_list_obj, indent = 4)) + server_file.write(json.dumps(server_list_obj, indent=4)) server_file.close() except: - PchumLog.error("failed") + PchumLog.error("failed") # Go back to original screen self.chooseServer() - + def resetServerlist(self): - default_server_list = [{ - "server": "irc.pesterchum.xyz", - "port": "6697", - "TLS": True - }] + default_server_list = [ + {"server": "irc.pesterchum.xyz", "port": "6697", "TLS": True} + ] if os.path.isfile(_datadir + "serverlist.json"): PchumLog.error("Failed to load server list from serverlist.json.") msgbox = QtWidgets.QMessageBox() - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.theme["main/defaultwindow/style"] + ) msgbox.setWindowIcon(PesterIcon(self.theme["main/icon"])) - msgbox.setInformativeText("Failed to load server list, do you want to revert to defaults?\n" - "If you choose no, Pesterchum will most likely crash unless you manually fix serverlist.json\n" - "Please tell me if this error occurs :'3") - msgbox.addButton(QtWidgets.QPushButton("Yes"), QtWidgets.QMessageBox.ButtonRole.YesRole) - msgbox.addButton(QtWidgets.QPushButton("No"), QtWidgets.QMessageBox.ButtonRole.NoRole) + msgbox.setInformativeText( + "Failed to load server list, do you want to revert to defaults?\n" + "If you choose no, Pesterchum will most likely crash unless you manually fix serverlist.json\n" + "Please tell me if this error occurs :'3" + ) + msgbox.addButton( + QtWidgets.QPushButton("Yes"), QtWidgets.QMessageBox.ButtonRole.YesRole + ) + msgbox.addButton( + QtWidgets.QPushButton("No"), QtWidgets.QMessageBox.ButtonRole.NoRole + ) msgbox.exec() - if (reply==QtWidgets.QMessageBox.ButtonRole.YesRole): + if reply == QtWidgets.QMessageBox.ButtonRole.YesRole: with open(_datadir + "serverlist.json", "w") as server_file: - server_file.write(json.dumps(default_server_list, indent = 4) ) + server_file.write(json.dumps(default_server_list, indent=4)) server_file.close() else: - PchumLog.warning("Failed to load server list because serverlist.json doesn't exist, " - "this isn't an issue if this is the first time Pesterchum has been started.") + PchumLog.warning( + "Failed to load server list because serverlist.json doesn't exist, " + "this isn't an issue if this is the first time Pesterchum has been started." + ) with open(_datadir + "serverlist.json", "w") as server_file: - server_file.write(json.dumps(default_server_list, indent = 4) ) - server_file.close() + server_file.write(json.dumps(default_server_list, indent=4)) + server_file.close() self.chooseServer() def removeServer(self): @@ -3405,20 +3788,19 @@ class PesterWindow(MovingWindow): return 1 selected_entry = None - + for i in range(len(server_list_obj)): if server_list_obj[i]["server"] == self.removeServerBox.currentText(): selected_entry = i if selected_entry != None: server_list_obj.pop(selected_entry) - + try: with open(_datadir + "serverlist.json", "w") as server_file: - server_file.write(json.dumps(server_list_obj, indent = 4)) + server_file.write(json.dumps(server_list_obj, indent=4)) server_file.close() except: - PchumLog.error("failed") - + PchumLog.error("failed") self.chooseServer() @@ -3427,45 +3809,53 @@ class PesterWindow(MovingWindow): # Text input self.customServerPrompt_qline = QtWidgets.QLineEdit(self) self.customServerPrompt_qline.setMinimumWidth(200) - + # Widget 1 self.customServerDialog = QtWidgets.QDialog() - + # Buttons cancel = QtWidgets.QPushButton("CANCEL") ok = QtWidgets.QPushButton("OK") - + ok.setDefault(True) ok.clicked.connect(self.customServerDialog.accept) cancel.clicked.connect(self.customServerDialog.reject) - - #Layout + + # Layout layout = QtWidgets.QHBoxLayout() - + self.TLS_checkbox = QtWidgets.QCheckBox(self) self.TLS_checkbox.setChecked(True) - TLS_checkbox_label = QtWidgets.QLabel(":33 < Check if you want to connect over TLS!!") - TLS_checkbox_label.setStyleSheet("QLabel { color: #416600; font-weight: bold;}") + TLS_checkbox_label = QtWidgets.QLabel( + ":33 < Check if you want to connect over TLS!!" + ) + TLS_checkbox_label.setStyleSheet( + "QLabel { color: #416600; font-weight: bold;}" + ) TLS_layout = QtWidgets.QHBoxLayout() TLS_layout.addWidget(TLS_checkbox_label) TLS_layout.addWidget(self.TLS_checkbox) - + layout.addWidget(cancel) layout.addWidget(ok) main_layout = QtWidgets.QVBoxLayout() - nep_prompt = QtWidgets.QLabel(":33 < Please put in the server's address in the format HOSTNAME:PORT\n:33 < Fur example, irc.pesterchum.xyz:6697") + nep_prompt = QtWidgets.QLabel( + ":33 < Please put in the server's address in the format HOSTNAME:PORT\n:33 < Fur example, irc.pesterchum.xyz:6697" + ) nep_prompt.setStyleSheet("QLabel { color: #416600; font-weight: bold;}") main_layout.addWidget(nep_prompt) main_layout.addWidget(self.customServerPrompt_qline) main_layout.addLayout(TLS_layout) main_layout.addLayout(layout) - + self.customServerDialog.setLayout(main_layout) - + # Theme - self.customServerDialog.setStyleSheet(self.theme["main/defaultwindow/style"]) + self.customServerDialog.setStyleSheet( + self.theme["main/defaultwindow/style"] + ) self.customServerDialog.setWindowIcon(PesterIcon(self.theme["main/icon"])) - + # Connect self.customServerDialog.accepted.connect(self.updateServerJson) self.customServerDialog.rejected.connect(self.chooseServer) @@ -3473,7 +3863,7 @@ class PesterWindow(MovingWindow): # Show self.customServerDialog.show() self.customServerDialog.setFocus() - + elif self.serverBox.currentText() == "Remove a server [Prompt]": # Read servers. server_list_items = [] @@ -3489,27 +3879,27 @@ class PesterWindow(MovingWindow): self.chooseServerAskedToReset = True self.resetServerlist() return 1 - + PchumLog.info("server_list_items: " + str(server_list_items)) - + # Widget 1 self.chooseRemoveServerWidged = QtWidgets.QDialog() - + # removeServerBox self.removeServerBox = QtWidgets.QComboBox() for i in range(len(server_list_items)): self.removeServerBox.addItem(server_list_items[i]) - + # Buttons cancel = QtWidgets.QPushButton("CANCEL") ok = QtWidgets.QPushButton("OK") - + ok.setDefault(True) ok.clicked.connect(self.chooseRemoveServerWidged.accept) cancel.clicked.connect(self.chooseRemoveServerWidged.reject) - - #Layout + + # Layout layout = QtWidgets.QHBoxLayout() layout.addWidget(cancel) layout.addWidget(ok) @@ -3517,13 +3907,17 @@ class PesterWindow(MovingWindow): main_layout.addWidget(QtWidgets.QLabel("Please choose a server to remove.")) main_layout.addWidget(self.removeServerBox) main_layout.addLayout(layout) - + self.chooseRemoveServerWidged.setLayout(main_layout) - + # Theme - self.chooseRemoveServerWidged.setStyleSheet(self.theme["main/defaultwindow/style"]) - self.chooseRemoveServerWidged.setWindowIcon(PesterIcon(self.theme["main/icon"])) - + self.chooseRemoveServerWidged.setStyleSheet( + self.theme["main/defaultwindow/style"] + ) + self.chooseRemoveServerWidged.setWindowIcon( + PesterIcon(self.theme["main/icon"]) + ) + # Connect self.chooseRemoveServerWidged.accepted.connect(self.removeServer) self.chooseRemoveServerWidged.rejected.connect(self.chooseServer) @@ -3540,19 +3934,19 @@ class PesterWindow(MovingWindow): server_obj = json.loads(read_file) selected_entry = None - + for i in range(len(server_obj)): if server_obj[i]["server"] == self.serverBox.currentText(): selected_entry = i - + try: with open(_datadir + "server.json", "w") as server_file: json_server_file = { - "server": server_obj[selected_entry]["server"], - "port": server_obj[selected_entry]["port"], - "TLS": server_obj[selected_entry]["TLS"] - } - server_file.write(json.dumps(json_server_file, indent = 4) ) + "server": server_obj[selected_entry]["server"], + "port": server_obj[selected_entry]["port"], + "TLS": server_obj[selected_entry]["TLS"], + } + server_file.write(json.dumps(json_server_file, indent=4)) server_file.close() except: PchumLog.error("Failed to set server :(") @@ -3562,7 +3956,7 @@ class PesterWindow(MovingWindow): self.irc.start() self.parent.reconnectok = False self.parent.showLoading(self.parent.widget) - self.show() # Not required? + self.show() # Not required? self.setFocus() def chooseServer(self): @@ -3580,30 +3974,30 @@ class PesterWindow(MovingWindow): self.chooseServerAskedToReset = True self.resetServerlist() return 1 - + PchumLog.info("server_list_items: " + str(server_list_items)) - + # Widget 1 self.chooseServerWidged = QtWidgets.QDialog() - + # Serverbox self.serverBox = QtWidgets.QComboBox() for i in range(len(server_list_items)): self.serverBox.addItem(server_list_items[i]) - + self.serverBox.addItem("Add a server [Prompt]") self.serverBox.addItem("Remove a server [Prompt]") - + # Buttons cancel = QtWidgets.QPushButton("CANCEL") ok = QtWidgets.QPushButton("OK") - + ok.setDefault(True) ok.clicked.connect(self.chooseServerWidged.accept) cancel.clicked.connect(self.chooseServerWidged.reject) - - #Layout + + # Layout layout = QtWidgets.QHBoxLayout() layout.addWidget(cancel) layout.addWidget(ok) @@ -3611,17 +4005,18 @@ class PesterWindow(MovingWindow): main_layout.addWidget(QtWidgets.QLabel("Please choose a server.")) main_layout.addWidget(self.serverBox) main_layout.addLayout(layout) - + self.chooseServerWidged.setLayout(main_layout) - + # Theme self.chooseServerWidged.setStyleSheet(self.theme["main/defaultwindow/style"]) self.chooseServerWidged.setWindowIcon(PesterIcon(self.theme["main/icon"])) - + # Connect self.chooseServerWidged.accepted.connect(self.setServer) - self.chooseServerWidged.rejected.connect(self.killApp, - QtCore.Qt.ConnectionType.QueuedConnection) + self.chooseServerWidged.rejected.connect( + self.killApp, QtCore.Qt.ConnectionType.QueuedConnection + ) # Show self.chooseServerWidged.show() @@ -3632,56 +4027,62 @@ class PesterWindow(MovingWindow): # Prompt user to connect anyway msgbox = QtWidgets.QMessageBox() try: - msgbox.setStyleSheet("QMessageBox{ %s }" - % self.theme["main/defaultwindow/style"]) + msgbox.setStyleSheet( + "QMessageBox{ %s }" % self.theme["main/defaultwindow/style"] + ) except: pass msgbox.setIcon(QtWidgets.QMessageBox.Icon.Warning) msgbox.setText("Server certificate validation failed") - msgbox.setInformativeText("Reason: \"%s (%s)\"" % (e.verify_message, e.verify_code) - + "\n\nConnect anyway?") - msgbox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Yes - | QtWidgets.QMessageBox.StandardButton.No) + msgbox.setInformativeText( + 'Reason: "%s (%s)"' % (e.verify_message, e.verify_code) + + "\n\nConnect anyway?" + ) + msgbox.setStandardButtons( + QtWidgets.QMessageBox.StandardButton.Yes + | QtWidgets.QMessageBox.StandardButton.No + ) msgbox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.No) ret = msgbox.exec() if ret == QtWidgets.QMessageBox.StandardButton.Yes: - self.parent.restartIRC(verify_hostname=False) + self.parent.restartIRC(verify_hostname=False) - pcUpdate = QtCore.pyqtSignal('QString', 'QString') + pcUpdate = QtCore.pyqtSignal("QString", "QString") closeToTraySignal = QtCore.pyqtSignal() - newConvoStarted = QtCore.pyqtSignal('QString', bool, name="newConvoStarted") - sendMessage = QtCore.pyqtSignal('QString', 'QString') - sendNotice = QtCore.pyqtSignal('QString', 'QString') - sendCTCP = QtCore.pyqtSignal('QString', 'QString') - convoClosed = QtCore.pyqtSignal('QString') + newConvoStarted = QtCore.pyqtSignal("QString", bool, name="newConvoStarted") + sendMessage = QtCore.pyqtSignal("QString", "QString") + sendNotice = QtCore.pyqtSignal("QString", "QString") + sendCTCP = QtCore.pyqtSignal("QString", "QString") + convoClosed = QtCore.pyqtSignal("QString") profileChanged = QtCore.pyqtSignal() animationSetting = QtCore.pyqtSignal(bool) moodRequest = QtCore.pyqtSignal(PesterProfile) moodsRequest = QtCore.pyqtSignal(PesterList) moodUpdated = QtCore.pyqtSignal() requestChannelList = QtCore.pyqtSignal() - requestNames = QtCore.pyqtSignal('QString') - namesUpdated = QtCore.pyqtSignal('QString') - modesUpdated = QtCore.pyqtSignal('QString', 'QString') - userPresentSignal = QtCore.pyqtSignal('QString','QString','QString') + requestNames = QtCore.pyqtSignal("QString") + namesUpdated = QtCore.pyqtSignal("QString") + modesUpdated = QtCore.pyqtSignal("QString", "QString") + userPresentSignal = QtCore.pyqtSignal("QString", "QString", "QString") mycolorUpdated = QtCore.pyqtSignal() trayIconSignal = QtCore.pyqtSignal(int) - blockedChum = QtCore.pyqtSignal('QString') - unblockedChum = QtCore.pyqtSignal('QString') - kickUser = QtCore.pyqtSignal('QString', 'QString') - joinChannel = QtCore.pyqtSignal('QString') - leftChannel = QtCore.pyqtSignal('QString') - setChannelMode = QtCore.pyqtSignal('QString', 'QString', 'QString') - channelNames = QtCore.pyqtSignal('QString') - inviteChum = QtCore.pyqtSignal('QString', 'QString') - inviteOnlyChan = QtCore.pyqtSignal('QString') - forbiddenChan = QtCore.pyqtSignal('QString', 'QString') + blockedChum = QtCore.pyqtSignal("QString") + unblockedChum = QtCore.pyqtSignal("QString") + kickUser = QtCore.pyqtSignal("QString", "QString") + joinChannel = QtCore.pyqtSignal("QString") + leftChannel = QtCore.pyqtSignal("QString") + setChannelMode = QtCore.pyqtSignal("QString", "QString", "QString") + channelNames = QtCore.pyqtSignal("QString") + inviteChum = QtCore.pyqtSignal("QString", "QString") + inviteOnlyChan = QtCore.pyqtSignal("QString") + forbiddenChan = QtCore.pyqtSignal("QString", "QString") closeSignal = QtCore.pyqtSignal() disconnectIRC = QtCore.pyqtSignal() gainAttention = QtCore.pyqtSignal(QtWidgets.QWidget) pingServer = QtCore.pyqtSignal() setAway = QtCore.pyqtSignal(bool) - killSomeQuirks = QtCore.pyqtSignal('QString', 'QString') + killSomeQuirks = QtCore.pyqtSignal("QString", "QString") + class PesterTray(QtWidgets.QSystemTrayIcon): def __init__(self, icon, mainwindow, parent): @@ -3694,10 +4095,12 @@ class PesterTray(QtWidgets.QSystemTrayIcon): self.setIcon(PesterIcon(self.mainwindow.theme["main/icon"])) else: self.setIcon(PesterIcon(self.mainwindow.theme["main/newmsgicon"])) + @QtCore.pyqtSlot() def mainWindowClosed(self): self.hide() + class MainProgram(QtCore.QObject): def __init__(self): super(MainProgram, self).__init__() @@ -3712,6 +4115,7 @@ class MainProgram(QtCore.QObject): # Windows computer at the moment. Hopefully it'll work. # See https://stackoverflow.com/a/1552105 for more details. from ctypes import windll + # Note that this has to be unicode. wid = "mspa.homestuck.pesterchum.314" # Designate this as a separate process - i.e., tell Windows that @@ -3729,46 +4133,47 @@ 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:]) - + # Check if the user is a silly little guy - for folder in ['smilies', 'themes']: + for folder in ["smilies", "themes"]: if not os.path.isdir(folder): msgbox = QtWidgets.QMessageBox() - msg = ("'%s' folder not found, Pesterchum will " - "probably not function correctly." - "\nIf this is an excecutable build, " - "verify you extracted the zipfile." % folder) + msg = ( + "'%s' folder not found, Pesterchum will " + "probably not function correctly." + "\nIf this is an excecutable build, " + "verify you extracted the zipfile." % folder + ) msgbox.setWindowTitle("SK1LL 1SSU3 >:[") msgbox.setInformativeText(msg) msgbox.setIcon(QtWidgets.QMessageBox.Icon.Critical) msgbox.exec() - - + # If we're using pygame for sound we need to init - if 'pygame' in sys.modules: + if "pygame" in sys.modules: # we could set the frequency higher but i love how cheesy it sounds try: pygame.mixer.init() except (pygame.error, Exception) as err: print("Warning: No sound! (pygame error: %s)" % err) - - self.widget = PesterWindow(options, parent=self, app=self.app) - #self.widget.show() <== Already called in showLoading() - self.trayicon = PesterTray(PesterIcon(self.widget.theme["main/icon"]), - self.widget, - self.app) + self.widget = PesterWindow(options, parent=self, app=self.app) + # self.widget.show() <== Already called in showLoading() + + self.trayicon = PesterTray( + PesterIcon(self.widget.theme["main/icon"]), self.widget, self.app + ) self.traymenu = QtWidgets.QMenu() - + moodMenu = self.traymenu.addMenu("SET MOOD") moodCategories = {} for k in Mood.moodcats: moodCategories[k] = moodMenu.addMenu(k.upper()) self.moodactions = {} - for (i,m) in enumerate(Mood.moods): + for (i, m) in enumerate(Mood.moods): maction = QAction(m.upper(), self) mobj = PesterMoodAction(i, self.widget.moods.updateMood) maction.triggered.connect(mobj.updateMood) @@ -3777,41 +4182,47 @@ class MainProgram(QtCore.QObject): miniAction = QAction("MINIMIZE", self) miniAction.triggered.connect(self.widget.showMinimized) exitAction = QAction("EXIT", self) - exitAction.triggered.connect(self.widget.killApp, QtCore.Qt.ConnectionType.QueuedConnection) + exitAction.triggered.connect( + self.widget.killApp, QtCore.Qt.ConnectionType.QueuedConnection + ) self.traymenu.addAction(miniAction) self.traymenu.addAction(exitAction) self.trayicon.setContextMenu(self.traymenu) self.trayicon.show() - self.trayicon.activated[QtWidgets.QSystemTrayIcon.ActivationReason].connect(self.widget.systemTrayActivated) + self.trayicon.activated[QtWidgets.QSystemTrayIcon.ActivationReason].connect( + self.widget.systemTrayActivated + ) self.widget.trayIconSignal[int].connect(self.trayicon.changeTrayIcon) self.widget.closeToTraySignal.connect(self.trayiconShow) self.widget.closeSignal.connect(self.trayicon.mainWindowClosed) self.trayicon.messageClicked.connect(self.trayMessageClick) self.attempts = 0 - + # but it's at least better than the way it was before. self.irc = PesterIRC(self.widget.config, self.widget) self.connectWidgets(self.irc, self.widget) - self.widget.passIRC(self.irc) # Maybe this is absolutely terrible in practice, but screw it. + 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.lastWindowClosed.connect(self.lastWindow) self.app.aboutToQuit.connect(self.death) def death(self): # app murder in progress - #print("death inbound") - if hasattr(self, 'widget'): + # print("death inbound") + if hasattr(self, "widget"): self.widget.killApp() - #def lastWindow(self): + # 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) @@ -3820,141 +4231,144 @@ 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.")) + self.trayicon.showMessage( + "Pesterchum", + ( + "Pesterchum is still running in the system tray." + "\n" + "Right click to close it." + ), + ) @QtCore.pyqtSlot() def trayMessageClick(self): - self.widget.config.set('traymsg', False) + self.widget.config.set("traymsg", False) + + widget2irc = [ + ("sendMessage(QString, QString)", "sendMessage(QString, QString)"), + ("sendNotice(QString, QString)", "sendNotice(QString, QString)"), + ("sendCTCP(QString, QString)", "sendCTCP(QString, QString)"), + ("newConvoStarted(QString, bool)", "startConvo(QString, bool)"), + ("convoClosed(QString)", "endConvo(QString)"), + ("profileChanged()", "updateProfile()"), + ("moodRequest(PyQt_PyObject)", "getMood(PyQt_PyObject)"), + ("moodsRequest(PyQt_PyObject)", "getMoods(PyQt_PyObject)"), + ("moodUpdated()", "updateMood()"), + ("mycolorUpdated()", "updateColor()"), + ("blockedChum(QString)", "blockedChum(QString)"), + ("unblockedChum(QString)", "unblockedChum(QString)"), + ("requestNames(QString)", "requestNames(QString)"), + ("requestChannelList()", "requestChannelList()"), + ("joinChannel(QString)", "joinChannel(QString)"), + ("leftChannel(QString)", "leftChannel(QString)"), + ("kickUser(QString, QString)", "kickUser(QString, QString)"), + ( + "setChannelMode(QString, QString, QString)", + "setChannelMode(QString, QString, QString)", + ), + ("channelNames(QString)", "channelNames(QString)"), + ("inviteChum(QString, QString)", "inviteChum(QString, QString)"), + ("pingServer()", "pingServer()"), + ("setAway(bool)", "setAway(bool)"), + ("killSomeQuirks(QString, QString)", "killSomeQuirks(QString, QString)"), + ("disconnectIRC()", "disconnectIRC()"), + ] + # IRC --> Main window + irc2widget = [ + ("connected()", "connected()"), + ( + "moodUpdated(QString, PyQt_PyObject)", + "updateMoodSlot(QString, PyQt_PyObject)", + ), + ( + "colorUpdated(QString, QtGui.QColor)", + "updateColorSlot(QString, QtGui.QColor)", + ), + ("messageReceived(QString, QString)", "deliverMessage(QString, QString)"), + ( + "memoReceived(QString, QString, QString)", + "deliverMemo(QString, QString, QString)", + ), + ("noticeReceived(QString, QString)", "deliverNotice(QString, QString)"), + ("inviteReceived(QString, QString)", "deliverInvite(QString, QString)"), + ("nickCollision(QString, QString)", "nickCollision(QString, QString)"), + ("getSvsnickedOn(QString, QString)", "getSvsnickedOn(QString, QString)"), + ("myHandleChanged(QString)", "myHandleChanged(QString)"), + ( + "namesReceived(QString, PyQt_PyObject)", + "updateNames(QString, PyQt_PyObject)", + ), + ( + "userPresentUpdate(QString, QString, QString)", + "userPresentUpdate(QString, QString, QString)", + ), + ("channelListReceived(PyQt_PyObject)", "updateChannelList(PyQt_PyObject)"), + ( + "timeCommand(QString, QString, QString)", + "timeCommand(QString, QString, QString)", + ), + ("chanInviteOnly(QString)", "chanInviteOnly(QString)"), + ("modesUpdated(QString, QString)", "modesUpdated(QString, QString)"), + ("cannotSendToChan(QString, QString)", "cannotSendToChan(QString, QString)"), + ("tooManyPeeps()", "tooManyPeeps()"), + ( + "quirkDisable(QString, QString, QString)", + "quirkDisable(QString, QString, QString)", + ), + ("forbiddenchannel(QString)", "forbiddenchannel(QString)"), + ] - widget2irc = [('sendMessage(QString, QString)', - 'sendMessage(QString, QString)'), - ('sendNotice(QString, QString)', - 'sendNotice(QString, QString)'), - ('sendCTCP(QString, QString)', - 'sendCTCP(QString, QString)'), - ('newConvoStarted(QString, bool)', - 'startConvo(QString, bool)'), - ('convoClosed(QString)', - 'endConvo(QString)'), - ('profileChanged()', - 'updateProfile()'), - ('moodRequest(PyQt_PyObject)', - 'getMood(PyQt_PyObject)'), - ('moodsRequest(PyQt_PyObject)', - 'getMoods(PyQt_PyObject)'), - ('moodUpdated()', 'updateMood()'), - ('mycolorUpdated()','updateColor()'), - ('blockedChum(QString)', 'blockedChum(QString)'), - ('unblockedChum(QString)', 'unblockedChum(QString)'), - ('requestNames(QString)','requestNames(QString)'), - ('requestChannelList()', 'requestChannelList()'), - ('joinChannel(QString)', 'joinChannel(QString)'), - ('leftChannel(QString)', 'leftChannel(QString)'), - ('kickUser(QString, QString)', - 'kickUser(QString, QString)'), - ('setChannelMode(QString, QString, QString)', - 'setChannelMode(QString, QString, QString)'), - ('channelNames(QString)', - 'channelNames(QString)'), - ('inviteChum(QString, QString)', - 'inviteChum(QString, QString)'), - ('pingServer()', 'pingServer()'), - ('setAway(bool)', 'setAway(bool)'), - ('killSomeQuirks(QString, QString)', - 'killSomeQuirks(QString, QString)'), - ('disconnectIRC()', 'disconnectIRC()') - ] -# IRC --> Main window - irc2widget = [('connected()', 'connected()'), - ('moodUpdated(QString, PyQt_PyObject)', - 'updateMoodSlot(QString, PyQt_PyObject)'), - ('colorUpdated(QString, QtGui.QColor)', - 'updateColorSlot(QString, QtGui.QColor)'), - ('messageReceived(QString, QString)', - 'deliverMessage(QString, QString)'), - ('memoReceived(QString, QString, QString)', - 'deliverMemo(QString, QString, QString)'), - ('noticeReceived(QString, QString)', - 'deliverNotice(QString, QString)'), - ('inviteReceived(QString, QString)', - 'deliverInvite(QString, QString)'), - ('nickCollision(QString, QString)', - 'nickCollision(QString, QString)'), - ('getSvsnickedOn(QString, QString)', - 'getSvsnickedOn(QString, QString)'), - ('myHandleChanged(QString)', - 'myHandleChanged(QString)'), - ('namesReceived(QString, PyQt_PyObject)', - 'updateNames(QString, PyQt_PyObject)'), - ('userPresentUpdate(QString, QString, QString)', - 'userPresentUpdate(QString, QString, QString)'), - ('channelListReceived(PyQt_PyObject)', - 'updateChannelList(PyQt_PyObject)'), - ('timeCommand(QString, QString, QString)', - 'timeCommand(QString, QString, QString)'), - ('chanInviteOnly(QString)', - 'chanInviteOnly(QString)'), - ('modesUpdated(QString, QString)', - 'modesUpdated(QString, QString)'), - ('cannotSendToChan(QString, QString)', - 'cannotSendToChan(QString, QString)'), - ('tooManyPeeps()', - 'tooManyPeeps()'), - ('quirkDisable(QString, QString, QString)', - 'quirkDisable(QString, QString, QString)'), - ('forbiddenchannel(QString)', - 'forbiddenchannel(QString)') - ] def ircQtConnections(self, irc, widget): # IRC --> Main window - return ((widget.sendMessage, irc.sendMessage), - (widget.sendNotice, irc.sendNotice), - (widget.sendCTCP, irc.sendCTCP), - (widget.newConvoStarted, irc.startConvo), - (widget.convoClosed, irc.endConvo), - (widget.profileChanged, irc.updateProfile), - (widget.moodRequest, irc.getMood), - (widget.moodsRequest, irc.getMoods), - (widget.moodUpdated, irc.updateMood), - (widget.mycolorUpdated, irc.updateColor), - (widget.blockedChum, irc.blockedChum), - (widget.unblockedChum, irc.unblockedChum), - (widget.requestNames, irc.requestNames), - (widget.requestChannelList, irc.requestChannelList), - (widget.joinChannel, irc.joinChannel), - (widget.leftChannel, irc.leftChannel), - (widget.kickUser, irc.kickUser), - (widget.setChannelMode, irc.setChannelMode), - (widget.channelNames, irc.channelNames), - (widget.inviteChum, irc.inviteChum), - (widget.pingServer, irc.pingServer), - (widget.setAway, irc.setAway), - (widget.killSomeQuirks, irc.killSomeQuirks), - (widget.disconnectIRC, irc.disconnectIRC), - # Main window --> IRC - (irc.connected, widget.connected), - (irc.askToConnect, widget.connectAnyway), - (irc.moodUpdated, widget.updateMoodSlot), - (irc.colorUpdated, widget.updateColorSlot), - (irc.messageReceived, widget.deliverMessage), - (irc.memoReceived, widget.deliverMemo), - (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), - (irc.channelListReceived, widget.updateChannelList), - (irc.timeCommand, widget.timeCommand), - (irc.chanInviteOnly, widget.chanInviteOnly), - (irc.modesUpdated, widget.modesUpdated), - (irc.cannotSendToChan, widget.cannotSendToChan), - (irc.forbiddenchannel, widget.forbiddenchannel), - (irc.tooManyPeeps, widget.tooManyPeeps), - (irc.quirkDisable, widget.quirkDisable)) + return ( + (widget.sendMessage, irc.sendMessage), + (widget.sendNotice, irc.sendNotice), + (widget.sendCTCP, irc.sendCTCP), + (widget.newConvoStarted, irc.startConvo), + (widget.convoClosed, irc.endConvo), + (widget.profileChanged, irc.updateProfile), + (widget.moodRequest, irc.getMood), + (widget.moodsRequest, irc.getMoods), + (widget.moodUpdated, irc.updateMood), + (widget.mycolorUpdated, irc.updateColor), + (widget.blockedChum, irc.blockedChum), + (widget.unblockedChum, irc.unblockedChum), + (widget.requestNames, irc.requestNames), + (widget.requestChannelList, irc.requestChannelList), + (widget.joinChannel, irc.joinChannel), + (widget.leftChannel, irc.leftChannel), + (widget.kickUser, irc.kickUser), + (widget.setChannelMode, irc.setChannelMode), + (widget.channelNames, irc.channelNames), + (widget.inviteChum, irc.inviteChum), + (widget.pingServer, irc.pingServer), + (widget.setAway, irc.setAway), + (widget.killSomeQuirks, irc.killSomeQuirks), + (widget.disconnectIRC, irc.disconnectIRC), + # Main window --> IRC + (irc.connected, widget.connected), + (irc.askToConnect, widget.connectAnyway), + (irc.moodUpdated, widget.updateMoodSlot), + (irc.colorUpdated, widget.updateColorSlot), + (irc.messageReceived, widget.deliverMessage), + (irc.memoReceived, widget.deliverMemo), + (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), + (irc.channelListReceived, widget.updateChannelList), + (irc.timeCommand, widget.timeCommand), + (irc.chanInviteOnly, widget.chanInviteOnly), + (irc.modesUpdated, widget.modesUpdated), + (irc.cannotSendToChan, widget.cannotSendToChan), + (irc.forbiddenchannel, widget.forbiddenchannel), + (irc.tooManyPeeps, widget.tooManyPeeps), + (irc.quirkDisable, widget.quirkDisable), + ) + def connectWidgets(self, irc, widget): irc.finished.connect(self.restartIRC) irc.connected.connect(self.connected) @@ -3983,10 +4397,10 @@ class MainProgram(QtCore.QObject): break newmsg.append(msg[:s]) newmsg.append("\n") - msg = msg[s+1:] + msg = msg[s + 1 :] newmsg.append(msg) msg = "".join(newmsg) - if hasattr(self.widget, 'loadingscreen') and widget.loadingscreen: + if hasattr(self.widget, "loadingscreen") and widget.loadingscreen: widget.loadingscreen.loadinglabel.setText(msg) if self.reconnectok: widget.loadingscreen.showReconnect() @@ -3997,9 +4411,11 @@ class MainProgram(QtCore.QObject): widget.loadingscreen.loadinglabel.setText(msg) widget.loadingscreen.rejected.connect(widget.app.quit) self.widget.loadingscreen.tryAgain.connect(self.tryAgain) - if (hasattr(self, 'irc') + if ( + hasattr(self, "irc") and self.irc.registeredIRC - and self.irc.unresponsive == False): + and self.irc.unresponsive == False + ): return if self.reconnectok: widget.loadingscreen.showReconnect() @@ -4010,6 +4426,7 @@ class MainProgram(QtCore.QObject): @QtCore.pyqtSlot() def connected(self): self.attempts = 0 + @QtCore.pyqtSlot() def tryAgain(self): if not self.reconnectok: @@ -4018,20 +4435,23 @@ class MainProgram(QtCore.QObject): self.widget.loadingscreen.done(QtWidgets.QDialog.DialogCode.Accepted) self.widget.loadingscreen = None self.attempts += 1 - if hasattr(self, 'irc') and self.irc: + if hasattr(self, "irc") and self.irc: self.irc.disconnectIRC() else: self.restartIRC() + @QtCore.pyqtSlot() def restartIRC(self, verify_hostname=True): - if hasattr(self, 'irc') and self.irc: + if hasattr(self, "irc") and self.irc: self.disconnectWidgets(self.irc, self.widget) stop = self.irc.stopIRC del self.irc else: stop = None if stop is None: - self.irc = PesterIRC(self.widget.config, self.widget, verify_hostname=verify_hostname) + self.irc = PesterIRC( + self.widget.config, self.widget, verify_hostname=verify_hostname + ) self.connectWidgets(self.irc, self.widget) self.irc.start() if self.attempts == 1: @@ -4068,7 +4488,7 @@ class MainProgram(QtCore.QObject): except Exception as e: print(e) return options - + return options def uncaughtException(self, exc, value, tb): @@ -4081,24 +4501,33 @@ class MainProgram(QtCore.QObject): try: lt = time.localtime() lt_str = time.strftime("%Y-%m-%d %H-%M", lt) - f = open(os.path.join(_datadir, 'errorlogs', ('pestererror %s.log' % lt_str)), 'a') + f = open( + os.path.join( + _datadir, "errorlogs", ("pestererror %s.log" % lt_str) + ), + "a", + ) traceback.print_tb(tb, file=f) f.close() except Exception as e: print(str(e)) - + # Show msgbox msgbox = QtWidgets.QMessageBox() msgbox.setIcon(QtWidgets.QMessageBox.Icon.Critical) try: - msgbox.setStyleSheet("QMessageBox{" + self.widget.theme["main/defaultwindow/style"] + "}") + msgbox.setStyleSheet( + "QMessageBox{" + self.widget.theme["main/defaultwindow/style"] + "}" + ) except Exception as e: print(str(e)) - msgbox.setStyleSheet("background-color: red; color: black; font-size: x-large;") - msgbox.setText("An uncaught exception occurred: %s \n%s \n%s " - % (exc, - value, - ''.join(traceback.format_tb(tb)))) + msgbox.setStyleSheet( + "background-color: red; color: black; font-size: x-large;" + ) + msgbox.setText( + "An uncaught exception occurred: %s \n%s \n%s " + % (exc, value, "".join(traceback.format_tb(tb))) + ) msgbox.exec() except Exception as e: print("failed to process uncaught except: " + str(e)) @@ -4107,12 +4536,14 @@ class MainProgram(QtCore.QObject): def run(self): sys.exit(self.app.exec()) + def _retrieveGlobals(): # NOTE: Yes, this is a terrible kludge so that the console can work # properly. I'm open to alternatives. return globals() -#def main(): + +# def main(): # pesterchum = MainProgram() # pesterchum.run() diff --git a/pnc/__init__.py b/pnc/__init__.py index 0085223..9e4eee4 100644 --- a/pnc/__init__.py +++ b/pnc/__init__.py @@ -5,4 +5,4 @@ ##from __future__ import division ##from __future__ import absolute_import # JUST in case. -# No __all__ or similar, for now. \ No newline at end of file +# No __all__ or similar, for now. diff --git a/pnc/dep/attrdict.py b/pnc/dep/attrdict.py index c8a1bb4..6b29762 100644 --- a/pnc/dep/attrdict.py +++ b/pnc/dep/attrdict.py @@ -4,29 +4,31 @@ class AttrDict(dict): """A dictionary with attribute-style access. It maps attribute access to the real dictionary. - + Note that accesses to preexisting (e.g. class inherited) or reserved attributes are handled as they would be normally, and will not be overwritten. Overload _is_reserved if you want to change this.""" + def __init__(self, init={}): super(AttrDict, self).__init__(init) def __getstate__(self): return list(self.__dict__.items()) + def __setstate__(self, items): - for key, val in items: self.__dict__[key] = val + for key, val in items: + self.__dict__[key] = val def __repr__(self): - return "{0}({1})".format( - type(self).__name__, - super(AttrDict, self).__repr__() - ) + return "{0}({1})".format(type(self).__name__, super(AttrDict, self).__repr__()) def __setitem__(self, name, value): return super(AttrDict, self).__setitem__(name, value) + def __getitem__(self, name): return super(AttrDict, self).__getitem__(name) + def __delitem__(self, name): return super(AttrDict, self).__delitem__(name) @@ -78,7 +80,8 @@ class AttrDict(dict): # See __setattr__. return object.__delattr__(self, name) else: - try: del self[name] + try: + del self[name] except KeyError as err: raise AttributeError(str(err)) @@ -86,13 +89,18 @@ class AttrDict(dict): def _is_reserved(name): """Check if an attribute name is reserved for system use.""" # A very simple method. - result = name[:2] == name[-2:] == '__' + result = name[:2] == name[-2:] == "__" return result - def copy(self): return type(self)(self) + def copy(self): + return type(self)(self) + __copy__ = copy + + ## end of http://code.activestate.com/recipes/473786/ }}} + class DefAttrDict(AttrDict): default_factory = None @@ -106,8 +114,8 @@ class DefAttrDict(AttrDict): self.default_factory, # We skip normal processing here, since AttrDict provides basic # repr for classes in general, which we don't want. - dict.__repr__(self) - ) + dict.__repr__(self), + ) def __getitem__(self, name): try: @@ -130,7 +138,10 @@ class DefAttrDict(AttrDict): raise return result - def copy(self): return type(self)(self.default_factory, self) + def copy(self): + return type(self)(self.default_factory, self) + __copy__ = copy + # vim: set autoindent ts=4 sts=4 sw=4 textwidth=79 expandtab: diff --git a/pnc/lexercon.py b/pnc/lexercon.py index d6ab20a..8337404 100644 --- a/pnc/lexercon.py +++ b/pnc/lexercon.py @@ -20,7 +20,6 @@ except NameError: # function appropriate to the given format - e.g. CTag.convert_pchum. - class Lexeme(object): def __init__(self, string, origin): # The 'string' property is just what it came from; the original @@ -28,21 +27,26 @@ class Lexeme(object): # shouldn't be. self.string = string self.origin = origin + def __str__(self): ##return self.string return self.convert(self.origin) + def __len__(self): ##return len(self.string) return len(str(self)) + def convert(self, format): # This is supposed to be overwritten by subclasses raise NotImplementedError + def rebuild(self, format): """Builds a copy of the owning Lexeme as if it had 'come from' a different original format, and returns the result.""" # TODO: This. Need to decide whether overloading will be required for # nearly every single subclass.... raise NotImplementedError + @classmethod def from_mo(cls, mo, origin): raise NotImplementedError @@ -52,6 +56,7 @@ class Message(Lexeme): """An object designed to represent a message, possibly containing Lexeme objects in their native form as well. Intended to be a combination of a list and a string, combining the former with the latter's methods.""" + def __init__(self, contents, origin): lexer = Lexer.lexer_for(origin)() working = lexer.lex(contents) @@ -69,7 +74,8 @@ class Message(Lexeme): working[i] = elt self.origin = origin self.contents = working - self.string = ''.join(lexer.list_convert(working)) + self.string = "".join(lexer.list_convert(working)) + # TODO: Finish all the rest of this. @@ -81,40 +87,51 @@ class Specifier(Lexeme): # If this form has a more compact form, use it compact = False + # Made so that certain odd message-ish things have a place to go. May have its # class changed later. class Chunk(Specifier): pass + class FTag(Specifier): pass + + class CTag(Specifier): """Denotes the beginning or end of a color change.""" + sets_color = True + def __init__(self, string, origin, color): super(CTag, self).__init__(string, origin) # So we can also have None if isinstance(color, tuple): - if len(color) < 2: raise ValueError + if len(color) < 2: + raise ValueError self.color, self.bg_color = color[:2] else: self.color = color self.bg_color = None + def has_color(self): if self.color is not None or self.bg_color is not None: return True return False + def convert(self, format): - text = '' + text = "" color = self.color bg = self.bg_color if format == "irc": # Put in the control character for a color code. - text = '\x03' + text = "\x03" if color: text += color.ccode - if bg: text += ',' + bg.ccode - elif bg: text += "99," + bg.ccode + if bg: + text += "," + bg.ccode + elif bg: + text += "99," + bg.ccode elif format == "pchum": if not color: text = "" @@ -138,7 +155,7 @@ class CTag(Specifier): # far; use it. text = hxs elif format == "plaintext": - text = '' + text = "" return text @classmethod @@ -147,10 +164,14 @@ class CTag(Specifier): if origin == "irc": text = mo.group() fg, bg = mo.groups() - try: fg = Color('\x03' + fg) - except: fg = None - try: bg = Color('\x03' + bg) - except: bg = None + try: + fg = Color("\x03" + fg) + except: + fg = None + try: + bg = Color("\x03" + bg) + except: + bg = None inst = cls(text, origin, color=(fg, bg)) elif origin == "pchum": text = mo.group() @@ -168,32 +189,49 @@ class CTag(Specifier): except: pass return inst + + class CTagEnd(CTag): # TODO: Make this a separate class - NOT a subclass of CTag like it is at # present resets_color = True + def convert(self, format): - text = '' - if format == "irc": return '\x03' - elif format == "pchum": return "" - elif format == "plaintext": return '' + text = "" + if format == "irc": + return "\x03" + elif format == "pchum": + return "" + elif format == "plaintext": + return "" return text - def has_color(self): return False + + def has_color(self): + return False @classmethod def from_mo(cls, mo, origin): # Turns the whole match into it (for now) return cls(mo.group(), origin, color=None) + + class LineColor(CTag): pass + + class LineColorEnd(CTagEnd): pass + + class FTagEnd(Specifier): resets_formatting = True + + class ResetTag(CTagEnd, FTagEnd): def convert(self, format): - text = '' - if format == "irc": return '\x0F' + text = "" + if format == "irc": + return "\x0F" elif format == "pchum": # Later on, this one is going to be infuriatingly tricky. # Supporting things like bold and so on doesn't really allow for an @@ -201,14 +239,18 @@ class ResetTag(CTagEnd, FTagEnd): # I *could* implement it, and it wouldn't be too hard, but it would # probably confuse more people than it helped. return "" - elif format == "plaintext": return '' + elif format == "plaintext": + return "" return text + + class SpecifierEnd(CTagEnd, FTagEnd): # This might not ever even be used, but you never know.... # If it does, we may need properties such as .resets_color, .resets_bold, # and so on and so forth pass + # TODO: Consider using a metaclass to check those properties - e.g. if # a class .sets_color and a subclass .resets_color, set the subclass's # .sets_color to False @@ -218,9 +260,12 @@ class Lexer(object): # Subclasses need to supply a ref themselves ref = None compress_tags = False + def breakdown(self, string, objlist): - if not isinstance(string, basestr): msglist = string - else: msglist = [string] + if not isinstance(string, basestr): + msglist = string + else: + msglist = [string] for obj, rxp in objlist: working = [] for i, msg in enumerate(msglist): @@ -248,12 +293,14 @@ class Lexer(object): # Exchange the old list with the processed one, and continue msglist = working return msglist + def lex(self, string): # Needs to be implemented by subclasses return self.breakdown(string, []) def list_convert(self, target, format=None): - if format is None: format = self.ref + if format is None: + format = self.ref converted = [] for elt in target: @@ -266,6 +313,7 @@ class Lexer(object): converted.append(elt) return converted + class Pesterchum(Lexer): ref = "pchum" _ctag_begin = re.compile(r"", flags=re.I) @@ -281,11 +329,11 @@ class Pesterchum(Lexer): def lex(self, string): lexlist = [ - ##(mecmd, self._mecmdre), - (CTag, self._ctag_begin), - ##(CTag, self._ctag_end) - (CTagEnd, self._ctag_end) - ] + ##(mecmd, self._mecmdre), + (CTag, self._ctag_begin), + ##(CTag, self._ctag_end) + (CTagEnd, self._ctag_end), + ] lexed = self.breakdown(string, lexlist) @@ -324,7 +372,7 @@ class Pesterchum(Lexer): ## balanced.append(o) # This will need to be re-evaluated to support the line end lexeme/etc. if beginc > endc: - for i in range(0, beginc-endc): + for i in range(0, beginc - endc): ##balanced.append(colorEnd("")) balanced.append(CTagEnd("", self.ref, None)) return balanced @@ -332,12 +380,15 @@ class Pesterchum(Lexer): # TODO: Let us contextually set compression here or something, ugh. If # 'None' assume the self-set one. def list_convert(self, target, format=None): - if format is None: format = self.ref + if format is None: + format = self.ref converted = [] cstack = [] ##closecolor = lambda: converted.append(CTagEnd("", self.ref, None)) - closecolor = lambda: converted.append(CTagEnd("", self.ref, None).convert(format)) + closecolor = lambda: converted.append( + CTagEnd("", self.ref, None).convert(format) + ) for elt in target: if isinstance(elt, LineColorEnd): @@ -422,6 +473,7 @@ class Pesterchum(Lexer): converted.append(elt) return converted + class RelayChat(Lexer): ref = "irc" # This could use some cleaning up later, but it'll work for now, hopefully @@ -435,8 +487,8 @@ class RelayChat(Lexer): lexlist = [ (CTag, self._ccode_rxp), (CTagEnd, self._ccode_end_rxp), - (ResetTag, self._reset_rxp) - ] + (ResetTag, self._reset_rxp), + ] lexed = self.breakdown(string, lexlist) @@ -444,7 +496,8 @@ class RelayChat(Lexer): return lexed def list_convert(self, target, format=None): - if format is None: format = self.ref + if format is None: + format = self.ref converted = [] cstack = [] @@ -525,98 +578,99 @@ class RelayChat(Lexer): return converted def _list_convert_new(self, target, format=None): - if format is None: format = self.ref - converted = [] - cstack = [] + if format is None: + format = self.ref + converted = [] + cstack = [] - for elt in target: - if isinstance(elt, LineColorEnd): - # Go down the stack until we have a line color TO end - while cstack: - # Add a since we'll need one anyway + for elt in target: + if isinstance(elt, LineColorEnd): + # Go down the stack until we have a line color TO end + while cstack: + # Add a since we'll need one anyway + # Is closecolor accessible here? + try: + closecolor() + except Exception as e: + print(e) + + ##if isinstance(color, LineColor): + if isinstance(cstack.pop(), LineColor): + # We found what we wanted, and the color + # was already popped from the stack, so + # we're good + # Breaking here allows it to be appended + break + continue + elif isinstance(elt, ResetTag): + # If it says reset, reset - which means go down the + # stack to the most recent line color. + while cstack: + color = cstack[-1] + if not isinstance(color, LineColor): + # It's not a line color, so remove it + del cstack[-1] + # Add a # Is closecolor accessible here? try: closecolor() except Exception as e: print(e) - - ##if isinstance(color, LineColor): - if isinstance(cstack.pop(), LineColor): - # We found what we wanted, and the color - # was already popped from the stack, so - # we're good - # Breaking here allows it to be appended - break - continue - elif isinstance(elt, ResetTag): - # If it says reset, reset - which means go down the - # stack to the most recent line color. - while cstack: - color = cstack[-1] - if not isinstance(color, LineColor): - # It's not a line color, so remove it - del cstack[-1] - # Add a - # Is closecolor accessible here? - try: - closecolor() - except Exception as e: - print(e) - else: - # It's a line color, so stop searching. - # Using break here prevents the 'else' - # clause of this while statement from - # executing. - break else: - # We don't have any more entries in the stack; - # just continue. - continue - ## We found the line color, so add it and continue - ##converted.append(color.convert(format)) + # It's a line color, so stop searching. + # Using break here prevents the 'else' + # clause of this while statement from + # executing. + break + else: + # We don't have any more entries in the stack; + # just continue. continue - ## TODO: Make this actually add the reset char - # The above shouldn't be necessary because this is Pesterchum's - # format, not IRC's - elif isinstance(elt, CTagEnd): - try: + ## We found the line color, so add it and continue + ##converted.append(color.convert(format)) + continue + ## TODO: Make this actually add the reset char + # The above shouldn't be necessary because this is Pesterchum's + # format, not IRC's + elif isinstance(elt, CTagEnd): + try: + color = cstack[-1] + # Remove the oldest color, the one we're exiting + if not isinstance(color, LineColor): + # If we got here, we don't have a line color, + # so we're free to act as usual + cstack.pop() + # Fetch the current nested color color = cstack[-1] - # Remove the oldest color, the one we're exiting - if not isinstance(color, LineColor): - # If we got here, we don't have a line color, - # so we're free to act as usual - cstack.pop() - # Fetch the current nested color - color = cstack[-1] - else: - # We have a line color and the current lexeme - # is NOT a line color end; don't even bother - # adding it to the processed result - continue - except LookupError: - # We aren't nested in a color anymore - # Passing here causes us to fall through to normal - # handling - pass - # Not necessary due to Pesterchum's format - ##else: - ## # We're still nested.... - ## ##converted.append(elt.convert(format)) - ## converted.append(color.convert(format)) - ## # We already added to the working list, so just - ## # skip the rest - ## continue - elif isinstance(elt, CTag): - # Push the color onto the stack - we're nested in it now - cstack.append(elt) - # Falling through adds it to the converted result + else: + # We have a line color and the current lexeme + # is NOT a line color end; don't even bother + # adding it to the processed result + continue + except LookupError: + # We aren't nested in a color anymore + # Passing here causes us to fall through to normal + # handling + pass + # Not necessary due to Pesterchum's format + ##else: + ## # We're still nested.... + ## ##converted.append(elt.convert(format)) + ## converted.append(color.convert(format)) + ## # We already added to the working list, so just + ## # skip the rest + ## continue + elif isinstance(elt, CTag): + # Push the color onto the stack - we're nested in it now + cstack.append(elt) + # Falling through adds it to the converted result - if isinstance(elt, Lexeme): - elt = elt.convert(format) - elif not isinstance(elt, basestr): - # Tempted to make this toss an error, but for now, we'll be - # safe and make it convert to str - elt = str(elt) - converted.append(elt) - return converted + if isinstance(elt, Lexeme): + elt = elt.convert(format) + elif not isinstance(elt, basestr): + # Tempted to make this toss an error, but for now, we'll be + # safe and make it convert to str + elt = str(elt) + converted.append(elt) + return converted diff --git a/pnc/unicolor.py b/pnc/unicolor.py index e7ee0e7..7a8208b 100644 --- a/pnc/unicolor.py +++ b/pnc/unicolor.py @@ -7,7 +7,6 @@ __all__ = ["Color"] # in mind that this may be phased out in the future. - from .dep.attrdict import AttrDict import collections @@ -21,11 +20,12 @@ else: basestr = str - # A named tuple for containing CIE L*a*b* (CIELAB) information. # NOTE TO THOSE MAINTAINING: If you don't know what that means, you're going to # hate yourself *and* me if you try to edit this. I know I did when I wrote it. -LabTuple = collections.namedtuple("LabTuple", ['L', 'a', 'b']) +LabTuple = collections.namedtuple("LabTuple", ["L", "a", "b"]) + + class Color(object): # The threshold at which to consider two colors noticeably different, even # if only barely @@ -38,7 +38,7 @@ class Color(object): # TODO: Split __init__, partly using __new__, so the former just has to do # conversions def __init__(self, *args, **kwargs): - self.ccode = '' + self.ccode = "" self.closest_name = self.name = None nargs = len(args) if nargs == 1: @@ -54,20 +54,25 @@ class Color(object): if isinstance(arg, basestr): # If it's a string, we've probably got a hex code, but check # anyway just in case - if arg.startswith('#'): + if arg.startswith("#"): self.hexstr = self.sanitize_hex(arg) rgb = self.hexstr_to_rgb(self.hexstr) self.red, self.green, self.blue = rgb ##return # TODO: This. - elif (arg.startswith('\003') and len(arg) > 1 - or len(arg) < 3 and arg.isdigit()): + elif ( + arg.startswith("\003") + and len(arg) > 1 + or len(arg) < 3 + and arg.isdigit() + ): # We have an IRC-style color code - arg = arg.lstrip('\003') + arg = arg.lstrip("\003") # Just in case - arg = arg.split(',')[0] + arg = arg.split(",")[0] cnum = int(arg) - try: color = _irc_colors[cnum] + try: + color = _irc_colors[cnum] except LookupError: raise ValueError("No color for ccode %r found" % cnum) # We found a color; fall through and so on @@ -75,7 +80,8 @@ class Color(object): else: # Presumably we have a color name name = arg.lower() - try: color = _svg_colors[name] + try: + color = _svg_colors[name] except LookupError: raise ValueError("No color with name %r found" % name) # We found a color; fall through so we make this one a copy @@ -103,14 +109,20 @@ class Color(object): self.x, self.y, self.z = self.rgb_to_xyz(*self.to_rgb_tuple()) # Calculate the LAB color self.cielab = LabTuple(*self.xyz_to_cielab(*self.to_xyz_tuple())) - if not self.closest_name: self.closest_name = self.get_svg_name() - if not self.ccode: self.ccode = self.get_ccode() + if not self.closest_name: + self.closest_name = self.get_svg_name() + if not self.ccode: + self.ccode = self.get_ccode() def __eq__(self, other): return hash(self) == hash(other) - def __ne__(self, other): return not self.__eq__(other) + + def __ne__(self, other): + return not self.__eq__(other) + def __sub__(self, other): - if not isinstance(other, Color): raise TypeError + if not isinstance(other, Color): + raise TypeError return self.distance(other) def __hash__(self): @@ -130,23 +142,27 @@ class Color(object): ##result ^= self.green ##result ^= self.blue ##return result + def __repr__(self): ##return "%s(%r)" % (type(self).__name__, str(self)) - return "%s(%r)" % (type(self).__name__, - self.reduce_hexstr(self.hexstr)) + return "%s(%r)" % (type(self).__name__, self.reduce_hexstr(self.hexstr)) + def __str__(self): ##return self.reduce_hexstr(self.hexstr) return self.name() # Builtins # These were yanked from Hostmask and changed around a bit - def __getitem__(self, ind): return (self.red, self.green, self.blue)[ind] + def __getitem__(self, ind): + return (self.red, self.green, self.blue)[ind] + def __iter__(self): targs = (self.red, self.green, self.blue) for t in targs: yield t # If we got here, we're out of attributes to provide raise StopIteration + ##def __len__(self): ## # Acceptable so long as we're returning RGB like we currently (at TOW) ## # are @@ -156,8 +172,8 @@ class Color(object): def from_ccode(cls, ccode): if isinstance(ccode, basestr): # We were passed a string - ccode = ccode.lstrip('\003') - ccode = ccode.split(',') + ccode = ccode.lstrip("\003") + ccode = ccode.split(",") if len(ccode) < 2: fg = ccode[0] bg = None @@ -236,9 +252,9 @@ class Color(object): # http://en.wikipedia.org/wiki/Color_difference slab, olab = self.to_cielab_tuple(), other.to_cielab_tuple() # Calculate the distance between the points for each - dist = list(map(lambda p1, p2: (p2 - p1)**2, slab, olab)) + dist = list(map(lambda p1, p2: (p2 - p1) ** 2, slab, olab)) # Add the results up, and sqrt to compensate for earlier squaring - dist = sum(dist) ** .5 + dist = sum(dist) ** 0.5 return dist def rgb_distance(self, other): @@ -252,17 +268,17 @@ class Color(object): ### Square the results from the above ##dist = [x**2 for x in dist] # Do what we WOULD have done in those two lines with a single one - dist = list(map(lambda x1, x2: (x1 - x2)**2, srgb, orgb)) + dist = list(map(lambda x1, x2: (x1 - x2) ** 2, srgb, orgb)) # Add the results up dist = sum(dist) # Fetch the square root to compensate for the earlier squaring - dist **= .5 + dist **= 0.5 return dist @classmethod def hexstr_to_rgb(cls, hexstr): hexstr = cls.sanitize_hex(hexstr) - hexstr = hexstr.lstrip('#') + hexstr = hexstr.lstrip("#") if len(hexstr) == 3: # NOTE: This will presently never happen, due to the way # sanitize_hex works. @@ -270,17 +286,14 @@ class Color(object): # first. # Multiplying each element by 17 expands it. Dividing it does the # opposite. - result = tuple( (int(h, 16) * 17) for h in hexstr ) + result = tuple((int(h, 16) * 17) for h in hexstr) else: # This is ugly, but the purpose is simple and it's accomplished in # a single line...it just runs through the string, picking two # characters at a time and converting them from hex values to ints. - result = tuple( - int(hexstr[i:i+2], 16) for i in range(0, len(hexstr), 2) - ) + result = tuple(int(hexstr[i : i + 2], 16) for i in range(0, len(hexstr), 2)) return result - @staticmethod def rgb_to_hexstr(red, green, blue, compress=False): rgb = [red, green, blue] @@ -303,7 +316,7 @@ class Color(object): # All of our codes were doubles; compress them all down. result = [h[0] for h in result] # Join and return the result - return '#' + ''.join(result) + return "#" + "".join(result) # These next two are from http://www.easyrgb.com/index.php?X=MATH @staticmethod @@ -311,8 +324,10 @@ class Color(object): rgb = [red, green, blue] for i, n in enumerate(rgb): n /= 255 - if n > 0.04045: n = ( ( n + 0.055 ) / 1.055 ) ** 2.4 - else: n /= 12.92 + if n > 0.04045: + n = ((n + 0.055) / 1.055) ** 2.4 + else: + n /= 12.92 rgb[i] = n * 100 r, g, b = rgb x = r * 0.4124 + g * 0.3576 + b * 0.1805 @@ -322,6 +337,7 @@ class Color(object): ##y = 0.222491598 * r + 0.71688606 * g + 0.060621486 * b ##z = 0.013929122 * r + 0.097097002 * g + 0.71418547 * b return x, y, z + @staticmethod def xyz_to_cielab(x, y, z): # Reference X, Y, and Z @@ -331,13 +347,14 @@ class Color(object): xyz = [x, y, z] for i, n in enumerate(xyz): n /= refs[i] - if n > 0.008856: n **= 1/3 + if n > 0.008856: + n **= 1 / 3 else: n *= 7.787 - n += 16/116 + n += 16 / 116 xyz[i] = n x, y, z = xyz - l = (y*116) - 16 + l = (y * 116) - 16 a = (x - y) * 500 b = (y - z) * 200 return l, a, b @@ -347,24 +364,23 @@ class Color(object): """Attempt to reduce a six-character hexadecimal color code down to a four-character one.""" orig = hexstr - hexstr = hexstr.lstrip('#') + hexstr = hexstr.lstrip("#") strlen = len(hexstr) h = hexstr.upper() for i in range(0, strlen, 2): - if h[i] != h[i+1]: + if h[i] != h[i + 1]: # We found a match that wouldn't work; give back the old value. return orig else: # All of these can be reduced; do so and return. - return '#' + hexstr[::2] - + return "#" + hexstr[::2] @staticmethod def sanitize_hex(hexstr): orig = hexstr hexstr = hexstr.upper() # We don't need the leading hash mark for now - hexstr = hexstr.lstrip('#') + hexstr = hexstr.lstrip("#") strlen = len(hexstr) if strlen == 6: # We just need to test this for validity. Fall through to the end. @@ -372,228 +388,232 @@ class Color(object): elif strlen == 3: # We have a short (CSS style) code; duplicate all of the characters hexstr = [c + c for c in hexstr] - hexstr = ''.join(hexstr) + hexstr = "".join(hexstr) else: - raise ValueError( - "Invalid hexadecimal value provided: %s" % orig - ) + raise ValueError("Invalid hexadecimal value provided: %s" % orig) try: # Make sure it works/is readable (no invalid characters). int(hexstr, 16) except ValueError: - raise ValueError( - "Invalid hexadecimal value provided: %s" % orig - ) - return '#' + hexstr + raise ValueError("Invalid hexadecimal value provided: %s" % orig) + return "#" + hexstr def to_cielab_tuple(self): # For now, just return the stored CIELAB tuple return self.cielab - def to_rgb_tuple(self): return (self.red, self.green, self.blue) + + def to_rgb_tuple(self): + return (self.red, self.green, self.blue) + # 2012-12-05T17:40:39-07:00: Changed 'self.blue' to 'self.z' like it SHOULD # have been in the FIRST place. Ugh. How did I fuck THAT one up? - def to_xyz_tuple(self): return (self.x, self.y, self.z) + def to_xyz_tuple(self): + return (self.x, self.y, self.z) + # All of these are effectively equivalent to the Qt-provided colors, so they # could be phased out - but there's no need to, yet. _svg_colors = AttrDict() _irc_colors = {} -_svg_colors.update({ - "aliceblue": Color(240, 248, 255), - "antiquewhite": Color(250, 235, 215), - "aqua": Color( 0, 255, 255), - "aquamarine": Color(127, 255, 212), - "azure": Color(240, 255, 255), - "beige": Color(245, 245, 220), - "bisque": Color(255, 228, 196), - "black": Color( 0, 0, 0), - "blanchedalmond": Color(255, 235, 205), - "blue": Color( 0, 0, 255), - "blueviolet": Color(138, 43, 226), - "brown": Color(165, 42, 42), - "burlywood": Color(222, 184, 135), - "cadetblue": Color( 95, 158, 160), - "chartreuse": Color(127, 255, 0), - "chocolate": Color(210, 105, 30), - "coral": Color(255, 127, 80), - "cornflowerblue": Color(100, 149, 237), - "cornsilk": Color(255, 248, 220), - "crimson": Color(220, 20, 60), - "cyan": Color( 0, 255, 255), - "darkblue": Color( 0, 0, 139), - "darkcyan": Color( 0, 139, 139), - "darkgoldenrod": Color(184, 134, 11), - "darkgray": Color(169, 169, 169), - "darkgreen": Color( 0, 100, 0), - "darkgrey": Color(169, 169, 169), - "darkkhaki": Color(189, 183, 107), - "darkmagenta": Color(139, 0, 139), - "darkolivegreen": Color( 85, 107, 47), - "darkorange": Color(255, 140, 0), - "darkorchid": Color(153, 50, 204), - "darkred": Color(139, 0, 0), - "darksalmon": Color(233, 150, 122), - "darkseagreen": Color(143, 188, 143), - "darkslateblue": Color( 72, 61, 139), - "darkslategray": Color( 47, 79, 79), - "darkslategrey": Color( 47, 79, 79), - "darkturquoise": Color( 0, 206, 209), - "darkviolet": Color(148, 0, 211), - "deeppink": Color(255, 20, 147), - "deepskyblue": Color( 0, 191, 255), - "dimgray": Color(105, 105, 105), - "dimgrey": Color(105, 105, 105), - "dodgerblue": Color( 30, 144, 255), - "firebrick": Color(178, 34, 34), - "floralwhite": Color(255, 250, 240), - "forestgreen": Color( 34, 139, 34), - "fuchsia": Color(255, 0, 255), - "gainsboro": Color(220, 220, 220), - "ghostwhite": Color(248, 248, 255), - "gold": Color(255, 215, 0), - "goldenrod": Color(218, 165, 32), - "gray": Color(128, 128, 128), - "grey": Color(128, 128, 128), - "green": Color( 0, 128, 0), - "greenyellow": Color(173, 255, 47), - "honeydew": Color(240, 255, 240), - "hotpink": Color(255, 105, 180), - "indianred": Color(205, 92, 92), - "indigo": Color( 75, 0, 130), - "ivory": Color(255, 255, 240), - "khaki": Color(240, 230, 140), - "lavender": Color(230, 230, 250), - "lavenderblush": Color(255, 240, 245), - "lawngreen": Color(124, 252, 0), - "lemonchiffon": Color(255, 250, 205), - "lightblue": Color(173, 216, 230), - "lightcoral": Color(240, 128, 128), - "lightcyan": Color(224, 255, 255), - "lightgoldenrodyellow": Color(250, 250, 210), - "lightgray": Color(211, 211, 211), - "lightgreen": Color(144, 238, 144), - "lightgrey": Color(211, 211, 211), - "lightpink": Color(255, 182, 193), - "lightsalmon": Color(255, 160, 122), - "lightseagreen": Color( 32, 178, 170), - "lightskyblue": Color(135, 206, 250), - "lightslategray": Color(119, 136, 153), - "lightslategrey": Color(119, 136, 153), - "lightsteelblue": Color(176, 196, 222), - "lightyellow": Color(255, 255, 224), - "lime": Color( 0, 255, 0), - "limegreen": Color( 50, 205, 50), - "linen": Color(250, 240, 230), - "magenta": Color(255, 0, 255), - "maroon": Color(128, 0, 0), - "mediumaquamarine": Color(102, 205, 170), - "mediumblue": Color( 0, 0, 205), - "mediumorchid": Color(186, 85, 211), - "mediumpurple": Color(147, 112, 219), - "mediumseagreen": Color( 60, 179, 113), - "mediumslateblue": Color(123, 104, 238), - "mediumspringgreen": Color( 0, 250, 154), - "mediumturquoise": Color( 72, 209, 204), - "mediumvioletred": Color(199, 21, 133), - "midnightblue": Color( 25, 25, 112), - "mintcream": Color(245, 255, 250), - "mistyrose": Color(255, 228, 225), - "moccasin": Color(255, 228, 181), - "navajowhite": Color(255, 222, 173), - "navy": Color( 0, 0, 128), - "oldlace": Color(253, 245, 230), - "olive": Color(128, 128, 0), - "olivedrab": Color(107, 142, 35), - "orange": Color(255, 165, 0), - "orangered": Color(255, 69, 0), - "orchid": Color(218, 112, 214), - "palegoldenrod": Color(238, 232, 170), - "palegreen": Color(152, 251, 152), - "paleturquoise": Color(175, 238, 238), - "palevioletred": Color(219, 112, 147), - "papayawhip": Color(255, 239, 213), - "peachpuff": Color(255, 218, 185), - "peru": Color(205, 133, 63), - "pink": Color(255, 192, 203), - "plum": Color(221, 160, 221), - "powderblue": Color(176, 224, 230), - "purple": Color(128, 0, 128), - "red": Color(255, 0, 0), - "rosybrown": Color(188, 143, 143), - "royalblue": Color( 65, 105, 225), - "saddlebrown": Color(139, 69, 19), - "salmon": Color(250, 128, 114), - "sandybrown": Color(244, 164, 96), - "seagreen": Color( 46, 139, 87), - "seashell": Color(255, 245, 238), - "sienna": Color(160, 82, 45), - "silver": Color(192, 192, 192), - "skyblue": Color(135, 206, 235), - "slateblue": Color(106, 90, 205), - "slategray": Color(112, 128, 144), - "slategrey": Color(112, 128, 144), - "snow": Color(255, 250, 250), - "springgreen": Color( 0, 255, 127), - "steelblue": Color( 70, 130, 180), - "tan": Color(210, 180, 140), - "teal": Color( 0, 128, 128), - "thistle": Color(216, 191, 216), - "tomato": Color(255, 99, 71), - "turquoise": Color( 64, 224, 208), - "violet": Color(238, 130, 238), - "wheat": Color(245, 222, 179), - "white": Color(255, 255, 255), - "whitesmoke": Color(245, 245, 245), - "yellow": Color(255, 255, 0), - "yellowgreen": Color(154, 205, 50) - }) +_svg_colors.update( + { + "aliceblue": Color(240, 248, 255), + "antiquewhite": Color(250, 235, 215), + "aqua": Color(0, 255, 255), + "aquamarine": Color(127, 255, 212), + "azure": Color(240, 255, 255), + "beige": Color(245, 245, 220), + "bisque": Color(255, 228, 196), + "black": Color(0, 0, 0), + "blanchedalmond": Color(255, 235, 205), + "blue": Color(0, 0, 255), + "blueviolet": Color(138, 43, 226), + "brown": Color(165, 42, 42), + "burlywood": Color(222, 184, 135), + "cadetblue": Color(95, 158, 160), + "chartreuse": Color(127, 255, 0), + "chocolate": Color(210, 105, 30), + "coral": Color(255, 127, 80), + "cornflowerblue": Color(100, 149, 237), + "cornsilk": Color(255, 248, 220), + "crimson": Color(220, 20, 60), + "cyan": Color(0, 255, 255), + "darkblue": Color(0, 0, 139), + "darkcyan": Color(0, 139, 139), + "darkgoldenrod": Color(184, 134, 11), + "darkgray": Color(169, 169, 169), + "darkgreen": Color(0, 100, 0), + "darkgrey": Color(169, 169, 169), + "darkkhaki": Color(189, 183, 107), + "darkmagenta": Color(139, 0, 139), + "darkolivegreen": Color(85, 107, 47), + "darkorange": Color(255, 140, 0), + "darkorchid": Color(153, 50, 204), + "darkred": Color(139, 0, 0), + "darksalmon": Color(233, 150, 122), + "darkseagreen": Color(143, 188, 143), + "darkslateblue": Color(72, 61, 139), + "darkslategray": Color(47, 79, 79), + "darkslategrey": Color(47, 79, 79), + "darkturquoise": Color(0, 206, 209), + "darkviolet": Color(148, 0, 211), + "deeppink": Color(255, 20, 147), + "deepskyblue": Color(0, 191, 255), + "dimgray": Color(105, 105, 105), + "dimgrey": Color(105, 105, 105), + "dodgerblue": Color(30, 144, 255), + "firebrick": Color(178, 34, 34), + "floralwhite": Color(255, 250, 240), + "forestgreen": Color(34, 139, 34), + "fuchsia": Color(255, 0, 255), + "gainsboro": Color(220, 220, 220), + "ghostwhite": Color(248, 248, 255), + "gold": Color(255, 215, 0), + "goldenrod": Color(218, 165, 32), + "gray": Color(128, 128, 128), + "grey": Color(128, 128, 128), + "green": Color(0, 128, 0), + "greenyellow": Color(173, 255, 47), + "honeydew": Color(240, 255, 240), + "hotpink": Color(255, 105, 180), + "indianred": Color(205, 92, 92), + "indigo": Color(75, 0, 130), + "ivory": Color(255, 255, 240), + "khaki": Color(240, 230, 140), + "lavender": Color(230, 230, 250), + "lavenderblush": Color(255, 240, 245), + "lawngreen": Color(124, 252, 0), + "lemonchiffon": Color(255, 250, 205), + "lightblue": Color(173, 216, 230), + "lightcoral": Color(240, 128, 128), + "lightcyan": Color(224, 255, 255), + "lightgoldenrodyellow": Color(250, 250, 210), + "lightgray": Color(211, 211, 211), + "lightgreen": Color(144, 238, 144), + "lightgrey": Color(211, 211, 211), + "lightpink": Color(255, 182, 193), + "lightsalmon": Color(255, 160, 122), + "lightseagreen": Color(32, 178, 170), + "lightskyblue": Color(135, 206, 250), + "lightslategray": Color(119, 136, 153), + "lightslategrey": Color(119, 136, 153), + "lightsteelblue": Color(176, 196, 222), + "lightyellow": Color(255, 255, 224), + "lime": Color(0, 255, 0), + "limegreen": Color(50, 205, 50), + "linen": Color(250, 240, 230), + "magenta": Color(255, 0, 255), + "maroon": Color(128, 0, 0), + "mediumaquamarine": Color(102, 205, 170), + "mediumblue": Color(0, 0, 205), + "mediumorchid": Color(186, 85, 211), + "mediumpurple": Color(147, 112, 219), + "mediumseagreen": Color(60, 179, 113), + "mediumslateblue": Color(123, 104, 238), + "mediumspringgreen": Color(0, 250, 154), + "mediumturquoise": Color(72, 209, 204), + "mediumvioletred": Color(199, 21, 133), + "midnightblue": Color(25, 25, 112), + "mintcream": Color(245, 255, 250), + "mistyrose": Color(255, 228, 225), + "moccasin": Color(255, 228, 181), + "navajowhite": Color(255, 222, 173), + "navy": Color(0, 0, 128), + "oldlace": Color(253, 245, 230), + "olive": Color(128, 128, 0), + "olivedrab": Color(107, 142, 35), + "orange": Color(255, 165, 0), + "orangered": Color(255, 69, 0), + "orchid": Color(218, 112, 214), + "palegoldenrod": Color(238, 232, 170), + "palegreen": Color(152, 251, 152), + "paleturquoise": Color(175, 238, 238), + "palevioletred": Color(219, 112, 147), + "papayawhip": Color(255, 239, 213), + "peachpuff": Color(255, 218, 185), + "peru": Color(205, 133, 63), + "pink": Color(255, 192, 203), + "plum": Color(221, 160, 221), + "powderblue": Color(176, 224, 230), + "purple": Color(128, 0, 128), + "red": Color(255, 0, 0), + "rosybrown": Color(188, 143, 143), + "royalblue": Color(65, 105, 225), + "saddlebrown": Color(139, 69, 19), + "salmon": Color(250, 128, 114), + "sandybrown": Color(244, 164, 96), + "seagreen": Color(46, 139, 87), + "seashell": Color(255, 245, 238), + "sienna": Color(160, 82, 45), + "silver": Color(192, 192, 192), + "skyblue": Color(135, 206, 235), + "slateblue": Color(106, 90, 205), + "slategray": Color(112, 128, 144), + "slategrey": Color(112, 128, 144), + "snow": Color(255, 250, 250), + "springgreen": Color(0, 255, 127), + "steelblue": Color(70, 130, 180), + "tan": Color(210, 180, 140), + "teal": Color(0, 128, 128), + "thistle": Color(216, 191, 216), + "tomato": Color(255, 99, 71), + "turquoise": Color(64, 224, 208), + "violet": Color(238, 130, 238), + "wheat": Color(245, 222, 179), + "white": Color(255, 255, 255), + "whitesmoke": Color(245, 245, 245), + "yellow": Color(255, 255, 0), + "yellowgreen": Color(154, 205, 50), + } +) for k, v in list(_svg_colors.items()): v.closest_name = v.name = k # 2012-12-08T14:29-07:00: Copied over from Colors.hexstr_for_ccodes in the main # textsub file, and subsequently modified. -_irc_colors.update({ - # These are all taken from *MY* XChat settings - they aren't guaranteed to - # please everyone! - 0: Color(0xFFFFFF), - 1: Color(0x1F1F1F), - 2: Color(0x00007F), - 3: Color(0x007F00), - 4: Color(0xFF0000), - 5: Color(0x7F0000), - 6: Color(0x9C009C), - 7: Color(0xFC7F00), - 8: Color(0xFFFF00), - 9: Color(0x00FC00), - ##10: Color(0x009393), - 10: Color(0x008282), - 11: Color(0x00FFFF), - 12: Color(0x0000FC), - 13: Color(0xFF00FF), - 14: Color(0x7F7F7F), - 15: Color(0xD2D2D2), - # My local colors - 16: Color(0xCCCCCC), - ##17: Color(0x000000), # Commented out 'til readability checks are in - 17: Color(0x1F1F1F), - 18: Color(0x000056), - 19: Color(0x008141), - 20: Color(0xE00707), - 21: Color(0xA10000), - 22: Color(0x6A006A), - 23: Color(0xA15000), - 24: Color(0xA1A100), - 25: Color(0x416600), - ##26: Color(0x008282), - 26: Color(0x005682), - 27: Color(0x00D5F2), - 28: Color(0x0715CD), - 29: Color(0x99004D), - 30: Color(0x323232), - 31: Color(0x929292), - - 99: Color(0x999999) # Until I think of a better solution to this - }) +_irc_colors.update( + { + # These are all taken from *MY* XChat settings - they aren't guaranteed to + # please everyone! + 0: Color(0xFFFFFF), + 1: Color(0x1F1F1F), + 2: Color(0x00007F), + 3: Color(0x007F00), + 4: Color(0xFF0000), + 5: Color(0x7F0000), + 6: Color(0x9C009C), + 7: Color(0xFC7F00), + 8: Color(0xFFFF00), + 9: Color(0x00FC00), + ##10: Color(0x009393), + 10: Color(0x008282), + 11: Color(0x00FFFF), + 12: Color(0x0000FC), + 13: Color(0xFF00FF), + 14: Color(0x7F7F7F), + 15: Color(0xD2D2D2), + # My local colors + 16: Color(0xCCCCCC), + ##17: Color(0x000000), # Commented out 'til readability checks are in + 17: Color(0x1F1F1F), + 18: Color(0x000056), + 19: Color(0x008141), + 20: Color(0xE00707), + 21: Color(0xA10000), + 22: Color(0x6A006A), + 23: Color(0xA15000), + 24: Color(0xA1A100), + 25: Color(0x416600), + ##26: Color(0x008282), + 26: Color(0x005682), + 27: Color(0x00D5F2), + 28: Color(0x0715CD), + 29: Color(0x99004D), + 30: Color(0x323232), + 31: Color(0x929292), + 99: Color(0x999999), # Until I think of a better solution to this + } +) for k, v in list(_irc_colors.items()): v.ccode = "%02d" % k del k, v diff --git a/profile.py b/profile.py index a2bee51..90678f9 100644 --- a/profile.py +++ b/profile.py @@ -22,7 +22,7 @@ from dataobjs import PesterProfile, pesterQuirks from parsetools import convertTags _datadir = ostools.getDataDir() -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") class PesterLog(object): @@ -31,7 +31,7 @@ class PesterLog(object): self.parent = parent self.handle = handle self.convos = {} - self.logpath = _datadir+"logs" + self.logpath = _datadir + "logs" def log(self, handle, msg): if self.parent.config.time12Format(): @@ -42,34 +42,46 @@ class PesterLog(object): log_time += strftime(":%S] ") else: log_time += "] " - if handle[0] == '#': - if not self.parent.config.logMemos() & self.parent.config.LOG: return + if handle[0] == "#": + if not self.parent.config.logMemos() & self.parent.config.LOG: + return if not self.parent.config.logMemos() & self.parent.config.STAMP: log_time = "" else: - if not self.parent.config.logPesters() & self.parent.config.LOG: return + if not self.parent.config.logPesters() & self.parent.config.LOG: + return if not self.parent.config.logPesters() & self.parent.config.STAMP: log_time = "" - if self.parent.isBot(handle): return - #watch out for illegal characters + if self.parent.isBot(handle): + return + # watch out for illegal characters handle = re.sub(r'[<>:"/\\|?*]', "_", handle) bbcodemsg = log_time + convertTags(msg, "bbcode") - html = log_time + convertTags(msg, "html")+"
" - msg = log_time +convertTags(msg, "text") + html = log_time + convertTags(msg, "html") + "
" + msg = log_time + convertTags(msg, "text") modes = {"bbcode": bbcodemsg, "html": html, "text": msg} try: if handle not in self.convos: log_time = datetime.now().strftime("%Y-%m-%d.%H.%M") self.convos[handle] = {} for (format, t) in modes.items(): - if not os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, handle, format)): - os.makedirs("%s/%s/%s/%s" % (self.logpath, self.handle, handle, format)) - fp = codecs.open("%s/%s/%s/%s/%s.%s.txt" % (self.logpath, self.handle, handle, format, handle, log_time), encoding='utf-8', mode='a') + if not os.path.exists( + "%s/%s/%s/%s" % (self.logpath, self.handle, handle, format) + ): + os.makedirs( + "%s/%s/%s/%s" % (self.logpath, self.handle, handle, format) + ) + fp = codecs.open( + "%s/%s/%s/%s/%s.%s.txt" + % (self.logpath, self.handle, handle, format, handle, log_time), + encoding="utf-8", + mode="a", + ) self.convos[handle][format] = fp - + for (format, t) in modes.items(): f = self.convos[handle][format] - f.write(t+"\r\n") + f.write(t + "\r\n") # flush + fsync force a write, # makes sure logs are saved in the case of a crash. f.flush() @@ -78,34 +90,41 @@ class PesterLog(object): # This way the file descriptors are closed and reopened for every message, # which is sub-optimal and definitely a performance drain but, # otherwise we still run into the ulimit on platforms like MacOS fairly easily. - #if ostools.isOSX() == True: + # if ostools.isOSX() == True: # for (format, t) in modes.items(): # self.finish(handle) - + except (IOError, OSError, KeyError, IndexError, ValueError) as e: # Catching this exception does not stop pchum from dying if we run out of file handles

Failed to load pesterchum.js, this might require manual intervention.

\ + msgbox.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links + msgbox.setInformativeText( + "

Failed to load pesterchum.js, this might require manual intervention.

\ Consider overriding: %s
\ -with a backup from: %s

" % (_datadir, self.filename, os.path.join(_datadir, "backup"), os.path.join(_datadir, "backup"))) +with a backup from: %s

" + % ( + _datadir, + self.filename, + os.path.join(_datadir, "backup"), + os.path.join(_datadir, "backup"), + ) + ) msgbox.exec() sys.exit() @@ -152,23 +179,23 @@ with a backup from: %s" % (_datadir, self.filename, else: self.userprofile = None - self.logpath = _datadir+"logs" + self.logpath = _datadir + "logs" if not os.path.exists(self.logpath): os.makedirs(self.logpath) try: - with open("%s/groups.js" % (self.logpath), 'r') as fp: + with open("%s/groups.js" % (self.logpath), "r") as fp: self.groups = json.load(fp) except (IOError, ValueError): self.groups = {} - with open("%s/groups.js" % (self.logpath), 'w') as fp: + with open("%s/groups.js" % (self.logpath), "w") as fp: json.dump(self.groups, fp) self.backup() def backup(self): # Backups - + try: # Backup pesterchum.js file. # Useful because it seems to randomly get blanked for people. @@ -176,17 +203,26 @@ with a backup from: %s" % (_datadir, self.filename, if os.path.exists(backup_path) == False: os.makedirs(backup_path) current_backup = datetime.now().day % 5 - shutil.copyfile(self.filename, os.path.join(backup_path, "pesterchum.backup-" + str(current_backup) + ".js")) + shutil.copyfile( + self.filename, + os.path.join( + backup_path, "pesterchum.backup-" + str(current_backup) + ".js" + ), + ) # Backup profiles. # Useful because people don't know how to add underscores to handles. profile_path = os.path.join(_datadir, "profiles") profile_list = os.listdir(profile_path) - with zipfile.ZipFile(os.path.join(backup_path, "profiles.backup-" + str(current_backup)) + ".zip", 'w') as pzip: + with zipfile.ZipFile( + os.path.join(backup_path, "profiles.backup-" + str(current_backup)) + + ".zip", + "w", + ) as pzip: for x in profile_list: if x.endswith(".js") == True: with open(self.filename) as f: - pzip.writestr(x, f.read()) + pzip.writestr(x, f.read()) PchumLog.info("Updated backups-%s." % current_backup) except OSError as e: @@ -198,81 +234,103 @@ with a backup from: %s" % (_datadir, self.filename, except zipfile.BadZipFile as e: PchumLog.warning("Failed to make backup, BadZipFile?") PchumLog.warning(e) - + def chums(self): - if 'chums' not in self.config: + if "chums" not in self.config: self.set("chums", []) - return self.config.get('chums', []) + return self.config.get("chums", []) + def setChums(self, newchums): with open(self.filename) as fp: # what if we have two clients open?? newconfig = json.load(fp) - oldchums = newconfig['chums'] + oldchums = newconfig["chums"] # Time to merge these two! :OOO for c in list(set(oldchums) - set(newchums)): newchums.append(c) self.set("chums", newchums) + def hideOfflineChums(self): - return self.config.get('hideOfflineChums', False) + return self.config.get("hideOfflineChums", False) + def defaultprofile(self): try: - return self.config['defaultprofile'] + return self.config["defaultprofile"] except KeyError: return None + def tabs(self): return self.config.get("tabs", True) + def tabMemos(self): - if 'tabmemos' not in self.config: + if "tabmemos" not in self.config: self.set("tabmemos", self.tabs()) return self.config.get("tabmemos", True) + def showTimeStamps(self): - if 'showTimeStamps' not in self.config: + if "showTimeStamps" not in self.config: self.set("showTimeStamps", True) - return self.config.get('showTimeStamps', True) + return self.config.get("showTimeStamps", True) + def time12Format(self): - if 'time12Format' not in self.config: + if "time12Format" not in self.config: self.set("time12Format", True) - return self.config.get('time12Format', True) + return self.config.get("time12Format", True) + def showSeconds(self): - if 'showSeconds' not in self.config: + if "showSeconds" not in self.config: self.set("showSeconds", False) - return self.config.get('showSeconds', False) + return self.config.get("showSeconds", False) + def sortMethod(self): - return self.config.get('sortMethod', 0) + return self.config.get("sortMethod", 0) + def useGroups(self): - return self.config.get('useGroups', False) + return self.config.get("useGroups", False) + def openDefaultGroup(self): groups = self.getGroups() for g in groups: if g[0] == "Chums": return g[1] return True + def showEmptyGroups(self): - if 'emptyGroups' not in self.config: + if "emptyGroups" not in self.config: self.set("emptyGroups", False) - return self.config.get('emptyGroups', False) + return self.config.get("emptyGroups", False) + def showOnlineNumbers(self): - if 'onlineNumbers' not in self.config: + if "onlineNumbers" not in self.config: self.set("onlineNumbers", False) - return self.config.get('onlineNumbers', False) + return self.config.get("onlineNumbers", False) + def logPesters(self): - return self.config.get('logPesters', self.LOG | self.STAMP) + return self.config.get("logPesters", self.LOG | self.STAMP) + def logMemos(self): - return self.config.get('logMemos', self.LOG) + return self.config.get("logMemos", self.LOG) + def disableUserLinks(self): - return not self.config.get('userLinks', True) + return not self.config.get("userLinks", True) + def idleTime(self): - return self.config.get('idleTime', 10) + return self.config.get("idleTime", 10) + def minimizeAction(self): - return self.config.get('miniAction', 0) + return self.config.get("miniAction", 0) + def closeAction(self): - return self.config.get('closeAction', 1) + return self.config.get("closeAction", 1) + def opvoiceMessages(self): - return self.config.get('opvMessages', True) + return self.config.get("opvMessages", True) + def animations(self): - return self.config.get('animations', True) - #def checkForUpdates(self): + return self.config.get("animations", True) + + # def checkForUpdates(self): # u = self.config.get('checkUpdates', 0) # if type(u) == type(bool()): # if u: u = 2 @@ -282,53 +340,67 @@ with a backup from: %s" % (_datadir, self.filename, # # Once a week # # Only on start # # Never - #def lastUCheck(self): + # def lastUCheck(self): # return self.config.get('lastUCheck', 0) - #def checkMSPA(self): + # def checkMSPA(self): # return self.config.get('mspa', False) def blink(self): - return self.config.get('blink', self.PBLINK | self.MBLINK) + return self.config.get("blink", self.PBLINK | self.MBLINK) + def notify(self): - return self.config.get('notify', True) + return self.config.get("notify", True) + def notifyType(self): - return self.config.get('notifyType', "default") + return self.config.get("notifyType", "default") + def notifyOptions(self): - return self.config.get('notifyOptions', self.SIGNIN | self.NEWMSG | self.NEWCONVO | self.INITIALS) + return self.config.get( + "notifyOptions", self.SIGNIN | self.NEWMSG | self.NEWCONVO | self.INITIALS + ) + def lowBandwidth(self): - return self.config.get('lowBandwidth', False) + return self.config.get("lowBandwidth", False) + def ghostchum(self): - return self.config.get('ghostchum', False) + return self.config.get("ghostchum", False) + def addChum(self, chum): if chum.handle not in self.chums(): with open(self.filename) as fp: # what if we have two clients open?? newconfig = json.load(fp) - newchums = newconfig['chums'] + [chum.handle] + newchums = newconfig["chums"] + [chum.handle] self.set("chums", newchums) + def removeChum(self, chum): if type(chum) is PesterProfile: handle = chum.handle else: handle = chum - newchums = [c for c in self.config['chums'] if c != handle] + newchums = [c for c in self.config["chums"] if c != handle] self.set("chums", newchums) + def getBlocklist(self): - if 'block' not in self.config: - self.set('block', []) - return self.config['block'] + if "block" not in self.config: + self.set("block", []) + return self.config["block"] + def addBlocklist(self, handle): l = self.getBlocklist() if handle not in l: l.append(handle) - self.set('block', l) + self.set("block", l) + def delBlocklist(self, handle): l = self.getBlocklist() l.pop(l.index(handle)) - self.set('block', l) + self.set("block", l) + def getGroups(self): - if 'groups' not in self.groups: + if "groups" not in self.groups: self.saveGroups([["Chums", True]]) - return self.groups.get('groups', [["Chums", True]]) + return self.groups.get("groups", [["Chums", True]]) + def addGroup(self, group, open=True): l = self.getGroups() exists = False @@ -337,18 +409,21 @@ with a backup from: %s" % (_datadir, self.filename, exists = True break if not exists: - l.append([group,open]) + l.append([group, open]) l.sort() self.saveGroups(l) + def delGroup(self, group): l = self.getGroups() i = 0 for g in l: - if g[0] == group: break - i = i+1 + if g[0] == group: + break + i = i + 1 l.pop(i) l.sort() self.saveGroups(l) + def expandGroup(self, group, open=True): l = self.getGroups() for g in l: @@ -356,114 +431,126 @@ with a backup from: %s" % (_datadir, self.filename, g[1] = open break self.saveGroups(l) + def saveGroups(self, groups): - self.groups['groups'] = groups + self.groups["groups"] = groups try: jsonoutput = json.dumps(self.groups) except ValueError as e: raise e - with open("%s/groups.js" % (self.logpath), 'w') as fp: + with open("%s/groups.js" % (self.logpath), "w") as fp: fp.write(jsonoutput) def server(self): - if hasattr(self.parent, 'serverOverride'): + if hasattr(self.parent, "serverOverride"): return self.parent.serverOverride try: with open(_datadir + "server.json", "r") as server_file: read_file = server_file.read() server_file.close() server_obj = json.loads(read_file) - return server_obj['server'] + return server_obj["server"] except: try: with open(_datadir + "server.json", "w") as server_file: json_server_file = { - "server": "irc.pesterchum.xyz", - "port": "6697", - "TLS": True - } - server_file.write(json.dumps(json_server_file, indent = 4) ) + "server": "irc.pesterchum.xyz", + "port": "6697", + "TLS": True, + } + server_file.write(json.dumps(json_server_file, indent=4)) server_file.close() server = "irc.pesterchum.xyz" except: - return self.config.get('server', "irc.pesterchum.xyz") + return self.config.get("server", "irc.pesterchum.xyz") + def port(self): - if hasattr(self.parent, 'portOverride'): + if hasattr(self.parent, "portOverride"): return self.parent.portOverride try: with open(_datadir + "server.json", "r") as server_file: read_file = server_file.read() server_file.close() server_obj = json.loads(read_file) - port = server_obj['port'] + port = server_obj["port"] return port except: - return self.config.get('port', '6697') + return self.config.get("port", "6697") def ssl(self): - #if hasattr(self.parent, 'tlsOverride'): + # if hasattr(self.parent, 'tlsOverride'): # return self.parent.tlsOverride try: with open(_datadir + "server.json", "r") as server_file: read_file = server_file.read() server_file.close() server_obj = json.loads(read_file) - return server_obj['TLS'] + return server_obj["TLS"] except: - return self.config.get('TLS', True) + return self.config.get("TLS", True) + def soundOn(self): - if 'soundon' not in self.config: - self.set('soundon', True) - return self.config['soundon'] + if "soundon" not in self.config: + self.set("soundon", True) + return self.config["soundon"] + def chatSound(self): - return self.config.get('chatSound', True) + return self.config.get("chatSound", True) + def memoSound(self): - return self.config.get('memoSound', True) + return self.config.get("memoSound", True) + def memoPing(self): - return self.config.get('pingSound', True) + return self.config.get("pingSound", True) + def nameSound(self): - return self.config.get('nameSound', True) + return self.config.get("nameSound", True) + def volume(self): - return self.config.get('volume', 100) + return self.config.get("volume", 100) + def trayMessage(self): - return self.config.get('traymsg', True) + return self.config.get("traymsg", True) + def set(self, item, setting): self.config[item] = setting try: jsonoutput = json.dumps(self.config) except ValueError as e: raise e - with open(self.filename, 'w') as fp: + with open(self.filename, "w") as fp: fp.write(jsonoutput) + def availableThemes(self): themes = [] # Load user themes. - for dirname, dirnames, filenames in os.walk(_datadir+'themes'): + for dirname, dirnames, filenames in os.walk(_datadir + "themes"): for d in dirnames: themes.append(d) # Also load embedded themes. if _datadir: - for dirname, dirnames, filenames in os.walk('themes'): + for dirname, dirnames, filenames in os.walk("themes"): for d in dirnames: if d not in themes: themes.append(d) themes.sort() return themes + def availableProfiles(self): profs = [] - profileloc = _datadir+'profiles' + profileloc = _datadir + "profiles" for dirname, dirnames, filenames in os.walk(profileloc): for filename in filenames: l = len(filename) - if filename[l-3:l] == ".js": - profs.append(filename[0:l-3]) + if filename[l - 3 : l] == ".js": + profs.append(filename[0 : l - 3]) profs.sort() PchumLog.info("Profiles: %s" % str(profs)) - + # Validity check PchumLog.info("Starting profile check. . .") for x in profs: - c_profile = os.path.join(profileloc, x+".js") + c_profile = os.path.join(profileloc, x + ".js") try: json.load(open(c_profile)) PchumLog.info(x + ": Pass.") @@ -472,34 +559,48 @@ with a backup from: %s" % (_datadir, self.filename, PchumLog.warning(e) profs.remove(x) PchumLog.warning(x + " removed from profile list.") - + msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Icon.Warning) msgBox.setWindowTitle(":(") - msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links - self.filename = _datadir+"pesterchum.js" - msgBox.setText("

Failed to load " + x + ", removed from list." + \ - "

Consider taking a look at: "+ os.path.join(profileloc, x + ".js") + "" + \ - "

" + str(e) + "<\h3><\html>") - #"\" if pesterchum acts oddly you might want to try backing up and then deleting \"" + \ - #_datadir+"pesterchum.js" + \ - #"\"") + msgBox.setTextFormat( + QtCore.Qt.TextFormat.RichText + ) # Clickable html links + self.filename = _datadir + "pesterchum.js" + msgBox.setText( + "

Failed to load " + + x + + ", removed from list." + + "

Consider taking a look at: " + + os.path.join(profileloc, x + ".js") + + "" + + "

" + + str(e) + + "<\h3><\html>" + ) + # "\" if pesterchum acts oddly you might want to try backing up and then deleting \"" + \ + # _datadir+"pesterchum.js" + \ + # "\"") PchumLog.critical(e) msgBox.exec() - - + return [userProfile(p) for p in profs] + class userProfile(object): def __init__(self, user): - self.profiledir = _datadir+"profiles" + self.profiledir = _datadir + "profiles" if type(user) is PesterProfile: self.chat = user - self.userprofile = {"handle":user.handle, - "color": str(user.color.name()), - "quirks": [], - "theme": "pesterchum"} + self.userprofile = { + "handle": user.handle, + "color": str(user.color.name()), + "quirks": [], + "theme": "pesterchum", + } self.theme = pesterTheme("pesterchum") self.chat.mood = Mood(self.theme["main/defaultmood"]) self.lastmood = self.chat.mood.value() @@ -507,7 +608,11 @@ class userProfile(object): self.randoms = False initials = self.chat.initials() if len(initials) >= 2: - initials = (initials, "%s%s" % (initials[0].lower(), initials[1]), "%s%s" % (initials[0], initials[1].lower())) + initials = ( + initials, + "%s%s" % (initials[0].lower(), initials[1]), + "%s%s" % (initials[0], initials[1].lower()), + ) self.mentions = [r"\b(%s)\b" % ("|".join(initials))] else: self.mentions = [] @@ -525,15 +630,25 @@ class userProfile(object): msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Icon.Warning) msgBox.setWindowTitle(":(") - msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText) # Clickable html links - self.filename = _datadir+"pesterchum.js" - msgBox.setText("

Failed to load: " + ("%s/%s.js" % (self.profiledir, self.profiledir, user)) + \ - "

Try to check for syntax errors if the file exists." + \ - "

If you got this message at launch you may want to change your default profile." + \ - "

" + str(e) + "<\h3><\html>") - #"\" if pesterchum acts oddly you might want to try backing up and then deleting \"" + \ - #_datadir+"pesterchum.js" + \ - #"\"") + msgBox.setTextFormat( + QtCore.Qt.TextFormat.RichText + ) # Clickable html links + self.filename = _datadir + "pesterchum.js" + msgBox.setText( + "

Failed to load: " + + ( + "%s/%s.js" + % (self.profiledir, self.profiledir, user) + ) + + "

Try to check for syntax errors if the file exists." + + "

If you got this message at launch you may want to change your default profile." + + "

" + + str(e) + + "<\h3><\html>" + ) + # "\" if pesterchum acts oddly you might want to try backing up and then deleting \"" + \ + # _datadir+"pesterchum.js" + \ + # "\"") PchumLog.critical(e) msgBox.exec() raise ValueError(e) @@ -542,10 +657,14 @@ class userProfile(object): self.theme = pesterTheme(self.userprofile["theme"]) except ValueError: self.theme = pesterTheme("pesterchum") - self.lastmood = self.userprofile.get('lastmood', self.theme["main/defaultmood"]) - self.chat = PesterProfile(self.userprofile["handle"], - QtGui.QColor(self.userprofile["color"]), - Mood(self.lastmood)) + self.lastmood = self.userprofile.get( + "lastmood", self.theme["main/defaultmood"] + ) + self.chat = PesterProfile( + self.userprofile["handle"], + QtGui.QColor(self.userprofile["color"]), + Mood(self.lastmood), + ) self.quirks = pesterQuirks(self.userprofile["quirks"]) if "randoms" not in self.userprofile: self.userprofile["randoms"] = False @@ -553,7 +672,11 @@ class userProfile(object): if "mentions" not in self.userprofile: initials = self.chat.initials() if len(initials) >= 2: - initials = (initials, "%s%s" % (initials[0].lower(), initials[1]), "%s%s" % (initials[0], initials[1].lower())) + initials = ( + initials, + "%s%s" % (initials[0].lower(), initials[1]), + "%s%s" % (initials[0], initials[1].lower()), + ) self.userprofile["mentions"] = [r"\b(%s)\b" % ("|".join(initials))] else: self.userprofile["mentions"] = [] @@ -563,7 +686,7 @@ class userProfile(object): self.autojoins = self.userprofile["autojoins"] try: - with open(_datadir+"passwd.js") as fp: + with open(_datadir + "passwd.js") as fp: self.passwd = json.load(fp) except: self.passwd = {} @@ -571,13 +694,13 @@ class userProfile(object): self.nickservpass = "" if self.chat.handle in self.passwd: # Fix for: - # Traceback (most recent call last): - # File "pesterchum.py", line 2944, in nickCollision - # File "pesterchum.py", line 1692, in changeProfile - # File "XXX\menus.py", line 795, in init - # File "XXX\profile.py", line 350, in availableProfiles - # File "XXX\profile.py", line 432, in init - # KeyError: 'pw' + # Traceback (most recent call last): + # File "pesterchum.py", line 2944, in nickCollision + # File "pesterchum.py", line 1692, in changeProfile + # File "XXX\menus.py", line 795, in init + # File "XXX\profile.py", line 350, in availableProfiles + # File "XXX\profile.py", line 432, in init + # KeyError: 'pw' if "auto" in self.passwd[self.chat.handle]: self.autoidentify = self.passwd[self.chat.handle]["auto"] if "pw" in self.passwd[self.chat.handle]: @@ -585,29 +708,36 @@ class userProfile(object): def setMood(self, mood): self.chat.mood = mood + def setTheme(self, theme): self.theme = theme self.userprofile["theme"] = theme.name self.save() + def setColor(self, color): self.chat.color = color self.userprofile["color"] = str(color.name()) self.save() + def setQuirks(self, quirks): self.quirks = quirks self.userprofile["quirks"] = self.quirks.plainList() self.save() + def getRandom(self): return self.randoms + def setRandom(self, random): self.randoms = random self.userprofile["randoms"] = random self.save() + def getMentions(self): return self.mentions + def setMentions(self, mentions): try: - for (i,m) in enumerate(mentions): + for (i, m) in enumerate(mentions): re.compile(m) except re.error as e: PchumLog.error("#%s Not a valid regular expression: %s" % (i, e)) @@ -615,36 +745,46 @@ class userProfile(object): self.mentions = mentions self.userprofile["mentions"] = mentions self.save() + def getLastMood(self): return self.lastmood + def setLastMood(self, mood): self.lastmood = mood.value() self.userprofile["lastmood"] = self.lastmood self.save() + def getTheme(self): return self.theme + def getAutoIdentify(self): return self.autoidentify + def setAutoIdentify(self, b): self.autoidentify = b if self.chat.handle not in self.passwd: self.passwd[self.chat.handle] = {} self.passwd[self.chat.handle]["auto"] = b self.saveNickServPass() + def getNickServPass(self): return self.nickservpass + def setNickServPass(self, pw): self.nickservpass = pw if self.chat.handle not in self.passwd: self.passwd[self.chat.handle] = {} self.passwd[self.chat.handle]["pw"] = pw self.saveNickServPass() + def getAutoJoins(self): return self.autojoins + def setAutoJoins(self, autojoins): self.autojoins = autojoins self.userprofile["autojoins"] = self.autojoins self.save() + def save(self): handle = self.chat.handle if handle[0:12] == "pesterClient": @@ -654,107 +794,130 @@ class userProfile(object): jsonoutput = json.dumps(self.userprofile) except ValueError as e: raise e - with open("%s/%s.js" % (self.profiledir, handle), 'w') as fp: + with open("%s/%s.js" % (self.profiledir, handle), "w") as fp: fp.write(jsonoutput) + def saveNickServPass(self): # remove profiles with no passwords - for h,t in list(self.passwd.items()): + for h, t in list(self.passwd.items()): if "auto" not in t and ("pw" not in t or t["pw"] == ""): del self.passwd[h] try: jsonoutput = json.dumps(self.passwd, indent=4) except ValueError as e: raise e - with open(_datadir+"passwd.js", 'w') as fp: + with open(_datadir + "passwd.js", "w") as fp: fp.write(jsonoutput) + @staticmethod def newUserProfile(chatprofile): - if os.path.exists("%s/%s.js" % (_datadir+"profiles", chatprofile.handle)): + if os.path.exists("%s/%s.js" % (_datadir + "profiles", chatprofile.handle)): newprofile = userProfile(chatprofile.handle) else: newprofile = userProfile(chatprofile) newprofile.save() return newprofile + class PesterProfileDB(dict): def __init__(self): - self.logpath = _datadir+"logs" + self.logpath = _datadir + "logs" if not os.path.exists(self.logpath): os.makedirs(self.logpath) try: - with open("%s/chums.js" % (self.logpath), 'r') as fp: + with open("%s/chums.js" % (self.logpath), "r") as fp: chumdict = json.load(fp) except (IOError, ValueError): # karxi: This code feels awfully familiar.... chumdict = {} - with open("%s/chums.js" % (self.logpath), 'w') as fp: + with open("%s/chums.js" % (self.logpath), "w") as fp: json.dump(chumdict, fp) u = [] for (handle, c) in chumdict.items(): options = dict() - if 'group' in c: - options['group'] = c['group'] - if 'notes' in c: - options['notes'] = c['notes'] - if 'color' not in c: - c['color'] = "#000000" - if 'mood' not in c: - c['mood'] = "offline" - u.append((handle, PesterProfile(handle, color=QtGui.QColor(c['color']), mood=Mood(c['mood']), **options))) + if "group" in c: + options["group"] = c["group"] + if "notes" in c: + options["notes"] = c["notes"] + if "color" not in c: + c["color"] = "#000000" + if "mood" not in c: + c["mood"] = "offline" + u.append( + ( + handle, + PesterProfile( + handle, + color=QtGui.QColor(c["color"]), + mood=Mood(c["mood"]), + **options + ), + ) + ) converted = dict(u) self.update(converted) def save(self): try: - with open("%s/chums.js" % (self.logpath), 'w') as fp: + with open("%s/chums.js" % (self.logpath), "w") as fp: chumdict = dict([p.plaindict() for p in self.values()]) json.dump(chumdict, fp) except Exception as e: raise e + def getColor(self, handle, default=None): if handle not in self: return default else: return self[handle].color + def setColor(self, handle, color): if handle in self: self[handle].color = color else: self[handle] = PesterProfile(handle, color) + def getGroup(self, handle, default="Chums"): if handle not in self: return default else: return self[handle].group + def setGroup(self, handle, theGroup): if handle in self: self[handle].group = theGroup else: self[handle] = PesterProfile(handle, group=theGroup) self.save() + def getNotes(self, handle, default=""): if handle not in self: return default else: return self[handle].notes + def setNotes(self, handle, notes): if handle in self: self[handle].notes = notes else: self[handle] = PesterProfile(handle, notes=notes) self.save() + def __setitem__(self, key, val): dict.__setitem__(self, key, val) self.save() + class pesterTheme(dict): def __init__(self, name, default=False): - possiblepaths = (_datadir+"themes/%s" % (name), - "themes/%s" % (name), - _datadir+"themes/pesterchum", - "themes/pesterchum") + possiblepaths = ( + _datadir + "themes/%s" % (name), + "themes/%s" % (name), + _datadir + "themes/pesterchum", + "themes/pesterchum", + ) self.path = "themes/pesterchum" for p in possiblepaths: if os.path.exists(p): @@ -763,7 +926,7 @@ class pesterTheme(dict): self.name = name try: - with open(self.path+"/style.js") as fp: + with open(self.path + "/style.js") as fp: theme = json.load(fp, object_hook=self.pathHook) except IOError: theme = json.loads("{}") @@ -772,34 +935,37 @@ class pesterTheme(dict): self.inheritedTheme = pesterTheme(self["inherits"]) if not default: self.defaultTheme = pesterTheme("pesterchum", default=True) + def __getitem__(self, key): keys = key.split("/") try: v = super(pesterTheme, self).__getitem__(keys.pop(0)) except KeyError as e: - if hasattr(self, 'inheritedTheme'): - return self.inheritedTheme[key] - elif hasattr(self, 'defaultTheme'): - return self.defaultTheme[key] - else: - raise e + if hasattr(self, "inheritedTheme"): + return self.inheritedTheme[key] + elif hasattr(self, "defaultTheme"): + return self.defaultTheme[key] + else: + raise e for k in keys: try: v = v[k] except KeyError as e: - if hasattr(self, 'inheritedTheme'): + if hasattr(self, "inheritedTheme"): return self.inheritedTheme[key] - elif hasattr(self, 'defaultTheme'): + elif hasattr(self, "defaultTheme"): return self.defaultTheme[key] else: raise e return v + def pathHook(self, d): for (k, v) in d.items(): if isinstance(v, str): s = Template(v) d[k] = s.safe_substitute(path=self.path) return d + def get(self, key, default): keys = key.split("/") try: @@ -808,7 +974,7 @@ class pesterTheme(dict): v = v[k] return default if v is None else v except KeyError: - if hasattr(self, 'inheritedTheme'): + if hasattr(self, "inheritedTheme"): return self.inheritedTheme.get(key, default) else: return default @@ -819,9 +985,9 @@ class pesterTheme(dict): v = super(pesterTheme, self).__getitem__(keys.pop(0)) for k in keys: v = v[k] - return (v is not None) + return v is not None except KeyError: - if hasattr(self, 'inheritedTheme'): + if hasattr(self, "inheritedTheme"): return key in self.inheritedTheme else: return False diff --git a/pyinstaller.py b/pyinstaller.py index 316a47d..f140a4c 100644 --- a/pyinstaller.py +++ b/pyinstaller.py @@ -5,425 +5,647 @@ import shutil import PyInstaller.__main__ is_64bit = sys.maxsize > 2**32 -#is_linux = sys.platform.startswith("linux") +# is_linux = sys.platform.startswith("linux") exclude_modules = [] -#if is_linux == False: +# if is_linux == False: # print("Not Linux, excluding pygame.") # exclude_modules.append('pygame') -add_data = ['quirks;quirks', - 'smilies;smilies', - 'themes;themes', - 'docs;docs', - 'README.md;.', - 'LICENSE;.', - 'CHANGELOG.md;.', - 'PCskins.png;.', - 'Pesterchum.png;.'] -data_folders = {'quirks': 'quirks', - 'smilies': 'smilies', - 'themes': 'themes', - 'docs': 'docs'} -data_files = {'README.md': 'README.md.txt', - 'LICENSE': 'LICENSE.txt', - 'CHANGELOG.md': 'CHANGELOG.md.txt', - 'PCskins.png': '.', - 'Pesterchum.png': '.'} -data_files_linux = {'README.md': 'README.md', - 'LICENSE': 'LICENSE.txt', - 'CHANGELOG.md': 'CHANGELOG.md', - 'PCskins.png': '.', - 'Pesterchum.png': '.'} +add_data = [ + "quirks;quirks", + "smilies;smilies", + "themes;themes", + "docs;docs", + "README.md;.", + "LICENSE;.", + "CHANGELOG.md;.", + "PCskins.png;.", + "Pesterchum.png;.", +] +data_folders = { + "quirks": "quirks", + "smilies": "smilies", + "themes": "themes", + "docs": "docs", +} +data_files = { + "README.md": "README.md.txt", + "LICENSE": "LICENSE.txt", + "CHANGELOG.md": "CHANGELOG.md.txt", + "PCskins.png": ".", + "Pesterchum.png": ".", +} +data_files_linux = { + "README.md": "README.md", + "LICENSE": "LICENSE.txt", + "CHANGELOG.md": "CHANGELOG.md", + "PCskins.png": ".", + "Pesterchum.png": ".", +} # Some of these might not be required anymore, # newer versions of PyInstaller claim to exclude certain problematic DDLs automatically. -upx_exclude = ["qwindows.dll", +upx_exclude = [ + "qwindows.dll", "Qt6Core.dll", "Qt6Gui.dll", "vcruntime140.dll", "MSVCP140.dll", - "MSVCP140_1.dll" - "api-ms-win-core-console-l1-1-0.dll",\ - "api-ms-win-core-console-l1-1-0.dll",\ - "api-ms-win-core-console-l1-2-0.dll",\ - "api-ms-win-core-datetime-l1-1-0.dll",\ - "api-ms-win-core-debug-l1-1-0.dll",\ - "api-ms-win-core-errorhandling-l1-1-0.dll",\ - "api-ms-win-core-file-l1-1-0.dll",\ - "api-ms-win-core-file-l1-2-0.dll",\ - "api-ms-win-core-file-l2-1-0.dll",\ - "api-ms-win-core-handle-l1-1-0.dll",\ - "api-ms-win-core-heap-l1-1-0.dll",\ - "api-ms-win-core-interlocked-l1-1-0.dll",\ - "api-ms-win-core-libraryloader-l1-1-0.dll",\ - "api-ms-win-core-localization-l1-2-0.dll",\ - "api-ms-win-core-memory-l1-1-0.dll",\ - "api-ms-win-core-namedpipe-l1-1-0.dll",\ - "api-ms-win-core-processenvironment-l1-1-0.dll",\ - "api-ms-win-core-processthreads-l1-1-0.dll",\ - "api-ms-win-core-processthreads-l1-1-1.dll",\ - "api-ms-win-core-profile-l1-1-0.dll",\ - "api-ms-win-core-rtlsupport-l1-1-0.dll",\ - "api-ms-win-core-string-l1-1-0.dll",\ - "api-ms-win-core-synch-l1-1-0.dll",\ - "api-ms-win-core-synch-l1-2-0.dll",\ - "api-ms-win-core-sysinfo-l1-1-0.dll",\ - "api-ms-win-core-timezone-l1-1-0.dll",\ - "api-ms-win-core-util-l1-1-0.dll",\ - "API-MS-Win-core-xstate-l2-1-0.dll",\ - "api-ms-win-crt-conio-l1-1-0.dll",\ - "api-ms-win-crt-convert-l1-1-0.dll",\ - "api-ms-win-crt-environment-l1-1-0.dll",\ - "api-ms-win-crt-filesystem-l1-1-0.dll",\ - "api-ms-win-crt-heap-l1-1-0.dll",\ - "api-ms-win-crt-locale-l1-1-0.dll",\ - "api-ms-win-crt-math-l1-1-0.dll",\ - "api-ms-win-crt-multibyte-l1-1-0.dll",\ - "api-ms-win-crt-private-l1-1-0.dll",\ - "api-ms-win-crt-process-l1-1-0.dll",\ - "api-ms-win-crt-runtime-l1-1-0.dll",\ - "api-ms-win-crt-stdio-l1-1-0.dll",\ - "api-ms-win-crt-string-l1-1-0.dll",\ - "api-ms-win-crt-time-l1-1-0.dll",\ - "api-ms-win-crt-utility-l1-1-0.dll",\ - "ucrtbase.dll"] + "MSVCP140_1.dll" "api-ms-win-core-console-l1-1-0.dll", + "api-ms-win-core-console-l1-1-0.dll", + "api-ms-win-core-console-l1-2-0.dll", + "api-ms-win-core-datetime-l1-1-0.dll", + "api-ms-win-core-debug-l1-1-0.dll", + "api-ms-win-core-errorhandling-l1-1-0.dll", + "api-ms-win-core-file-l1-1-0.dll", + "api-ms-win-core-file-l1-2-0.dll", + "api-ms-win-core-file-l2-1-0.dll", + "api-ms-win-core-handle-l1-1-0.dll", + "api-ms-win-core-heap-l1-1-0.dll", + "api-ms-win-core-interlocked-l1-1-0.dll", + "api-ms-win-core-libraryloader-l1-1-0.dll", + "api-ms-win-core-localization-l1-2-0.dll", + "api-ms-win-core-memory-l1-1-0.dll", + "api-ms-win-core-namedpipe-l1-1-0.dll", + "api-ms-win-core-processenvironment-l1-1-0.dll", + "api-ms-win-core-processthreads-l1-1-0.dll", + "api-ms-win-core-processthreads-l1-1-1.dll", + "api-ms-win-core-profile-l1-1-0.dll", + "api-ms-win-core-rtlsupport-l1-1-0.dll", + "api-ms-win-core-string-l1-1-0.dll", + "api-ms-win-core-synch-l1-1-0.dll", + "api-ms-win-core-synch-l1-2-0.dll", + "api-ms-win-core-sysinfo-l1-1-0.dll", + "api-ms-win-core-timezone-l1-1-0.dll", + "api-ms-win-core-util-l1-1-0.dll", + "API-MS-Win-core-xstate-l2-1-0.dll", + "api-ms-win-crt-conio-l1-1-0.dll", + "api-ms-win-crt-convert-l1-1-0.dll", + "api-ms-win-crt-environment-l1-1-0.dll", + "api-ms-win-crt-filesystem-l1-1-0.dll", + "api-ms-win-crt-heap-l1-1-0.dll", + "api-ms-win-crt-locale-l1-1-0.dll", + "api-ms-win-crt-math-l1-1-0.dll", + "api-ms-win-crt-multibyte-l1-1-0.dll", + "api-ms-win-crt-private-l1-1-0.dll", + "api-ms-win-crt-process-l1-1-0.dll", + "api-ms-win-crt-runtime-l1-1-0.dll", + "api-ms-win-crt-stdio-l1-1-0.dll", + "api-ms-win-crt-string-l1-1-0.dll", + "api-ms-win-crt-time-l1-1-0.dll", + "api-ms-win-crt-utility-l1-1-0.dll", + "ucrtbase.dll", +] -delete_builddist = '' -upx_enabled = '' -package_universal_crt = '' -onefile = '' -windowed = '' +delete_builddist = "" +upx_enabled = "" +package_universal_crt = "" +onefile = "" +windowed = "" try: print("This is a script to make building with Pyinstaller a bit more conventient.") - while (delete_builddist != 'y') and (delete_builddist != 'n'): + while (delete_builddist != "y") and (delete_builddist != "n"): delete_builddist = input("Delete build & dist folders? (Y/N): ").lower() if delete_builddist == "y": try: - shutil.rmtree('dist') + shutil.rmtree("dist") except FileNotFoundError as e: print(e) try: - shutil.rmtree('build') + shutil.rmtree("build") except FileNotFoundError as e: print(e) - - while (upx_enabled != 'y') and (upx_enabled != 'n'): + + while (upx_enabled != "y") and (upx_enabled != "n"): upx_enabled = input("Enable UPX? (Y/N): ").lower() - if upx_enabled == 'y': + if upx_enabled == "y": print("If upx is on your path you don't need to include anything here.") if is_64bit == True: upx_dir = input("UPX directory [D:\\upx-3.96-win64]: ") - if upx_dir == '': - upx_dir = "D:\\upx-3.96-win64" # Default dir for me :) + if upx_dir == "": + upx_dir = "D:\\upx-3.96-win64" # Default dir for me :) else: upx_dir = input("UPX directory [D:\\upx-3.96-win32]: ") - if upx_dir == '': - upx_dir = "D:\\upx-3.96-win32" # Default dir for me :) + if upx_dir == "": + upx_dir = "D:\\upx-3.96-win32" # Default dir for me :) print("upx_dir = " + upx_dir) - elif upx_enabled == 'n': - upx_dir = '' - - while (windowed != 'y') and (windowed != 'n'): + elif upx_enabled == "n": + upx_dir = "" + + while (windowed != "y") and (windowed != "n"): windowed = input("Build with '--windowed'? (Y/N): ").lower() - - if sys.platform == 'win32': - print("(https://pyinstaller.readthedocs.io/en/stable/usage.html?highlight=sdk#windows)") - while (package_universal_crt != 'y') and (package_universal_crt != 'n'): - package_universal_crt = input("Try to include universal CRT? (Y/N): ").lower() - if package_universal_crt == 'y': + + if sys.platform == "win32": + print( + "(https://pyinstaller.readthedocs.io/en/stable/usage.html?highlight=sdk#windows)" + ) + while (package_universal_crt != "y") and (package_universal_crt != "n"): + package_universal_crt = input( + "Try to include universal CRT? (Y/N): " + ).lower() + if package_universal_crt == "y": if is_64bit == True: - crt_path = input("Universal CRT: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64]: ") - if crt_path == '': - #crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64" # Default directory. - crt_path = os.path.join('C:%s' % os.sep, 'Program Files (x86)', 'Windows Kits', '10', '10.0.19041.0', 'ucrt', 'DLLs', 'x64') + crt_path = input( + "Universal CRT: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64]: " + ) + if crt_path == "": + # crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64" # Default directory. + crt_path = os.path.join( + "C:%s" % os.sep, + "Program Files (x86)", + "Windows Kits", + "10", + "10.0.19041.0", + "ucrt", + "DLLs", + "x64", + ) else: - crt_path = input("Extra path: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86]: ") - if crt_path == '': - #crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86" # Default directory. - crt_path = os.path.join('C:%s' % os.sep, 'Program Files (x86)', 'Windows Kits', '10', '10.0.19041.0', 'ucrt', 'DLLs', 'x86') + crt_path = input( + "Extra path: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86]: " + ) + if crt_path == "": + # crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86" # Default directory. + crt_path = os.path.join( + "C:%s" % os.sep, + "Program Files (x86)", + "Windows Kits", + "10", + "10.0.19041.0", + "ucrt", + "DLLs", + "x86", + ) print("crt_path = " + crt_path) - - if (sys.platform == 'win32') or (sys.platform == 'linux'): - while (onefile != 'y') and (onefile != 'n'): + + if (sys.platform == "win32") or (sys.platform == "linux"): + while (onefile != "y") and (onefile != "n"): onefile = input("Build with '--onefile'? (Y/N): ").lower() - + except KeyboardInterrupt: sys.exit("KeyboardInterrupt") -#Windows -if sys.platform == 'win32': +# Windows +if sys.platform == "win32": run_win32 = [ - 'pesterchum.py', - '--name=Pesterchum', + "pesterchum.py", + "--name=Pesterchum", #'--noconfirm', # Overwrite output directory. #'--windowed', # Hide console - '--icon=pesterchum.ico', - '--clean', # Clear cache - + "--icon=pesterchum.ico", + "--clean", # Clear cache ] if (sys.version_info.major == 3) & (sys.version_info.minor == 8): - exclude_modules.append('tkinter') - if upx_enabled == 'y': + exclude_modules.append("tkinter") + if upx_enabled == "y": if os.path.isdir(upx_dir): - run_win32.append('--upx-dir=%s' % upx_dir) + run_win32.append("--upx-dir=%s" % upx_dir) for x in upx_exclude: - run_win32.append('--upx-exclude=%s' % x ) + run_win32.append("--upx-exclude=%s" % x) # Lower case variants are required. - run_win32.append('--upx-exclude=%s' % x.lower() ) - elif upx_enabled == 'n': - run_win32.append('--noupx') + run_win32.append("--upx-exclude=%s" % x.lower()) + elif upx_enabled == "n": + run_win32.append("--noupx") for x in exclude_modules: - run_win32.append('--exclude-module=%s' % x ) - if windowed == 'y': - run_win32.append('--windowed') - if onefile == 'y': - run_win32.append('--onefile') - elif onefile == 'n': + run_win32.append("--exclude-module=%s" % x) + if windowed == "y": + run_win32.append("--windowed") + if onefile == "y": + run_win32.append("--onefile") + elif onefile == "n": for x in add_data: - run_win32.append('--add-data=%s' % x ) + run_win32.append("--add-data=%s" % x) - - if package_universal_crt == 'y': - run_win32.append('--paths=\"%s\"' % crt_path) + if package_universal_crt == "y": + run_win32.append('--paths="%s"' % crt_path) if os.path.exists(crt_path): if is_64bit == False: - run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\API-MS-Win-core-xstate-l2-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\ucrtbase.dll;.' % crt_path) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\API-MS-Win-core-xstate-l2-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;." % crt_path + ) + run_win32.append("--add-binary=%s\\ucrtbase.dll;." % crt_path) elif is_64bit == True: - run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;.' % crt_path) - run_win32.append('--add-binary=%s\\ucrtbase.dll;.' % crt_path) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;." + % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;." % crt_path + ) + run_win32.append( + "--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;." % crt_path + ) + run_win32.append("--add-binary=%s\\ucrtbase.dll;." % crt_path) print(run_win32) PyInstaller.__main__.run(run_win32) - - if onefile == 'y': + + if onefile == "y": # There's more proper ways to do this, but this doesn't require changing our paths for x in data_folders: print(x) - shutil.copytree(x, os.path.join('dist', data_folders[x]), - ignore=shutil.ignore_patterns('*.psd', - '*.xcf*', - 'ebg2.png', - 'ebg1.png')) + shutil.copytree( + x, + os.path.join("dist", data_folders[x]), + ignore=shutil.ignore_patterns( + "*.psd", "*.xcf*", "ebg2.png", "ebg1.png" + ), + ) for x in data_files: print(x) - shutil.copy(x, os.path.join('dist', data_files[x])) - - files = os.listdir('dist') + shutil.copy(x, os.path.join("dist", data_files[x])) + + files = os.listdir("dist") try: - os.mkdir(os.path.join('dist', 'Pesterchum')) + os.mkdir(os.path.join("dist", "Pesterchum")) except FileExistsError: pass for x in files: - shutil.move(os.path.join('dist',x), os.path.join('dist', 'Pesterchum')) - - #shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'), + shutil.move(os.path.join("dist", x), os.path.join("dist", "Pesterchum")) + + # shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'), # os.path.join('dist', 'Pesterchum', 'xref-Pesterchum.html')) - #shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'), + # shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'), # os.path.join('dist', 'Pesterchum', 'Analysis-00.toc')) - -#MacOS -elif sys.platform == 'darwin' : - run_darwin =[ - 'pesterchum.py', - '--name=Pesterchum', + +# MacOS +elif sys.platform == "darwin": + run_darwin = [ + "pesterchum.py", + "--name=Pesterchum", #'--windowed', # Hide console #'--noconfirm', # Overwrite output directory. - '--icon=trayicon32.icns', # Icon - '--onedir', - '--clean', # Clear cache + "--icon=trayicon32.icns", # Icon + "--onedir", + "--clean", # Clear cache #'--noupx' ] - if upx_enabled == 'y': + if upx_enabled == "y": if os.path.isdir(upx_dir): - run_darwin.append('--upx-dir=%s' % upx_dir) + run_darwin.append("--upx-dir=%s" % upx_dir) for x in upx_exclude: - run_darwin.append('--upx-exclude=%s' % x ) + run_darwin.append("--upx-exclude=%s" % x) # Lower case variants are required. - run_darwin.append('--upx-exclude=%s' % x.lower() ) - elif upx_enabled == 'n': - run_darwin.append('--noupx') + run_darwin.append("--upx-exclude=%s" % x.lower()) + elif upx_enabled == "n": + run_darwin.append("--noupx") if os.path.isdir(upx_dir): - run_darwin.append('--upx-dir=%s' % upx_dir) + run_darwin.append("--upx-dir=%s" % upx_dir) for x in exclude_modules: - run_darwin.append('--exclude-module=%s' % x ) + run_darwin.append("--exclude-module=%s" % x) for x in add_data: - run_darwin.append('--add-data=%s' % x.replace(';',':') ) - if windowed == 'y': - run_darwin.append('--windowed') + run_darwin.append("--add-data=%s" % x.replace(";", ":")) + if windowed == "y": + run_darwin.append("--windowed") PyInstaller.__main__.run(run_darwin) - -#Linux -elif sys.platform == 'linux': - run_linux =[ - 'pesterchum.py', - '--name=Pesterchum', + +# Linux +elif sys.platform == "linux": + run_linux = [ + "pesterchum.py", + "--name=Pesterchum", #'--windowed', # Hide console #'--noconfirm', # Overwrite output directory. - '--icon=trayicon32.icns', # Icon - '--clean', # Clear cache + "--icon=trayicon32.icns", # Icon + "--clean", # Clear cache ] - if upx_enabled == 'y': + if upx_enabled == "y": if os.path.isdir(upx_dir): - run_linux.append('--upx-dir=%s' % upx_dir) + run_linux.append("--upx-dir=%s" % upx_dir) for x in upx_exclude: - run_linux.append('--upx-exclude=%s' % x ) + run_linux.append("--upx-exclude=%s" % x) # Lower case variants are required. - run_linux.append('--upx-exclude=%s' % x.lower() ) - elif upx_enabled == 'n': - run_linux.append('--noupx') + run_linux.append("--upx-exclude=%s" % x.lower()) + elif upx_enabled == "n": + run_linux.append("--noupx") for x in exclude_modules: - run_linux.append('--exclude-module=%s' % x ) - if onefile == 'y': - run_linux.append('--onefile') - elif onefile == 'n': + run_linux.append("--exclude-module=%s" % x) + if onefile == "y": + run_linux.append("--onefile") + elif onefile == "n": for x in add_data: - run_linux.append('--add-data=%s' % x.replace(';',':') ) - if windowed == 'y': - run_linux.append('--windowed') - + run_linux.append("--add-data=%s" % x.replace(";", ":")) + if windowed == "y": + run_linux.append("--windowed") + print(run_linux) PyInstaller.__main__.run(run_linux) - - if onefile == 'y': + + if onefile == "y": # There's more proper ways to do this, but this doesn't require changing our paths for x in data_folders: print(x) - shutil.copytree(x, os.path.join('dist', data_folders[x]), - ignore=shutil.ignore_patterns('*.psd', - '*.xcf*', - 'ebg2.png', - 'ebg1.png')) + shutil.copytree( + x, + os.path.join("dist", data_folders[x]), + ignore=shutil.ignore_patterns( + "*.psd", "*.xcf*", "ebg2.png", "ebg1.png" + ), + ) for x in data_files_linux: print(x) - shutil.copy(x, os.path.join('dist', data_files_linux[x])) - - files = os.listdir('dist') + shutil.copy(x, os.path.join("dist", data_files_linux[x])) + + files = os.listdir("dist") try: - os.mkdir(os.path.join('dist', '.cache')) + os.mkdir(os.path.join("dist", ".cache")) except FileExistsError as e: print(e) for x in files: try: - shutil.move(os.path.join('dist',x), os.path.join('dist', '.cache', x)) + shutil.move(os.path.join("dist", x), os.path.join("dist", ".cache", x)) except FileExistsError as e: print(e) - shutil.move(os.path.join('dist', '.cache'), os.path.join('dist', 'Pesterchum')) - #shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'), + shutil.move(os.path.join("dist", ".cache"), os.path.join("dist", "Pesterchum")) + # shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'), # os.path.join('dist', 'Pesterchum', 'xref-Pesterchum.html')) - #shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'), + # shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'), # os.path.join('dist', 'Pesterchum', 'Analysis-00.toc')) - + else: print("Unknown platform.") - - run_generic =[ - 'pesterchum.py', - '--name=Pesterchum' - '--clean', # Clear cache + + run_generic = [ + "pesterchum.py", + "--name=Pesterchum" "--clean", # Clear cache ] - if upx_enabled == 'y': + if upx_enabled == "y": if os.path.isdir(upx_dir): - run_generic.append('--upx-dir=%s' % upx_dir) + run_generic.append("--upx-dir=%s" % upx_dir) else: - run_generic.append('--noupx') + run_generic.append("--noupx") for x in upx_exclude: - run_generic.append('--upx-exclude=%s' % x ) + run_generic.append("--upx-exclude=%s" % x) # Lower case variants are required. - run_generic.append('--upx-exclude=%s' % x.lower() ) + run_generic.append("--upx-exclude=%s" % x.lower()) for x in exclude_modules: - run_generic.append('--exclude-module=%s' % x ) + run_generic.append("--exclude-module=%s" % x) for x in add_data: - run_generic.append('--add-data=%s' % x.replace(';',':') ) - if windowed == 'y': - run_win32.append('--windowed') - + run_generic.append("--add-data=%s" % x.replace(";", ":")) + if windowed == "y": + run_win32.append("--windowed") + print(run_generic) - + PyInstaller.__main__.run(run_generic) diff --git a/pyquirks.py b/pyquirks.py index 2dd272a..487e121 100644 --- a/pyquirks.py +++ b/pyquirks.py @@ -10,44 +10,45 @@ except ImportError: import ostools from quirks import ScriptQuirks -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + class PythonQuirks(ScriptQuirks): def loadModule(self, name, filename): # imp is deprecated since Python 3.4 - #return imp.load_source(name, filename) - + # return imp.load_source(name, filename) + spec = importlib.util.spec_from_file_location(name, filename) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module - + def getExtension(self): - return '.py' + return ".py" def modHas(self, module, attr): - if attr == 'commands': + if attr == "commands": variables = vars(module) for name, obj in variables.items(): - if self.modHas(obj, 'command'): + if self.modHas(obj, "command"): return True return hasattr(module, attr) def register(self, module): variables = vars(module) for name, obj in variables.items(): - if self.modHas(obj, 'command'): + if self.modHas(obj, "command"): try: if not isinstance(obj("test"), str): raise Exception except: - #print("Quirk malformed: %s" % (obj.command)) + # print("Quirk malformed: %s" % (obj.command)) PchumLog.error("Quirk malformed: %s" % (obj.command)) # Since this is executed before QApplication is constructed, # This prevented pesterchum from starting entirely when a quirk was malformed :/ # (QWidget: Must construct a QApplication before a QWidget) - + if QtWidgets.QApplication.instance() != None: msgbox = QtWidgets.QMessageBox() msgbox.setWindowTitle("Error!") @@ -55,4 +56,3 @@ class PythonQuirks(ScriptQuirks): msgbox.exec() else: self.quirks[obj.command] = obj - diff --git a/pytwmn.py b/pytwmn.py index 6974e59..74b4efc 100755 --- a/pytwmn.py +++ b/pytwmn.py @@ -1,12 +1,15 @@ import os import socket + class TwmnError(Exception): UNWN_ERR = -1 NO_TWMND = -2 - NO_CONF = -3 + NO_CONF = -3 + def __init__(self, code): self.code = code + def __str__(self): if self.code == TwmnError.NO_TWMND: return "Unable to connect to twmnd" @@ -19,10 +22,12 @@ class TwmnError(Exception): def confExists(): try: from xdg import BaseDirectory - return os.path.join(BaseDirectory.xdg_config_home,"twmn/twmn.conf") + + return os.path.join(BaseDirectory.xdg_config_home, "twmn/twmn.conf") except: return False + def init(host="127.0.0.1", port=None): if not port: port = 9797 @@ -32,8 +37,7 @@ def init(host="127.0.0.1", port=None): return False with open(fn) as f: for line in f.readlines(): - if line.startswith("port=") and \ - line[5:-1].isdigit(): + if line.startswith("port=") and line[5:-1].isdigit(): port = int(line[5:-1]) break except IOError: @@ -44,6 +48,7 @@ def init(host="127.0.0.1", port=None): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect((host, port)) + class Notification(object): def __init__(self, title="", msg="", icon=""): self.title = str(title) @@ -59,17 +64,22 @@ class Notification(object): def show(self): try: if self.time is None: - s.send("" + self.title + "" + s.send( + "" + self.title + "" "" + self.msg + "" - "" + self.icon + "") + "" + self.icon + "" + ) else: - s.send("" + self.title + "" + s.send( + "" + self.title + "" "" + self.msg + "" "" + self.icon + "" - "" + str(self.time) + "") + "" + str(self.time) + "" + ) except: raise TwmnError(TwmnError.NO_TWMND) + if __name__ == "__main__": init() n = Notification("PyTwmn", "This is a notification!") diff --git a/quirks.py b/quirks.py index 52f04ab..fa49a53 100644 --- a/quirks.py +++ b/quirks.py @@ -4,7 +4,8 @@ import logging import ostools _datadir = ostools.getDataDir() -PchumLog = logging.getLogger('pchumLogger') +PchumLog = logging.getLogger("pchumLogger") + class ScriptQuirks(object): def __init__(self): @@ -13,7 +14,7 @@ class ScriptQuirks(object): self.quirks = {} self.last = {} self.scripts = [] - #self.load() + # self.load() def loadModule(self, name, filename): raise Exception @@ -27,17 +28,17 @@ class ScriptQuirks(object): for script in self.scripts: PchumLog.info(script.getExtension()) script.load() - #print script.quirks + # print script.quirks for q in script.quirks: self.quirks.update(script.quirks) for k in self.last: if k in self.quirks: if self.last[k] == self.quirks[k]: del self.quirks[k] - #print self.quirks - if hasattr(self, 'quirks'): + # print self.quirks + if hasattr(self, "quirks"): # See https://stackoverflow.com/questions/12843099/python-logging-typeerror-not-all-arguments-converted-during-string-formatting - reg_quirks = ('Registered quirks:', '(), '.join(self.quirks) + "()") + reg_quirks = ("Registered quirks:", "(), ".join(self.quirks) + "()") PchumLog.info(reg_quirks) else: PchumLog.warning("Couldn't find any script quirks") @@ -50,18 +51,17 @@ class ScriptQuirks(object): self.quirks.clear() extension = self.getExtension() filenames = [] - if not os.path.exists(os.path.join(self.home, 'quirks')): - os.makedirs(os.path.join(self.home, 'quirks'), exist_ok=True) - for fn in os.listdir(os.path.join(self.home, 'quirks')): - if fn.endswith(extension) and not fn.startswith('_'): - filenames.append(os.path.join(self.home, 'quirks', fn)) - if hasattr(self, '_datadir'): - if not os.path.exists(os.path.join(self._datadir, 'quirks')): - os.makedirs(os.path.join(self._datadir, 'quirks'), exist_ok=True) - for fn in os.listdir(os.path.join(self._datadir, 'quirks')): - if fn.endswith(extension) and not fn.startswith('_'): - filenames.append(os.path.join(self._datadir, 'quirks', fn)) - + if not os.path.exists(os.path.join(self.home, "quirks")): + os.makedirs(os.path.join(self.home, "quirks"), exist_ok=True) + for fn in os.listdir(os.path.join(self.home, "quirks")): + if fn.endswith(extension) and not fn.startswith("_"): + filenames.append(os.path.join(self.home, "quirks", fn)) + if hasattr(self, "_datadir"): + if not os.path.exists(os.path.join(self._datadir, "quirks")): + os.makedirs(os.path.join(self._datadir, "quirks"), exist_ok=True) + for fn in os.listdir(os.path.join(self._datadir, "quirks")): + if fn.endswith(extension) and not fn.startswith("_"): + filenames.append(os.path.join(self._datadir, "quirks", fn)) modules = [] for filename in filenames: @@ -72,11 +72,13 @@ class ScriptQuirks(object): if module is None: continue except Exception as e: - PchumLog.warning("Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e)) + PchumLog.warning( + "Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e) + ) else: - if self.modHas(module, 'setup'): + if self.modHas(module, "setup"): module.setup() - if self.modHas(module, 'commands'): + if self.modHas(module, "commands"): self.register(module) modules.append(name) for k in self.last: @@ -85,10 +87,10 @@ class ScriptQuirks(object): del self.quirks[k] def funcre(self): - if not hasattr(self, 'quirks'): + if not hasattr(self, "quirks"): return r"\\[0-9]+" f = r"(" for q in self.quirks: - f = f + q+r"\(|" + f = f + q + r"\(|" f = f + r"\)|\\[0-9]+)" return f diff --git a/quirks/defaults.py b/quirks/defaults.py index 7508adc..7c74433 100644 --- a/quirks/defaults.py +++ b/quirks/defaults.py @@ -1,17 +1,29 @@ import random + def upperrep(text): return text.upper() + + upperrep.command = "upper" + def lowerrep(text): return text.lower() + + lowerrep.command = "lower" + def scramblerep(text): return "".join(random.sample(text, len(text))) + + scramblerep.command = "scramble" + def reverserep(text): return text[::-1] + + reverserep.command = "reverse" diff --git a/quirks/gradient.py b/quirks/gradient.py index d585ac9..b0c4c81 100644 --- a/quirks/gradient.py +++ b/quirks/gradient.py @@ -1,5 +1,6 @@ import re + def rainbow(text): """Example implementation of a gradient function, distributes colors over text, accounting for links, @@ -9,66 +10,62 @@ def rainbow(text): Regexp Replace Regexp: ^(.*)$ Replace With: rainbow(\1) - + To customize it: 1. Copy this function. 2. Replace the hex colors in 'gradient' below with your own colors. 3. Replace 'rainbow' above and below with something more fitting :3 - + There's lots of implementations of this that predate mine, see: https://paste.0xfc.de/?e60df5a155e93583#AmcgN9cRnCcBycmVMvw6KJ1YLKPXGbaSzZLbgAhoNCQD ^ There's more useful info here too :3c """ # Values of 'gradient' can be any amount of hex/RGB colors. - gradient = ["#ff0000", - "#ff8000", - "#ffff00", - "#80ff00", - "#00ff00", - "#00ff80", - "#00ffff", - "#0080ff", - "#0000ff", - "#8000ff", - "#ff00ff", - "#ff0080"] - + gradient = [ + "#ff0000", + "#ff8000", + "#ffff00", + "#80ff00", + "#00ff00", + "#00ff80", + "#00ffff", + "#0080ff", + "#0000ff", + "#8000ff", + "#ff00ff", + "#ff0080", + ] + # Set base distribution of colors over text, # stored as list of lists. color_and_position = [] for color in range(0, len(gradient)): ratio = len(text) / len(gradient) # To account for text length. - color_and_position.append( - [gradient[color], - round(color * ratio)]) + color_and_position.append([gradient[color], round(color * ratio)]) # Iterate through match object representing all links/smilies in text, # if a color tag is going to be placed within it, # move its position to after the link. for match in re.finditer(_urlre, text): for cp in color_and_position: - if ((cp[1] >= match.start()) # cp[1] is pos - and (cp[1] <= match.end())): + if (cp[1] >= match.start()) and (cp[1] <= match.end()): # cp[1] is pos cp[1] = match.end() + 1 # Move to 1 character after link. for match in re.finditer(_smilere, text): for cp in color_and_position: - if ((cp[1] >= match.start()) - and (cp[1] <= match.end())): + if (cp[1] >= match.start()) and (cp[1] <= match.end()): cp[1] = match.end() + 1 for match in re.finditer(_memore, text): for cp in color_and_position: - if ((cp[1] >= match.start()) - and (cp[1] <= match.end())): + if (cp[1] >= match.start()) and (cp[1] <= match.end()): cp[1] = match.end() + 1 for match in re.finditer(_handlere, text): for cp in color_and_position: - if ((cp[1] >= match.start()) - and (cp[1] <= match.end())): + if (cp[1] >= match.start()) and (cp[1] <= match.end()): cp[1] = match.end() + 1 - + # Iterate through characters in text and write them to the output, # if a color tag should be placed, add it before the character. - output = '' + output = "" for char in range(0, len(text)): # Add color if at position. for cp in color_and_position: @@ -82,6 +79,8 @@ def rainbow(text): # Add character. output += text[char] return output + + rainbow.command = "rainbow" # These can't always be imported from their original functions, @@ -152,10 +151,10 @@ smiledict = { ":scorpio:": "scorpio.gif", ":shades:": "shades.png", ":honk:": "honk.png", - } +} # Regular expression templates for detecting links/smilies. _smilere = re.compile("|".join(list(smiledict.keys()))) _urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+") -#_url2re = re.compile(r"(?i)(? bool: return sys.maxsize > 2**32 + path = "" base = None if sys.platform == "win32": @@ -19,120 +21,131 @@ if sys.platform == "win32": path = sys.path if is_64bit() == True: - path.append(r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x64") + path.append( + r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x64" + ) elif is_64bit() == False: - path.append(r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x86") - + path.append( + r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x86" + ) + print("Path = " + str(path)) - -includefiles = ["quirks", - "smilies", - "themes", - "docs", - "README.md", - "LICENSE", - "CHANGELOG.md", - "PCskins.png", - "Pesterchum.png"] + +includefiles = [ + "quirks", + "smilies", + "themes", + "docs", + "README.md", + "LICENSE", + "CHANGELOG.md", + "PCskins.png", + "Pesterchum.png", +] build_exe_options = { -# "includes": ['PyQt6.QtCore', -# 'PyQt6.QtGui', -# 'PyQt6.QtWidgets'], - "excludes": ['collections.sys', - 'collections._sre', - 'collections._json', - 'collections._locale', - 'collections._struct', - 'collections.array', - 'collections._weakref', - 'PyQt6.QtMultimedia', - 'PyQt6.QtDBus', - 'PyQt6.QtDeclarative', - 'PyQt6.QtHelp', - 'PyQt6.QtNetwork', - 'PyQt6.QtSql', - 'PyQt6.QtSvg', - 'PyQt6.QtTest', - 'PyQt6.QtWebKit', - 'PyQt6.QtXml', - 'PyQt6.QtXmlPatterns', - 'PyQt6.phonon', - 'PyQt6.QtAssistant', - 'PyQt6.QtDesigner', - 'PyQt6.QAxContainer', - 'pygame.docs' # Hopefully we can just not have pygame at all at some point =3 - # (when QtMultimedia stops relying on local codecs maxwidth) or \ - len(text[:lastspace]) < 1: + if (metric.horizontalAdvance(text[:lastspace]) > maxwidth) or len( + text[:lastspace] + ) < 1: for i in range(len(text)): if metric.horizontalAdvance(text[:i]) > maxwidth: - lastspace = i-1 + lastspace = i - 1 break ret.append(text[:lastspace]) - text = text[lastspace+1:] + text = text[lastspace + 1 :] ret.append(text) return "\n".join(ret) class PesterToastMachine(ToastMachine, QtCore.QObject): - def __init__(self, parent, name, on=True, type="default", - types=({'default' : DefaultToast, - 'libnotify' : pynotify.Notification} - if pynotify else - {'default' : DefaultToast}), - extras={}): + def __init__( + self, + parent, + name, + on=True, + type="default", + types=( + {"default": DefaultToast, "libnotify": pynotify.Notification} + if pynotify + else {"default": DefaultToast} + ), + extras={}, + ): ToastMachine.__init__(self, parent, name, on, type, types, extras) QtCore.QObject.__init__(self, parent) @@ -396,7 +455,7 @@ class PesterToastMachine(ToastMachine, QtCore.QObject): oldon = self.on ToastMachine.setEnabled(self, on) if oldon != self.on: - self.parent.config.set('notify', self.on) + self.parent.config.set("notify", self.on) if self.on: self.timer.start() else: @@ -406,7 +465,7 @@ class PesterToastMachine(ToastMachine, QtCore.QObject): oldtype = self.type ToastMachine.setCurrentType(self, type) if oldtype != self.type: - self.parent.config.set('notifyType', self.type) + self.parent.config.set("notifyType", self.type) @QtCore.pyqtSlot() def showNext(self): @@ -414,9 +473,9 @@ class PesterToastMachine(ToastMachine, QtCore.QObject): def run(self): pass - #~ self.timer = QtCore.QTimer(self) - #~ self.timer.setInterval(1000) - #~ self.connect(self.timer, QtCore.SIGNAL('timeout()'), - #~ self, QtCore.SLOT('showNext()')) - #~ if self.on: - #~ self.timer.start() + # ~ self.timer = QtCore.QTimer(self) + # ~ self.timer.setInterval(1000) + # ~ self.connect(self.timer, QtCore.SIGNAL('timeout()'), + # ~ self, QtCore.SLOT('showNext()')) + # ~ if self.on: + # ~ self.timer.start()