Reformat codebase (#97)

* Reformat codebase with black

* Create black.yml and add black style badge to README.md
This commit is contained in:
MiguelX413 2022-10-07 13:51:40 -07:00 committed by GitHub
parent c8a8097673
commit a51e4dd69e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 6139 additions and 3682 deletions

10
.github/workflows/black.yml vendored Normal file
View file

@ -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

View file

@ -8,6 +8,7 @@
<br>
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/y/Dpeta/pesterchum-alt-servers?style=for-the-badge">
<img alt="Lines of code" src="https://img.shields.io/tokei/lines/github/Dpeta/pesterchum-alt-servers?style=for-the-badge">
<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000?style=for-the-badge"></a>
</h1>
<img alt="PESTERCHUM" align="right" src="Pesterchum.png">

View file

@ -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', "<string>", "single")
code = compile(scriptstr + "\n", "<string>", "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 = "<span style=\"color:#000000\">{} {} {!r}</span>"
# ~result = "<span style=\"color:#000000\">{} {} {!r}</span>"
# 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"

459
convo.py
View file

@ -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.
@ -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,11 +209,13 @@ 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()))
@ -206,25 +223,28 @@ class PesterTabWindow(QtWidgets.QFrame):
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.document().addResource(
QtGui.QTextDocument.ResourceType.ImageResource.value,
text.urls[movie],
movie.currentPixmap())
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.
@ -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 }"
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/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"]))
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("<c=%s>" % (color), color),
"%s: " % (initials)]
lexmsg[0:0] = [colorBegin("<c=%s>" % (color), color), "%s: " % (initials)]
lexmsg.append(colorEnd("</c>"))
self.append("<span style=\"color:#000000\">"
+ time
+ convertTags(lexmsg)
+ "</span>")
#self.append('<img src="/Users/lexi/pesterchum-lex/smilies/tab.gif" />'
self.append(
'<span style="color:#000000">' + time + convertTags(lexmsg) + "</span>"
)
# self.append('<img src="/Users/lexi/pesterchum-lex/smilies/tab.gif" />'
# + '<img src="/Users/lexi/pesterchum/smilies/tab.gif" />'
# + '<img src="/Applications/Pesterchum.app/Contents/Resources/smilies/tab.gif" />'
# + '<img src="smilies/tab.gif" />');
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,17 +631,20 @@ 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(' ', ''):
if "color:" not in theme[self.stylesheet_path].replace(" ", ""):
StyleSheet = "color: black; " + StyleSheet
self.setStyleSheet(StyleSheet)
@ -585,6 +652,7 @@ class PesterInput(QtWidgets.QLineEdit):
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.
@ -831,6 +949,7 @@ 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.
@ -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,9 +982,11 @@ 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:
@ -875,55 +997,86 @@ class PesterConvo(QtWidgets.QFrame):
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,
aligndict = {
"h": {
"center": QtCore.Qt.AlignmentFlag.AlignHCenter,
"left": QtCore.Qt.AlignmentFlag.AlignLeft,
"right": QtCore.Qt.AlignmentFlag.AlignRight },
"v": {"center": QtCore.Qt.AlignmentFlag.AlignVCenter,
"right": QtCore.Qt.AlignmentFlag.AlignRight,
},
"v": {
"center": QtCore.Qt.AlignmentFlag.AlignVCenter,
"top": QtCore.Qt.AlignmentFlag.AlignTop,
"bottom": QtCore.Qt.AlignmentFlag.AlignBottom } }
"bottom": QtCore.Qt.AlignmentFlag.AlignBottom,
},
}
# the import is way down here to avoid recursive imports
from logviewer import PesterLogViewer

View file

@ -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,
from parsetools import (
timeDifference,
convertTags,
lexMessage,
parseRegexpFunctions,
smiledict)
smiledict,
)
from mispeller import mispeller
_urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+")
#_url2re = re.compile(r"(?i)(?<!//)\bwww\.[^\s]+?\.")
# _url2re = re.compile(r"(?i)(?<!//)\bwww\.[^\s]+?\.")
_groupre = re.compile(r"\\([0-9]+)")
_upperre = re.compile(r"upper\(([\w<>\\]+)\)")
_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:
@ -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("(</?c=?.*?>)", re.I)
@ -108,27 +114,37 @@ 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):
@ -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,8 +186,8 @@ 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()
@ -180,7 +196,7 @@ class pesterQuirks(object):
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,
return (
self.handle,
{
"handle": self.handle,
"mood": self.mood.name(),
"color": str(self.color.name()),
"group": str(self.group),
"notes": str(self.notes)})
"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 "<c=%s>-- %s%s <c=%s>[%s]</c> %s --</c>" % (syscolor.name(), handle, suffix, self.colorhtml(), initials, msg)
initials = self.initials() + uppersuffix
return "<c=%s>-- %s%s <c=%s>[%s]</c> %s --</c>" % (
syscolor.name(),
handle,
suffix,
self.colorhtml(),
initials,
msg,
)
def pestermsg(self, otherchum, syscolor, verb):
return "<c=%s>-- %s <c=%s>[%s]</c> %s %s <c=%s>[%s]</c> at %s --</c>" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), verb, otherchum.handle, otherchum.colorhtml(), otherchum.initials(), datetime.now().strftime("%H:%M"))
return "<c=%s>-- %s <c=%s>[%s]</c> %s %s <c=%s>[%s]</c> at %s --</c>" % (
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 "<c=%s>-- %s <c=%s>[%s]</c> changed their mood to %s <img src='%s' /> --</c>" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), mood.name().upper(), theme["main/chums/moods"][mood.name()]["icon"])
return (
"<c=%s>-- %s <c=%s>[%s]</c> changed their mood to %s <img src='%s' /> --</c>"
% (
syscolor.name(),
self.handle,
self.colorhtml(),
self.initials(),
mood.name().upper(),
theme["main/chums/moods"][mood.name()]["icon"],
)
)
def idlemsg(self, syscolor, verb):
return "<c=%s>-- %s <c=%s>[%s]</c> %s --</c>" % (syscolor.name(), self.handle, self.colorhtml(), self.initials(), verb)
return "<c=%s>-- %s <c=%s>[%s]</c> %s --</c>" % (
syscolor.name(),
self.handle,
self.colorhtml(),
self.initials(),
verb,
)
def memoclosemsg(self, syscolor, initials, verb):
if type(initials) == type(list()):
return "<c=%s><c=%s>%s</c> %s.</c>" % (syscolor.name(), self.colorhtml(), ", ".join(initials), verb)
return "<c=%s><c=%s>%s</c> %s.</c>" % (
syscolor.name(),
self.colorhtml(),
", ".join(initials),
verb,
)
else:
return "<c=%s><c=%s>%s%s%s</c> %s.</c>" % (syscolor.name(), self.colorhtml(), initials.pcf, self.initials(), initials.number, verb)
return "<c=%s><c=%s>%s%s%s</c> %s.</c>" % (
syscolor.name(),
self.colorhtml(),
initials.pcf,
self.initials(),
initials.number,
verb,
)
def memonetsplitmsg(self, syscolor, initials):
if len(initials) <= 0:
return "<c=%s>Netsplit quits: <c=black>None</c></c>" % (syscolor.name())
else:
return "<c=%s>Netsplit quits: <c=black>%s</c></c>" % (syscolor.name(), ", ".join(initials))
return "<c=%s>Netsplit quits: <c=black>%s</c></c>" % (
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 "<c=%s><c=%s>%s</c> %s %s %s.</c>" % \
(syscolor.name(), self.colorhtml(), initials, timetext, verb, channel[1:].upper().replace("_", " "))
return "<c=%s><c=%s>%s</c> %s %s %s.</c>" % (
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 "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo." % \
(opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials))
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo." % (
opchum.colorhtml(),
opinit,
self.colorhtml(),
", ".join(initials),
)
else:
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>." % \
(opchum.colorhtml(), opinit, self.colorhtml(), ", ".join(initials), str(reason))
return (
"<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>."
% (
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 "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo." % \
(opchum.colorhtml(), opinit, self.colorhtml(), initials)
return (
"<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo."
% (opchum.colorhtml(), opinit, self.colorhtml(), initials)
)
else:
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>." % \
(opchum.colorhtml(), opinit, self.colorhtml(), initials, str(reason))
return (
"<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>."
% (
opchum.colorhtml(),
opinit,
self.colorhtml(),
initials,
str(reason),
)
)
except:
PchumLog.exception('')
PchumLog.exception("")
initials = self.initials()
if opchum.handle == reason:
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo." % \
(opchum.colorhtml(), opinit, self.colorhtml(), initials)
return (
"<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo."
% (opchum.colorhtml(), opinit, self.colorhtml(), initials)
)
else:
return "<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>." % \
(opchum.colorhtml(), opinit, self.colorhtml(), initials, str(reason))
return (
"<c=%s>%s</c> banned <c=%s>%s</c> from responding to memo: <c=black>[%s]</c>."
% (
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 "<c=%s>%s</c> permabanned <c=%s>%s</c> from the memo." % \
(opchum.colorhtml(), opinit, self.colorhtml(), initials)
initials = timeGrammar.pcf + self.initials() + timeGrammar.number
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
return "<c=%s>%s</c> permabanned <c=%s>%s</c> 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 "<c=%s><c=%s>%s %s [%s]</c> %s %s.</c>" % \
(syscolor.name(), self.colorhtml(), timeGrammar.temporal, self.handle,
initials, timetext, verb)
initials = timeGrammar.pcf + self.initials() + timeGrammar.number
return "<c=%s><c=%s>%s %s [%s]</c> %s %s.</c>" % (
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 "<c=%s>%s</c> made <c=%s>%s</c> an OP." % \
(opchum.colorhtml(), opinit, self.colorhtml(), self.initials())
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
return "<c=%s>%s</c> made <c=%s>%s</c> an OP." % (
opchum.colorhtml(),
opinit,
self.colorhtml(),
self.initials(),
)
def memodeopmsg(self, opchum, opgrammar, syscolor):
opinit = opgrammar.pcf+opchum.initials()+opgrammar.number
return "<c=%s>%s</c> took away <c=%s>%s</c>'s OP powers." % \
(opchum.colorhtml(), opinit, self.colorhtml(), self.initials())
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
return "<c=%s>%s</c> took away <c=%s>%s</c>'s OP powers." % (
opchum.colorhtml(),
opinit,
self.colorhtml(),
self.initials(),
)
def memovoicemsg(self, opchum, opgrammar, syscolor):
opinit = opgrammar.pcf+opchum.initials()+opgrammar.number
return "<c=%s>%s</c> gave <c=%s>%s</c> voice." % \
(opchum.colorhtml(), opinit, self.colorhtml(), self.initials())
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
return "<c=%s>%s</c> gave <c=%s>%s</c> voice." % (
opchum.colorhtml(),
opinit,
self.colorhtml(),
self.initials(),
)
def memodevoicemsg(self, opchum, opgrammar, syscolor):
opinit = opgrammar.pcf+opchum.initials()+opgrammar.number
return "<c=%s>%s</c> took away <c=%s>%s</c>'s voice." % \
(opchum.colorhtml(), opinit, self.colorhtml(), self.initials())
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
return "<c=%s>%s</c> took away <c=%s>%s</c>'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 "<c=%s>Memo is %s <c=black>%s</c> by <c=%s>%s</c></c>" % \
(syscolor.name(), modeon, modeverb, opchum.colorhtml(), opinit)
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
if modeon:
modeon = "now"
else:
modeon = "no longer"
return "<c=%s>Memo is %s <c=black>%s</c> by <c=%s>%s</c></c>" % (
syscolor.name(),
modeon,
modeverb,
opchum.colorhtml(),
opinit,
)
def memoquirkkillmsg(self, opchum, opgrammar, syscolor):
opinit = opgrammar.pcf+opchum.initials()+opgrammar.number
return "<c=%s><c=%s>%s</c> turned off your quirk.</c>" % \
(syscolor.name(), opchum.colorhtml(), opinit)
opinit = opgrammar.pcf + opchum.initials() + opgrammar.number
return "<c=%s><c=%s>%s</c> turned off your quirk.</c>" % (
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()

View file

@ -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 </3
# fuckin Qt <--- I feel that </3
if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse:
listing = self.itemAt(event.pos())
self.setCurrentItem(listing)
optionsMenu = self.getOptionsMenu()
if optionsMenu:
optionsMenu.popup(event.globalPos())
def getOptionsMenu(self):
return self.optionsMenu
class RightClickTree(QtWidgets.QTreeWidget):
def contextMenuEvent(self, event):
if event.reason() == QtGui.QContextMenuEvent.Reason.Mouse:
@ -65,9 +79,11 @@ class RightClickTree(QtWidgets.QTreeWidget):
optionsMenu = self.getOptionsMenu()
if optionsMenu:
optionsMenu.popup(event.globalPos())
def getOptionsMenu(self):
return self.optionsMenu
class MultiTextDialog(QtWidgets.QDialog):
def __init__(self, title, parent, *queries):
super(MultiTextDialog, self).__init__(parent)
@ -98,6 +114,7 @@ class MultiTextDialog(QtWidgets.QDialog):
layout_0.addLayout(layout_ok)
self.setLayout(layout_0)
def getText(self):
r = self.exec()
if r == QtWidgets.QDialog.DialogCode.Accepted:
@ -108,6 +125,7 @@ class MultiTextDialog(QtWidgets.QDialog):
else:
return None
class MovingWindow(QtWidgets.QFrame):
# Qt supports starting a system-specific move operation since 5.15, so we shouldn't need to manually set position like this anymore.
# https://doc.qt.io/qt-5/qwindow.html#startSystemMove
@ -116,6 +134,7 @@ class MovingWindow(QtWidgets.QFrame):
super(MovingWindow, self).__init__(*x, **y)
self.moving = None
self.moveupdate = 0
def mouseMoveEvent(self, event):
if self.moving:
move = event.globalPos() - self.moving
@ -124,6 +143,7 @@ class MovingWindow(QtWidgets.QFrame):
if self.moveupdate > 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.
@ -136,17 +156,26 @@ class MovingWindow(QtWidgets.QFrame):
print(str(e))
if event.button() == 1:
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):

455
irc.py
View file

@ -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,
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)
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] == "</c>":
space = space+4
a = l[0][0:space+1]
b = l[0][space+1:]
elif l[0][space + 1 : space + 5] == "</c>":
space = space + 4
a = l[0][0 : space + 1]
b = l[0][space + 1 :]
if a.count("<c") > a.count("</c>"):
# oh god ctags will break!! D=
hanging = []
@ -161,17 +170,18 @@ class PesterIRC(QtCore.QThread):
while c != -1:
d = a.find("</c>", c)
while d in usedends:
d = a.find("</c>", d+1)
if d != -1: usedends.append(d)
d = a.find("</c>", 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("<c",0,c)
c = a.rfind("<c", 0, c)
# end all ctags in first part
for i in range(a.count("<c")-a.count("</c>")):
for i in range(a.count("<c") - a.count("</c>")):
a = a + "</c>"
#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,9 +352,10 @@ 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])
@ -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 <Target> <Key> <Visibility> <Value>
@ -457,31 +498,33 @@ class PesterHandler(DefaultCommandHandler):
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))
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
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()
@ -672,45 +731,47 @@ 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:
@ -723,8 +784,9 @@ class PesterHandler(DefaultCommandHandler):
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,17 +810,45 @@ 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)
@ -768,9 +858,13 @@ class PesterHandler(DefaultCommandHandler):
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))
@ -780,26 +874,31 @@ class PesterHandler(DefaultCommandHandler):
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))
@ -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"))
@ -823,15 +923,17 @@ class PesterHandler(DefaultCommandHandler):
self.parent.mainwindow.randhandler.setRunning(False)
elif newnick == self.parent.mainwindow.randhandler.randNick:
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)
@ -854,11 +956,15 @@ 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
@ -870,34 +976,42 @@ class PesterHandler(DefaultCommandHandler):
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))
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)
@ -907,8 +1021,8 @@ class PesterHandler(DefaultCommandHandler):
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:
@ -944,8 +1060,7 @@ class PesterHandler(DefaultCommandHandler):
PchumLog.warning(e)
self.parent.setConnectionBroken()
#def isOn(self, *chums):
# def isOn(self, *chums):
# isonNicks = ""
# for c in chums:
# chandle = c.handle

View file

@ -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]","</c>").replace("[url]","").replace("[/url]","")
cline = (
line.replace("\r\n", "")
.replace("[/color]", "</c>")
.replace("[url]", "")
.replace("[/url]", "")
)
cline = re.sub("\[color=(#.{6})]", r"<c=\1>", 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))

834
memos.py

File diff suppressed because it is too large Load diff

603
menus.py

File diff suppressed because it is too large Load diff

View file

@ -1,29 +1,55 @@
import random
kbloc = [[x for x in "1234567890-="],
kbloc = [
[x for x in "1234567890-="],
[x for x in "qwertyuiop[]"],
[x for x in "asdfghjkl:;'"],
[x for x in "zxcvbnm,.>/?"]]
[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

74
mood.py
View file

@ -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)

View file

@ -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] == "":

View file

@ -8,30 +8,37 @@ 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
@ -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 ""

View file

@ -16,7 +16,8 @@
# THE SOFTWARE.
import logging
PchumLog = logging.getLogger('pchumLogger')
PchumLog = logging.getLogger("pchumLogger")
import logging
import socket
@ -29,17 +30,18 @@ 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
"""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
@ -93,7 +95,7 @@ class IRCClient:
self._end = False
def send(self, *args, **kwargs):
""" send a message to the connected server. all arguments are joined
"""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)
@ -109,19 +111,24 @@ class IRCClient:
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,10 +166,7 @@ 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
@ -168,23 +174,25 @@ class IRCClient:
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()
@ -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()
@ -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,7 +240,7 @@ 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)
@ -241,7 +249,7 @@ class IRCClient:
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,15 +288,14 @@ 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:
@ -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:
@ -348,26 +357,32 @@ class IRCClient:
else:
PchumLog.debug("ending while, end is %s" % self._end)
if self.socket:
PchumLog.info('finished: closing 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
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:
@ -417,7 +432,7 @@ class IRCApp:
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
@ -430,7 +445,7 @@ class IRCApp:
found_one_alive = True
if not found_one_alive:
PchumLog.info('nothing left alive... quiting')
PchumLog.info("nothing left alive... quiting")
self.stop()
now = time.time()
@ -438,7 +453,7 @@ class IRCApp:
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:
self._timers.append((target_time, cb))
@ -446,9 +461,5 @@ class IRCApp:
time.sleep(self.sleep_time)
def stop(self):
""" stop the application """
"""stop the application"""
self.running = False

View file

@ -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,39 +118,45 @@ 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))
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'))]
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)
@ -163,7 +175,7 @@ class DefaultBotCommandHandler(CommandHandler):
class BotCommandHandler(DefaultCommandHandler):
""" complete command handler for bots """
"""complete command handler for bots"""
def __init__(self, client, command_handler):
DefaultCommandHandler.__init__(self, client)
@ -174,7 +186,7 @@ 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.
"""
@ -183,13 +195,13 @@ class BotCommandHandler(DefaultCommandHandler):
if dest == self.client.nick:
dest = parse_nick(prefix)[0]
elif msg.startswith(self.client.nick):
msg = msg[len(self.client.nick)+1:]
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

View file

@ -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=="":
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()

View file

@ -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())

View file

@ -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):
"""
@ -60,15 +61,15 @@ def parse_raw_irc_command(element):
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)
<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)

View file

@ -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

View file

@ -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)<c=(.*?)>')
_gtag_begin = re.compile(r'(?i)<g[a-f]>')
_ctag_end = re.compile(r'(?i)</c>')
_ctag_rgb = re.compile(r'\d+,\d+,\d+')
_ctag_begin = re.compile(r"(?i)<c=(.*?)>")
_gtag_begin = re.compile(r"(?i)<g[a-f]>")
_ctag_end = re.compile(r"(?i)</c>")
_ctag_rgb = re.compile(r"\d+,\d+,\d+")
_urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+")
#_url2re = re.compile(r"(?i)(?<!//)\bwww\.[^\s]+?\.")
# _url2re = re.compile(r"(?i)(?<!//)\bwww\.[^\s]+?\.")
_memore = re.compile(r"(\s|^)(#[A-Za-z0-9_]+)")
_handlere = re.compile(r"(\s|^)(@[A-Za-z0-9_]+)")
_imgre = re.compile(r"""(?i)<img src=['"](\S+)['"]\s*/>""")
_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)</([ibu])>')
_format_begin = re.compile(r"(?i)<([ibu])>")
_format_end = re.compile(r"(?i)</([ibu])>")
_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 "<c=%s>" % (color)
try:
qc = QtGui.QColor(*[int(c) for c in color.split(",")])
@ -105,14 +112,16 @@ class colorBegin(lexercon.Chunk):
if format == "html":
return '<span style="color:%s">' % (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 '<c=%s,%s,%s>' % (r,g,b)
(r, g, b, a) = qc.getRgb()
return "<c=%s,%s,%s>" % (r, g, b)
class colorEnd(lexercon.Chunk):
def __init__(self, string):
self.string = string
def convert(self, format):
if format == "html":
return "</span>"
@ -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 "</%s>" % (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 "<a href='%s'>%s</a>" % (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,73 +202,92 @@ 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<a href='%s'>%s</a>" % (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<a href='%s'>%s</a>" % (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 "<img src='smilies/%s' alt='%s' title='%s' />" % (smiledict[self.string], self.string, self.string)
return "<img src='smilies/%s' alt='%s' title='%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 "<img src='smilies/honk.png' alt'honk' title='honk' />"
#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),
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.
@ -260,7 +298,8 @@ def lexMessage(string):
(memolex, _memore),
(chumhandlelex, _handlere),
(smiley, _smilere),
(honker, _honk)]
(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("</c>"))
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("&", "&amp;").replace(">", "&gt;").replace("<","&lt;")
escaped += (
o.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
)
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.
@ -351,6 +394,7 @@ 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.
@ -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,10 +502,10 @@ 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.
@ -509,16 +563,20 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
cte = lexercon.CTagEnd("</c>", 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
@ -707,7 +763,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
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,7 +806,9 @@ 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)
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
@ -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.
@ -807,8 +861,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
clientMsg, colorcmd, grammar.pcf, initials, grammar.number
)
# Not sure if this needs a space at the end...?
serverMsg = "<c={1}>{2}: {0}</c>".format(
serverMsg, colorcmd, initials)
serverMsg = "<c={1}>{2}: {0}</c>".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,12 +896,13 @@ 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 :(
return "??:?? FROM ????"
@ -850,17 +911,17 @@ def timeDifference(td):
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'<img src="smilies/(\S+)" />', 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:

File diff suppressed because it is too large Load diff

View file

@ -9,24 +9,26 @@ class AttrDict(dict):
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,7 +114,7 @@ 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):
@ -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:

View file

@ -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 = "</c>"
@ -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 "</c>"
elif format == "plaintext": return ''
text = ""
if format == "irc":
return "\x03"
elif format == "pchum":
return "</c>"
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 "</c>"
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"<c=(.*?)>", flags=re.I)
@ -284,7 +332,7 @@ class Pesterchum(Lexer):
##(mecmd, self._mecmdre),
(CTag, self._ctag_begin),
##(CTag, self._ctag_end)
(CTagEnd, 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("</c>"))
balanced.append(CTagEnd("</c>", 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("</c>", self.ref, None))
closecolor = lambda: converted.append(CTagEnd("</c>", self.ref, None).convert(format))
closecolor = lambda: converted.append(
CTagEnd("</c>", 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,7 +487,7 @@ 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,7 +578,8 @@ class RelayChat(Lexer):
return converted
def _list_convert_new(self, target, format=None):
if format is None: format = self.ref
if format is None:
format = self.ref
converted = []
cstack = []

View file

@ -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,81 +388,83 @@ 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({
_svg_colors.update(
{
"aliceblue": Color(240, 248, 255),
"antiquewhite": Color(250, 235, 215),
"aqua": Color( 0, 255, 255),
"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),
"black": Color(0, 0, 0),
"blanchedalmond": Color(255, 235, 205),
"blue": Color( 0, 0, 255),
"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),
"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),
"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),
"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),
"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),
"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),
"deepskyblue": Color(0, 191, 255),
"dimgray": Color(105, 105, 105),
"dimgrey": Color(105, 105, 105),
"dodgerblue": Color( 30, 144, 255),
"dodgerblue": Color(30, 144, 255),
"firebrick": Color(178, 34, 34),
"floralwhite": Color(255, 250, 240),
"forestgreen": Color( 34, 139, 34),
"forestgreen": Color(34, 139, 34),
"fuchsia": Color(255, 0, 255),
"gainsboro": Color(220, 220, 220),
"ghostwhite": Color(248, 248, 255),
@ -454,12 +472,12 @@ _svg_colors.update({
"goldenrod": Color(218, 165, 32),
"gray": Color(128, 128, 128),
"grey": Color(128, 128, 128),
"green": Color( 0, 128, 0),
"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),
"indigo": Color(75, 0, 130),
"ivory": Color(255, 255, 240),
"khaki": Color(240, 230, 140),
"lavender": Color(230, 230, 250),
@ -475,32 +493,32 @@ _svg_colors.update({
"lightgrey": Color(211, 211, 211),
"lightpink": Color(255, 182, 193),
"lightsalmon": Color(255, 160, 122),
"lightseagreen": Color( 32, 178, 170),
"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),
"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),
"mediumblue": Color(0, 0, 205),
"mediumorchid": Color(186, 85, 211),
"mediumpurple": Color(147, 112, 219),
"mediumseagreen": Color( 60, 179, 113),
"mediumseagreen": Color(60, 179, 113),
"mediumslateblue": Color(123, 104, 238),
"mediumspringgreen": Color( 0, 250, 154),
"mediumturquoise": Color( 72, 209, 204),
"mediumspringgreen": Color(0, 250, 154),
"mediumturquoise": Color(72, 209, 204),
"mediumvioletred": Color(199, 21, 133),
"midnightblue": Color( 25, 25, 112),
"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),
"navy": Color(0, 0, 128),
"oldlace": Color(253, 245, 230),
"olive": Color(128, 128, 0),
"olivedrab": Color(107, 142, 35),
@ -520,11 +538,11 @@ _svg_colors.update({
"purple": Color(128, 0, 128),
"red": Color(255, 0, 0),
"rosybrown": Color(188, 143, 143),
"royalblue": Color( 65, 105, 225),
"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),
"seagreen": Color(46, 139, 87),
"seashell": Color(255, 245, 238),
"sienna": Color(160, 82, 45),
"silver": Color(192, 192, 192),
@ -533,26 +551,28 @@ _svg_colors.update({
"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),
"springgreen": Color(0, 255, 127),
"steelblue": Color(70, 130, 180),
"tan": Color(210, 180, 140),
"teal": Color( 0, 128, 128),
"teal": Color(0, 128, 128),
"thistle": Color(216, 191, 216),
"tomato": Color(255, 99, 71),
"turquoise": Color( 64, 224, 208),
"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)
})
"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({
_irc_colors.update(
{
# These are all taken from *MY* XChat settings - they aren't guaranteed to
# please everyone!
0: Color(0xFFFFFF),
@ -591,9 +611,9 @@ _irc_colors.update({
29: Color(0x99004D),
30: Color(0x323232),
31: Color(0x929292),
99: Color(0x999999) # Until I think of a better solution to this
})
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

File diff suppressed because it is too large Load diff

View file

@ -5,424 +5,646 @@ 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 == '':
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 == '':
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 = ''
elif upx_enabled == "n":
upx_dir = ""
while (windowed != 'y') and (windowed != 'n'):
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]))
shutil.copy(x, os.path.join("dist", data_files[x]))
files = os.listdir('dist')
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.move(os.path.join("dist", x), os.path.join("dist", "Pesterchum"))
#shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
# 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]))
shutil.copy(x, os.path.join("dist", data_files_linux[x]))
files = os.listdir('dist')
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)

View file

@ -10,12 +10,13 @@ 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)
@ -23,25 +24,25 @@ class PythonQuirks(ScriptQuirks):
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,
@ -55,4 +56,3 @@ class PythonQuirks(ScriptQuirks):
msgbox.exec()
else:
self.quirks[obj.command] = obj

View file

@ -1,12 +1,15 @@
import os
import socket
class TwmnError(Exception):
UNWN_ERR = -1
NO_TWMND = -2
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("<root><title>" + self.title + "</title>"
s.send(
"<root><title>" + self.title + "</title>"
"<content>" + self.msg + "</content>"
"<icon>" + self.icon + "</icon></root>")
"<icon>" + self.icon + "</icon></root>"
)
else:
s.send("<root><title>" + self.title + "</title>"
s.send(
"<root><title>" + self.title + "</title>"
"<content>" + self.msg + "</content>"
"<icon>" + self.icon + "</icon>"
"<duration>" + str(self.time) + "</duration></root>")
"<duration>" + str(self.time) + "</duration></root>"
)
except:
raise TwmnError(TwmnError.NO_TWMND)
if __name__ == "__main__":
init()
n = Notification("PyTwmn", "This is a notification!")

View file

@ -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

View file

@ -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"

View file

@ -1,5 +1,6 @@
import re
def rainbow(text):
"""Example implementation of a gradient function,
distributes colors over text, accounting for links,
@ -20,7 +21,8 @@ def rainbow(text):
^ There's more useful info here too :3c
"""
# Values of 'gradient' can be any amount of hex/RGB colors.
gradient = ["#ff0000",
gradient = [
"#ff0000",
"#ff8000",
"#ffff00",
"#80ff00",
@ -31,44 +33,39 @@ def rainbow(text):
"#0000ff",
"#8000ff",
"#ff00ff",
"#ff0080"]
"#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)(?<!//)\bwww\.[^\s]+?\.")
# _url2re = re.compile(r"(?i)(?<!//)\bwww\.[^\s]+?\.")
_memore = re.compile(r"(\s|^)(#[A-Za-z0-9_]+)")
_handlere = re.compile(r"(\s|^)(@[A-Za-z0-9_]+)")

View file

@ -8,10 +8,11 @@ except ImportError:
import ostools
PchumLog = logging.getLogger('pchumLogger')
PchumLog = logging.getLogger("pchumLogger")
RANDNICK = "randomEncounter"
class RandomHandler(QtCore.QObject):
def __init__(self, parent):
QtCore.QObject.__init__(self, parent)
@ -30,14 +31,18 @@ class RandomHandler(QtCore.QObject):
def setRandomer(self, r):
if r != self.mainwindow.userprofile.getRandom():
if r: code = "+"
else: code = "-"
if r:
code = "+"
else:
code = "-"
self.queue.append(code)
self.mainwindow.sendNotice.emit(code, self.randNick)
def setIdle(self, i):
if i: code = "~"
else: code = "*"
if i:
code = "~"
else:
code = "*"
self.queue.append(code)
self.mainwindow.sendNotice.emit(code, self.randNick)
@ -57,13 +62,13 @@ class RandomHandler(QtCore.QObject):
self.mainwindow.userprofile.setRandom(True)
elif l[1][0] == "n":
self.mainwindow.userprofile.setRandom(False)
elif code in ["+","-"]:
elif code in ["+", "-"]:
if l[1][0] == "k":
if code == "+":
self.mainwindow.userprofile.setRandom(True)
else:
self.mainwindow.userprofile.setRandom(False)
elif code in ["~","*"]:
elif code in ["~", "*"]:
if l[1][0] == "k":
pass
elif code == "!":

View file

@ -7,12 +7,12 @@ import os
import sys
from subprocess import call
#print(sys.argv)
# print(sys.argv)
def main(argv):
arguments = ''
arguments = ""
for x in argv[1:]:
arguments += x + ' '
#print(arguments)
arguments += x + " "
# print(arguments)
directory_path = os.getcwd()
print("Working directory: " + directory_path)
@ -22,5 +22,6 @@ def main(argv):
retcode = call("python3 pesterchum.py " + " " + str(arguments), shell=True)
print(retcode)
if __name__ == "__main__":
main(sys.argv)

143
setup.py
View file

@ -9,9 +9,11 @@ from version import buildVersion
if sys.version_info < (3, 0, 0):
sys.exit("Python versions lower than 3 are not supported.")
def is_64bit() -> bool:
return sys.maxsize > 2**32
path = ""
base = None
if sys.platform == "win32":
@ -19,13 +21,18 @@ 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",
includefiles = [
"quirks",
"smilies",
"themes",
"docs",
@ -33,106 +40,112 @@ includefiles = ["quirks",
"LICENSE",
"CHANGELOG.md",
"PCskins.png",
"Pesterchum.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
# "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 </3)
'pygame.examples',
'pygame.tests',
'pydoc_data'],
"pygame.examples",
"pygame.tests",
"pydoc_data",
],
"include_files": includefiles,
"include_msvcr": True, # cx_freeze copies 64-bit binaries always?
"path": path # Improved in 6.6, path to be safe
# VCRUNTIME140.dll <3
}
if (sys.platform == 'win32') & (sys.version_info.major == 3) & (sys.version_info.minor == 8):
build_exe_options["excludes"].append('tkinter')
if (
(sys.platform == "win32")
& (sys.version_info.major == 3)
& (sys.version_info.minor == 8)
):
build_exe_options["excludes"].append("tkinter")
bdist_mac_options = {
'iconfile': 'trayicon32.icns',
'bundle_name': "Pesterchum"
}
bdist_mac_options = {"iconfile": "trayicon32.icns", "bundle_name": "Pesterchum"}
description = "Pesterchum"
icon = "pesterchum.ico"
# See https://stackoverflow.com/questions/15734703/use-cx-freeze-to-create-an-msi-that-adds-a-shortcut-to-the-desktop
shortcut_table = [
("DesktopShortcut", # Shortcut
(
"DesktopShortcut", # Shortcut
"DesktopFolder", # Directory_
"Pesterchum", # Name
"TARGETDIR", # Component_
"[TARGETDIR]pesterchum.exe",# Target
"[TARGETDIR]pesterchum.exe", # Target
None, # Arguments
description, # Description
None, # Hotkey
None, # Icon (Is inherited from pesterchum.exe)
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # WkDir
"TARGETDIR", # WkDir
),
("StartMenuShortcut", # Shortcut
(
"StartMenuShortcut", # Shortcut
"StartMenuFolder", # Directory_
"Pesterchum", # Name
"TARGETDIR", # Component_
"[TARGETDIR]pesterchum.exe",# Target
"[TARGETDIR]pesterchum.exe", # Target
None, # Arguments
description, # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # WkDir
)
]
"TARGETDIR", # WkDir
),
]
msi_data = {"Shortcut": shortcut_table}
bdist_msi_options = {'data': msi_data,
'summary_data': {
'comments': "FL1P",
'keywords': "Pesterchum"},
'upgrade_code': "{86740d75-f1f2-48e8-8266-f36395a2d77f}",
'add_to_path': False, # !!!
'all_users': False,
'install_icon': "pesterchum.ico"}
bdist_msi_options = {
"data": msi_data,
"summary_data": {"comments": "FL1P", "keywords": "Pesterchum"},
"upgrade_code": "{86740d75-f1f2-48e8-8266-f36395a2d77f}",
"add_to_path": False, # !!!
"all_users": False,
"install_icon": "pesterchum.ico",
}
setup(
name = "Pesterchum",
version = buildVersion,
url = "https://github.com/Dpeta/pesterchum-alt-servers",
description = description,#"P3ST3RCHUM",
options = {"build_exe": build_exe_options,
name="Pesterchum",
version=buildVersion,
url="https://github.com/Dpeta/pesterchum-alt-servers",
description=description, # "P3ST3RCHUM",
options={
"build_exe": build_exe_options,
"bdist_msi": bdist_msi_options,
"bdist_mac": bdist_mac_options},
"bdist_mac": bdist_mac_options,
},
packages="",
executables = [Executable("pesterchum.py",
base=base,
icon=icon
)])
executables=[Executable("pesterchum.py", base=base, icon=icon)],
)

203
toast.py
View file

@ -1,5 +1,6 @@
import os
#import time
# import time
import inspect
import logging
@ -12,25 +13,28 @@ except ImportError:
import ostools
_datadir = ostools.getDataDir()
PchumLog = logging.getLogger('pchumLogger')
PchumLog = logging.getLogger("pchumLogger")
#try:
# try:
# import pynotify
#except:
# except:
# pynotify = None
# Pynotify is broken.
pynotify = None
class DefaultToast(object):
def __init__(self, machine, title, msg, icon):
self.machine = machine
self.title = title
self.msg = msg
self.icon = icon
def show(self):
print(self.title, self.msg, self.icon)
self.done()
def done(self):
t = self.machine.toasts[0]
if t.title == self.title and t.msg == self.msg and t.icon == self.icon:
@ -38,6 +42,7 @@ class DefaultToast(object):
self.machine.displaying = False
PchumLog.info("Done")
class ToastMachine(object):
class __Toast__(object):
def __init__(self, machine, title, msg, time=3000, icon="", importance=0):
@ -57,19 +62,32 @@ class ToastMachine(object):
self.title = title
if inspect.ismethod(self.title) or inspect.isfunction(self.title):
self.title = self.title()
else: return self.title
else:
return self.title
def msgM(self, msg=None):
if msg: self.msg = msg
else: return self.msg
if msg:
self.msg = msg
else:
return self.msg
def timeM(self, time=None):
if time: self.time = time
else: return self.time
if time:
self.time = time
else:
return self.time
def iconM(self, icon=None):
if icon: self.icon = icon
else: return self.icon
if icon:
self.icon = icon
else:
return self.icon
def importanceM(self, importance=None):
if importance != None: self.importance = importance
else: return self.importance
if importance != None:
self.importance = importance
else:
return self.importance
def show(self):
if self.machine.on:
@ -85,7 +103,7 @@ class ToastMachine(object):
def realShow(self):
self.machine.displaying = True
t = None
for (k,v) in self.machine.types.items():
for (k, v) in self.machine.types.items():
if self.machine.type == k:
try:
args = inspect.getargspec(v.__init__).args
@ -93,10 +111,10 @@ class ToastMachine(object):
args = []
extras = {}
if 'parent' in args:
extras['parent'] = self.machine.parent
if 'time' in args:
extras['time'] = self.time
if "parent" in args:
extras["parent"] = self.machine.parent
if "time" in args:
extras["time"] = self.time
if k == "libnotify" or k == "twmn":
t = v(self.title, self.msg, self.icon, **extras)
else:
@ -111,21 +129,41 @@ class ToastMachine(object):
t.set_urgency(pynotify.URGENCY_LOW)
break
if not t:
if 'default' in self.machine.types:
if 'parent' in inspect.getargspec(self.machine.types['default'].__init__).args:
t = self.machine.types['default'](self.machine, self.title, self.msg, self.icon, self.machine.parent)
if "default" in self.machine.types:
if (
"parent"
in inspect.getargspec(
self.machine.types["default"].__init__
).args
):
t = self.machine.types["default"](
self.machine,
self.title,
self.msg,
self.icon,
self.machine.parent,
)
else:
t = self.machine.types['default'](self.machine, self.title, self.msg, self.icon)
t = self.machine.types["default"](
self.machine, self.title, self.msg, self.icon
)
else:
t = DefaultToast(self.title, self.msg, self.icon)
t.show()
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={},
):
self.parent = parent
self.name = name
self.on = on
@ -143,7 +181,7 @@ class ToastMachine(object):
return self.__Toast__(self, title, msg, time=time, icon=icon)
def setEnabled(self, on):
self.on = (on is True)
self.on = on is True
def currentType(self):
return self.type
@ -157,15 +195,16 @@ class ToastMachine(object):
if not pynotify or not pynotify.init("ToastMachine"):
PchumLog.info("Problem initilizing pynotify")
return
#self.type = type = "default"
# self.type = type = "default"
elif type == "twmn":
import pytwmn
try:
pytwmn.init()
except pytwmn.ERROR as e:
PchumLog.error("Problem initilizing pytwmn: " + str(e))
return
#self.type = type = "default"
# self.type = type = "default"
self.type = type
def appName(self):
@ -200,7 +239,11 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
if ostools.isWin32():
self.setWindowFlags(QtCore.Qt.WindowType.ToolTip)
else:
self.setWindowFlags(QtCore.Qt.WindowType.WindowStaysOnTopHint | QtCore.Qt.WindowType.X11BypassWindowManagerHint | QtCore.Qt.WindowType.ToolTip)
self.setWindowFlags(
QtCore.Qt.WindowType.WindowStaysOnTopHint
| QtCore.Qt.WindowType.X11BypassWindowManagerHint
| QtCore.Qt.WindowType.ToolTip
)
self.m_animation = QtCore.QParallelAnimationGroup()
anim = QtCore.QPropertyAnimation(self)
@ -219,7 +262,7 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
self.icon = QtWidgets.QLabel("")
iconPixmap = QtGui.QPixmap(icon).scaledToWidth(30)
self.icon.setPixmap(iconPixmap)
#else:
# else:
# self.icon.setPixmap(QtGui.QPixmap(30, 30))
# self.icon.pixmap().fill(QtGui.QColor(0,0,0,0))
@ -228,8 +271,8 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
if self.icon:
layout_1 = QtWidgets.QGridLayout()
layout_1.addWidget(self.icon, 0,0, 1,1)
layout_1.addWidget(self.title, 0,1, 1,7)
layout_1.addWidget(self.icon, 0, 0, 1, 1)
layout_1.addWidget(self.title, 0, 1, 1, 7)
layout_1.setAlignment(self.msg, QtCore.Qt.AlignmentFlag.AlignTop)
layout_0.addLayout(layout_1)
else:
@ -242,7 +285,12 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
self.setLayout(layout_0)
self.setGeometry(0,0, self.parent().theme["toasts/width"], self.parent().theme["toasts/height"])
self.setGeometry(
0,
0,
self.parent().theme["toasts/width"],
self.parent().theme["toasts/height"],
)
self.setStyleSheet(self.parent().theme["toasts/style"])
self.title.setStyleSheet(self.parent().theme["toasts/title/style"])
if self.icon:
@ -250,10 +298,14 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
self.msg.setStyleSheet(self.parent().theme["toasts/content/style"])
self.layout().setSpacing(0)
self.msg.setText(PesterToast.wrapText(self.msg.font(),
self.msg.setText(
PesterToast.wrapText(
self.msg.font(),
str(self.msg.text()),
self.parent().theme["toasts/width"],
self.parent().theme["toasts/content/style"]))
self.parent().theme["toasts/content/style"],
)
)
screens = QtWidgets.QApplication.screens()
screen = screens[0] # Should be the main one right???
@ -274,8 +326,7 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
def done(self):
QtWidgets.QWidget.hide(self)
t = self.machine.toasts[0]
if t.title == str(self.title.text()) and \
t.msg == str(self.content):
if t.title == str(self.title.text()) and t.msg == str(self.content):
self.machine.toasts.pop(0)
self.machine.displaying = False
if self.machine.on:
@ -300,13 +351,13 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
@QtCore.pyqtSlot(QtCore.QVariant)
def updateBottomLeftAnimation(self, value):
#p = QtWidgets.QApplication.desktop().availableGeometry(self).bottomRight()
# p = QtWidgets.QApplication.desktop().availableGeometry(self).bottomRight()
screens = QtWidgets.QApplication.screens()
screen = screens[0] # Main window?
p = screen.availableGeometry().bottomRight()
val = (self.height())/100
val = (self.height()) / 100
# Does type casting this to an int have any negative consequences?
self.move(int(p.x()-self.width()), int(p.y() - (value * val) +1))
self.move(int(p.x() - self.width()), int(p.y() - (value * val) + 1))
self.layout().setSpacing(0)
QtWidgets.QWidget.show(self)
@ -321,21 +372,21 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
ret = []
metric = QtGui.QFontMetrics(font)
if "padding" in css:
if css[css.find("padding")+7] != "-":
if css[css.find("padding") + 7] != "-":
colon = css.find(":", css.find("padding"))
semicolon = css.find(";", css.find("padding"))
if semicolon < 0:
stuff = css[colon+1:]
stuff = css[colon + 1 :]
else:
stuff = css[colon+1:semicolon]
stuff = css[colon + 1 : semicolon]
stuff = stuff.replace("px", "").lstrip().rstrip()
stuff = stuff.split(" ")
if len(stuff) == 1:
maxwidth -= int(stuff[0])*2
maxwidth -= int(stuff[0]) * 2
elif len(stuff) == 2:
maxwidth -= int(stuff[1])*2
maxwidth -= int(stuff[1]) * 2
elif len(stuff) == 3:
maxwidth -= int(stuff[1])*2
maxwidth -= int(stuff[1]) * 2
elif len(stuff) == 4:
maxwidth -= int(stuff[1]) + int(stuff[3])
else:
@ -343,9 +394,9 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
colon = css.find(":", css.find("padding-left"))
semicolon = css.find(";", css.find("padding-left"))
if semicolon < 0:
stuff = css[colon+1:]
stuff = css[colon + 1 :]
else:
stuff = css[colon+1:semicolon]
stuff = css[colon + 1 : semicolon]
stuff = stuff.replace("px", "").lstrip().rstrip()
if stuff.isdigit():
maxwidth -= int(stuff)
@ -353,9 +404,9 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
colon = css.find(":", css.find("padding-right"))
semicolon = css.find(";", css.find("padding-right"))
if semicolon < 0:
stuff = css[colon+1:]
stuff = css[colon + 1 :]
else:
stuff = css[colon+1:semicolon]
stuff = css[colon + 1 : semicolon]
stuff = stuff.replace("px", "").lstrip().rstrip()
if stuff.isdigit():
maxwidth -= int(stuff)
@ -367,28 +418,36 @@ class PesterToast(QtWidgets.QWidget, DefaultToast):
curspace = lastspace
while metric.horizontalAdvance(text, curspace) < maxwidth:
lastspace = curspace
curspace = text.find(" ", lastspace+1)
curspace = text.find(" ", lastspace + 1)
if curspace == -1:
break
if (metric.horizontalAdvance(text[:lastspace]) > 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()