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

@ -5,6 +5,6 @@ import sys
try:
from .run_as_subprocess import main
except (ImportError, ModuleNotFoundError):
from run_as_subprocess import main
from run_as_subprocess import main
main(sys.argv)

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"
@ -516,7 +536,7 @@ class ConsoleInput(QtWidgets.QLineEdit):
# NOTE: Do we really want everyone knowing we're around if we're
# messing around in the console? Hm.
parent.mainwindow.idler.time = 0
if evtkey == QtCore.Qt.Key.Key_Up:
text = str(self.text())
next = parent.text.history.next(text)

521
convo.py

File diff suppressed because it is too large Load diff

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,
convertTags,
lexMessage,
parseRegexpFunctions,
smiledict)
from parsetools import (
timeDifference,
convertTags,
lexMessage,
parseRegexpFunctions,
smiledict,
)
from mispeller import mispeller
_urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+")
#_url2re = re.compile(r"(?i)(?<!//)\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:
@ -46,7 +50,7 @@ class pesterQuirk(object):
self.checkstate = self.quirk["checkstate"]
except KeyError:
pass
def apply(self, string, first=False, last=False):
if not self.on:
return string
@ -60,7 +64,7 @@ class pesterQuirk(object):
fr = self.quirk["from"]
if not first and len(fr) > 0 and fr[0] == "^":
return string
if not last and len(fr) > 0 and fr[len(fr)-1] == "$":
if not last and len(fr) > 0 and fr[len(fr) - 1] == "$":
return string
to = self.quirk["to"]
pt = parseRegexpFunctions(to)
@ -71,15 +75,17 @@ class pesterQuirk(object):
fr = self.quirk["from"]
if not first and len(fr) > 0 and fr[0] == "^":
return string
if not last and len(fr) > 0 and fr[len(fr)-1] == "$":
if not last and len(fr) > 0 and fr[len(fr) - 1] == "$":
return string
def randomrep(mo):
choice = random.choice(self.quirk["randomlist"])
pt = parseRegexpFunctions(choice)
return pt.expand(mo)
return re.sub(self.quirk["from"], randomrep, string)
elif self.type == "spelling":
percentage = self.quirk["percentage"]/100.0
percentage = self.quirk["percentage"] / 100.0
words = string.split(" ")
newl = []
ctag = re.compile("(</?c=?.*?>)", re.I)
@ -99,7 +105,7 @@ class pesterQuirk(object):
else:
newl.append(w)
return " ".join(newl)
def __str__(self):
if self.type == "prefix":
return "BEGIN WITH: %s" % (self.quirk["value"])
@ -108,28 +114,38 @@ class pesterQuirk(object):
elif self.type == "replace":
return "REPLACE %s WITH %s" % (self.quirk["from"], self.quirk["to"])
elif self.type == "regexp":
return "REGEXP: %s REPLACED WITH %s" % (self.quirk["from"], self.quirk["to"])
return "REGEXP: %s REPLACED WITH %s" % (
self.quirk["from"],
self.quirk["to"],
)
elif self.type == "random":
return "REGEXP: %s RANDOMLY REPLACED WITH %s" % (self.quirk["from"], [r for r in self.quirk["randomlist"]])
return "REGEXP: %s RANDOMLY REPLACED WITH %s" % (
self.quirk["from"],
[r for r in self.quirk["randomlist"]],
)
elif self.type == "spelling":
return "MISPELLER: %d%%" % (self.quirk["percentage"])
class pesterQuirks(object):
def __init__(self, quirklist):
self.quirklist = []
for q in quirklist:
self.addQuirk(q)
def plainList(self):
return [q.quirk for q in self.quirklist]
def addQuirk(self, q):
if type(q) == dict:
self.quirklist.append(pesterQuirk(q))
elif type(q) == pesterQuirk:
self.quirklist.append(q)
def apply(self, lexed, first=False, last=False):
prefix = [q for q in self.quirklist if q.type=='prefix']
suffix = [q for q in self.quirklist if q.type=='suffix']
prefix = [q for q in self.quirklist if q.type == "prefix"]
suffix = [q for q in self.quirklist if q.type == "suffix"]
newlist = []
for (i, o) in enumerate(lexed):
if type(o) not in [str, str]:
@ -140,7 +156,7 @@ class pesterQuirks(object):
newlist.append(string)
newlist.append(o)
continue
lastStr = (i == len(lexed)-1)
lastStr = i == len(lexed) - 1
string = o
for q in self.quirklist:
try:
@ -170,17 +186,17 @@ class pesterQuirks(object):
excludes.sort(key=lambda exclude: exclude.start())
# Recursion check.
# Strings like http://:3: require this.
for n in range(0, len(excludes)-1):
if excludes[n].end() > excludes[n+1].start():
for n in range(0, len(excludes) - 1):
if excludes[n].end() > excludes[n + 1].start():
excludes.pop(n)
# Seperate parts to be quirked.
sendparts = list()
# Add string until start of exclude at index 0.
until = excludes[0].start()
sendparts.append(string[:until])
sendparts.append(string[:until])
# Add strings between excludes.
for part in range(1, len(excludes)):
after = excludes[part-1].end()
after = excludes[part - 1].end()
until = excludes[part].start()
sendparts.append(string[after:until])
# Add string after exclude at last index.
@ -191,50 +207,46 @@ class pesterQuirks(object):
recvparts = list()
for part in sendparts:
# No split, apply like normal.
if q.type == 'regexp' or q.type == 'random':
recvparts.append(q.apply(part,
first=(i==0),
last=lastStr))
elif q.type == 'prefix' and i == 0:
if q.type == "regexp" or q.type == "random":
recvparts.append(
q.apply(part, first=(i == 0), last=lastStr)
)
elif q.type == "prefix" and i == 0:
recvparts.append(q.apply(part))
elif q.type == 'suffix' and lastStr:
elif q.type == "suffix" and lastStr:
recvparts.append(q.apply(part))
else:
recvparts.append(q.apply(part))
# Reconstruct and update string.
string = ''
#print("excludes: " + str(excludes))
#print("sendparts: " + str(sendparts))
#print("recvparts: " + str(recvparts))
string = ""
# print("excludes: " + str(excludes))
# print("sendparts: " + str(sendparts))
# print("recvparts: " + str(recvparts))
for part in range(0, len(excludes)):
string += recvparts[part]
string += excludes[part].group()
string += recvparts[-1]
else:
# No split, apply like normal.
if q.type != 'prefix' and q.type != 'suffix':
if q.type == 'regexp' or q.type == 'random':
string = q.apply(string,
first=(i==0),
last=lastStr)
if q.type != "prefix" and q.type != "suffix":
if q.type == "regexp" or q.type == "random":
string = q.apply(string, first=(i == 0), last=lastStr)
else:
string = q.apply(string)
elif q.type == 'prefix' and i == 0:
elif q.type == "prefix" and i == 0:
string = q.apply(string)
elif q.type == 'suffix' and lastStr:
elif q.type == "suffix" and lastStr:
string = q.apply(string)
else:
# No split, apply like normal.
if q.type != 'prefix' and q.type != 'suffix':
if q.type == 'regexp' or q.type == 'random':
string = q.apply(string,
first=(i==0),
last=lastStr)
if q.type != "prefix" and q.type != "suffix":
if q.type == "regexp" or q.type == "random":
string = q.apply(string, first=(i == 0), last=lastStr)
else:
string = q.apply(string)
elif q.type == 'prefix' and i == 0:
elif q.type == "prefix" and i == 0:
string = q.apply(string)
elif q.type == 'suffix' and lastStr:
elif q.type == "suffix" and lastStr:
string = q.apply(string)
newlist.append(string)
final = []
@ -249,8 +261,17 @@ class pesterQuirks(object):
for q in self.quirklist:
yield q
class PesterProfile(object):
def __init__(self, handle, color=None, mood=Mood("offline"), group=None, notes="", chumdb=None):
def __init__(
self,
handle,
color=None,
mood=Mood("offline"),
group=None,
notes="",
chumdb=None,
):
self.handle = handle
if color is None:
if chumdb:
@ -266,6 +287,7 @@ class PesterProfile(object):
group = "Chums"
self.group = group
self.notes = notes
def initials(self, time=None):
handle = self.handle
caps = [l for l in handle if l.isupper()]
@ -275,37 +297,46 @@ class PesterProfile(object):
PchumLog.debug("caps = " + str(caps))
# Fallback for invalid string
try:
initials = (handle[0]+caps[0]).upper()
initials = (handle[0] + caps[0]).upper()
except:
PchumLog.exception('')
PchumLog.exception("")
initials = "XX"
PchumLog.debug("initials = " + str(initials))
if hasattr(self, 'time') and time:
if hasattr(self, "time") and time:
if self.time > time:
return "F"+initials
return "F" + initials
elif self.time < time:
return "P"+initials
return "P" + initials
else:
return "C"+initials
return "C" + initials
else:
return initials
def colorhtml(self):
if self.color:
return self.color.name()
else:
return "#000000"
def colorcmd(self):
if self.color:
(r, g, b, a) = self.color.getRgb()
return "%d,%d,%d" % (r,g,b)
return "%d,%d,%d" % (r, g, b)
else:
return "0,0,0"
def plaindict(self):
return (self.handle, {"handle": self.handle,
"mood": self.mood.name(),
"color": str(self.color.name()),
"group": str(self.group),
"notes": str(self.notes)})
return (
self.handle,
{
"handle": self.handle,
"mood": self.mood.name(),
"color": str(self.color.name()),
"group": str(self.group),
"notes": str(self.notes),
},
)
def blocked(self, config):
return self.handle in config.getBlocklist()
@ -315,112 +346,248 @@ class PesterProfile(object):
uppersuffix = suffix.upper()
if time is not None:
handle = "%s %s" % (time.temporal, self.handle)
initials = time.pcf+self.initials()+time.number+uppersuffix
initials = time.pcf + self.initials() + time.number + uppersuffix
else:
handle = self.handle
initials = self.initials()+uppersuffix
return "<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.
@ -135,18 +155,27 @@ class MovingWindow(QtWidgets.QFrame):
print("PyQt <= 5.14?")
print(str(e))
if event.button() == 1:
self.moving = event.globalPos() - self.pos()
self.moving = event.globalPos() - self.pos()
def mouseReleaseEvent(self, event):
if event.button() == 1:
self.update()
self.moving = None
class NoneSound(object):
def __init__(self, *args, **kwargs):
pass
def play(self): pass
def setVolume(self, v): pass
def set_volume(self, v): pass
def play(self):
pass
def setVolume(self, v):
pass
def set_volume(self, v):
pass
class WMButton(QtWidgets.QPushButton):
def __init__(self, icon, parent=None):

501
irc.py

File diff suppressed because it is too large Load diff

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

1164
memos.py

File diff suppressed because it is too large Load diff

693
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-="],
[x for x in "qwertyuiop[]"],
[x for x in "asdfghjkl:;'"],
[x for x in "zxcvbnm,.>/?"]]
kbloc = [
[x for x in "1234567890-="],
[x for x in "qwertyuiop[]"],
[x for x in "asdfghjkl:;'"],
[x for x in "zxcvbnm,.>/?"],
]
kbdict = {}
for (i, l) in enumerate(kbloc):
for (j, k) in enumerate(l):
kbdict[k] = (i, j)
sounddict = {"a": "e", "b": "d", "c": "k", "d": "g", "e": "eh",
"f": "ph", "g": "j", "h": "h", "i": "ai", "j": "ge",
"k": "c", "l": "ll", "m": "n", "n": "m", "o": "oa",
"p": "b", "q": "kw", "r": "ar", "s": "ss", "t": "d",
"u": "you", "v": "w", "w": "wn", "x": "cks", "y": "uy", "z": "s"}
sounddict = {
"a": "e",
"b": "d",
"c": "k",
"d": "g",
"e": "eh",
"f": "ph",
"g": "j",
"h": "h",
"i": "ai",
"j": "ge",
"k": "c",
"l": "ll",
"m": "n",
"n": "m",
"o": "oa",
"p": "b",
"q": "kw",
"r": "ar",
"s": "ss",
"t": "d",
"u": "you",
"v": "w",
"w": "wn",
"x": "cks",
"y": "uy",
"z": "s",
}
def mispeller(word):
if len(word) <= 6:
num = 1
else:
num = random.choice([1,2])
num = random.choice([1, 2])
wordseq = list(range(0, len(word)))
random.shuffle(wordseq)
letters = wordseq[0:num]
def mistype(string, i):
l = string[i]
if l not in kbdict:
@ -31,30 +57,42 @@ def mispeller(word):
lpos = kbdict[l]
newpos = lpos
while newpos == lpos:
newpos = ((lpos[0] + random.choice([-1, 0, 1])) % len(kbloc),
(lpos[1] + random.choice([-1,0,1])) % len(kbloc[0]))
string = string[0:i]+kbloc[newpos[0]][newpos[1]]+string[i+1:]
newpos = (
(lpos[0] + random.choice([-1, 0, 1])) % len(kbloc),
(lpos[1] + random.choice([-1, 0, 1])) % len(kbloc[0]),
)
string = string[0:i] + kbloc[newpos[0]][newpos[1]] + string[i + 1 :]
return string
def transpose(string, i):
j = (i + random.choice([-1,1])) % len(string)
j = (i + random.choice([-1, 1])) % len(string)
l = [c for c in string]
l[i], l[j] = l[j], l[i]
return "".join(l)
def randomletter(string, i):
string = string[0:i+1]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:]
string = (
string[0 : i + 1]
+ random.choice("abcdefghijklmnopqrstuvwxyz")
+ string[i + 1 :]
)
return string
def randomreplace(string, i):
string = string[0:i]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:]
string = (
string[0:i] + random.choice("abcdefghijklmnopqrstuvwxyz") + string[i + 1 :]
)
return string
def soundalike(string, i):
try:
c = sounddict[string[i]]
except:
return string
string = string[0:i]+c+string[i+1:]
string = string[0:i] + c + string[i + 1 :]
return string
func = random.choice([mistype, transpose, randomletter, randomreplace,
soundalike])
func = random.choice([mistype, transpose, randomletter, randomreplace, soundalike])
for i in letters:
word = func(word, i)
return word

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

@ -1,37 +1,44 @@
import os
import sys
import platform
try:
from PyQt6.QtCore import QStandardPaths
except ImportError:
print("PyQt5 fallback (ostools.py)")
from PyQt5.QtCore import QStandardPaths
def isOSX():
return sys.platform == "darwin"
def isWin32():
return sys.platform == "win32"
def isLinux():
return sys.platform.startswith("linux")
def isOSXBundle():
return isOSX() and (os.path.abspath('.').find(".app") != -1)
return isOSX() and (os.path.abspath(".").find(".app") != -1)
def isOSXLeopard():
return isOSX() and platform.mac_ver()[0].startswith("10.5")
def osVer():
if isWin32():
return " ".join(platform.win32_ver())
elif isOSX():
ver = platform.mac_ver();
ver = platform.mac_ver()
return " ".join((ver[0], " (", ver[2], ")"))
elif isLinux():
return " ".join(platform.linux_distribution())
def validateDataDir():
"""Checks if data directory is present"""
# Define paths
@ -42,7 +49,7 @@ def validateDataDir():
errorlogs = os.path.join(datadir, "errorlogs")
backup = os.path.join(datadir, "backup")
js_pchum = os.path.join(datadir, "pesterchum.js")
dirs = [datadir, profile, quirks, logs, errorlogs, backup]
for d in dirs:
if (os.path.isdir(d) == False) or (os.path.exists(d) == False):
@ -50,20 +57,36 @@ def validateDataDir():
# pesterchum.js
if not os.path.exists(js_pchum):
with open(js_pchum, 'w') as f:
with open(js_pchum, "w") as f:
f.write("{}")
def getDataDir():
# Temporary fix for non-ascii usernames
# If username has non-ascii characters, just store userdata
# in the Pesterchum install directory (like before)
try:
if isOSX():
return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation), "Pesterchum/")
return os.path.join(
QStandardPaths.writableLocation(
QStandardPaths.StandardLocation.AppLocalDataLocation
),
"Pesterchum/",
)
elif isLinux():
return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation), ".pesterchum/")
return os.path.join(
QStandardPaths.writableLocation(
QStandardPaths.StandardLocation.HomeLocation
),
".pesterchum/",
)
else:
return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation), "pesterchum/")
return os.path.join(
QStandardPaths.writableLocation(
QStandardPaths.StandardLocation.AppLocalDataLocation
),
"pesterchum/",
)
except UnicodeDecodeError as e:
print(e)
return ''
return ""

View file

@ -16,7 +16,8 @@
# THE SOFTWARE.
import logging
PchumLog = logging.getLogger('pchumLogger')
PchumLog = logging.getLogger("pchumLogger")
import logging
import socket
@ -29,32 +30,33 @@ from oyoyo.parse import parse_raw_irc_command
from oyoyo import helpers
from oyoyo.cmdhandler import CommandError
class IRCClientError(Exception):
pass
class IRCClient:
""" IRC Client class. This handles one connection to a server.
"""IRC Client class. This handles one connection to a server.
This can be used either with or without IRCApp ( see connect() docs )
"""
def __init__(self, cmd_handler, **kwargs):
""" the first argument should be an object with attributes/methods named
as the irc commands. You may subclass from one of the classes in
oyoyo.cmdhandler for convenience but it is not required. The
methods should have arguments (prefix, args). prefix is
"""the first argument should be an object with attributes/methods named
as the irc commands. You may subclass from one of the classes in
oyoyo.cmdhandler for convenience but it is not required. The
methods should have arguments (prefix, args). prefix is
normally the sender of the command. args is a list of arguments.
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
this class provides defaults for callbacks that are required for
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
this class provides defaults for callbacks that are required for
normal IRC operation.
all other arguments should be keyword arguments. The most commonly
used will be nick, host and port. You can also specify an "on connect"
callback. ( check the source for others )
Warning: By default this class will not block on socket operations, this
Warning: By default this class will not block on socket operations, this
means if you use a plain while loop your app will consume 100% cpu.
To enable blocking pass blocking=True.
To enable blocking pass blocking=True.
>>> class My_Handler(DefaultCommandHandler):
... def privmsg(self, prefix, command, args):
@ -76,7 +78,7 @@ class IRCClient:
"""
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.nick = None
self.realname = None
self.username = None
@ -93,9 +95,9 @@ class IRCClient:
self._end = False
def send(self, *args, **kwargs):
""" send a message to the connected server. all arguments are joined
with a space for convenience, for example the following are identical
"""send a message to the connected server. all arguments are joined
with a space for convenience, for example the following are identical
>>> cli.send("JOIN %s" % some_room)
>>> cli.send("JOIN", some_room)
@ -104,24 +106,29 @@ class IRCClient:
the 'encoding' keyword argument (default 'utf8').
In python 3, all args must be of type str or bytes, *BUT* if they are
str they will be converted to bytes with the encoding specified by the
'encoding' keyword argument (default 'utf8').
'encoding' keyword argument (default 'utf8').
"""
if self._end == True:
return
# Convert all args to bytes if not already
encoding = kwargs.get('encoding') or 'utf8'
encoding = kwargs.get("encoding") or "utf8"
bargs = []
for arg in args:
if isinstance(arg, str):
bargs.append(bytes(arg, encoding))
elif isinstance(arg, bytes):
bargs.append(arg)
elif type(arg).__name__ == 'unicode':
elif type(arg).__name__ == "unicode":
bargs.append(arg.encode(encoding))
else:
PchumLog.warning('Refusing to send one of the args from provided: %s'% repr([(type(arg), arg) for arg in args]))
raise IRCClientError('Refusing to send one of the args from provided: %s'
% repr([(type(arg), arg) for arg in args]))
PchumLog.warning(
"Refusing to send one of the args from provided: %s"
% repr([(type(arg), arg) for arg in args])
)
raise IRCClientError(
"Refusing to send one of the args from provided: %s"
% repr([(type(arg), arg) for arg in args])
)
msg = bytes(" ", "UTF-8").join(bargs)
PchumLog.info('---> send "%s"' % msg)
@ -135,7 +142,9 @@ class IRCClient:
self._end = True
break
try:
ready_to_read, ready_to_write, in_error = select.select([], [self.socket], [])
ready_to_read, ready_to_write, in_error = select.select(
[], [self.socket], []
)
for x in ready_to_write:
x.sendall(msg + bytes("\r\n", "UTF-8"))
break
@ -157,35 +166,34 @@ class IRCClient:
# socket.timeout is deprecated in 3.10
PchumLog.warning("TimeoutError in on send, " + str(e))
raise socket.timeout
except (OSError,
IndexError,
ValueError,
Exception) as e:
except (OSError, IndexError, ValueError, Exception) as e:
PchumLog.warning("Unkown error on send, " + str(e))
if tries >= 9:
raise e
tries += 1
PchumLog.warning("Retrying send. (attempt %s)" % str(tries))
time.sleep(0.1)
PchumLog.debug("ready_to_write (len %s): " % str(len(ready_to_write)) + str(ready_to_write))
PchumLog.debug(
"ready_to_write (len %s): " % str(len(ready_to_write))
+ str(ready_to_write)
)
except Exception as se:
PchumLog.warning("Send Exception %s" % str(se))
try:
if not self.blocking and se.errno == 11:
pass
else:
#raise se
# raise se
self._end = True # This ok?
except AttributeError:
#raise se
# raise se
self._end = True # This ok?
def connect(self, verify_hostname=True):
""" initiates the connection to the server set in self.host:self.port
"""
PchumLog.info('connecting to %s:%s' % (self.host, self.port))
"""initiates the connection to the server set in self.host:self.port"""
PchumLog.info("connecting to %s:%s" % (self.host, self.port))
if self.ssl == True:
context = ssl.create_default_context()
if verify_hostname == False:
@ -193,9 +201,9 @@ class IRCClient:
context.verify_mode = ssl.CERT_NONE
bare_sock = socket.create_connection((self.host, self.port))
self.socket = context.wrap_socket(bare_sock,
server_hostname=self.host,
do_handshake_on_connect=False)
self.socket = context.wrap_socket(
bare_sock, server_hostname=self.host, do_handshake_on_connect=False
)
while True:
try:
self.socket.do_handshake()
@ -208,8 +216,8 @@ class IRCClient:
# Disconnect for now
self.socket.close()
bare_sock.close()
raise e
raise e
PchumLog.info("secure sockets version is %s" % self.socket.version())
else:
@ -224,7 +232,7 @@ class IRCClient:
elif self.blocking:
self.socket.setblocking(True)
#try:
# try:
# self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# if hasattr(socket, "TCP_KEEPIDLE"):
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
@ -232,16 +240,16 @@ class IRCClient:
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
# if hasattr(socket, "TCP_KEEPCNT"):
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 1)
#except Exception as e:
# except Exception as e:
# print(e)
helpers.nick(self, self.nick)
helpers.user(self, self.username, self.realname)
if self.connect_cb:
self.connect_cb(self)
def conn(self):
"""returns a generator object. """
"""returns a generator object."""
try:
buffer = bytes()
while not self._end:
@ -256,7 +264,9 @@ class IRCClient:
self._end = True
break
try:
ready_to_read, ready_to_write, in_error = select.select([self.socket], [], [])
ready_to_read, ready_to_write, in_error = select.select(
[self.socket], [], []
)
for x in ready_to_read:
buffer += x.recv(1024)
break
@ -278,17 +288,16 @@ class IRCClient:
# socket.timeout is deprecated in 3.10
PchumLog.warning("TimeoutError in on send, " + str(e))
raise socket.timeout
except (OSError,
IndexError,
ValueError,
Exception) as e:
except (OSError, IndexError, ValueError, Exception) as e:
PchumLog.debug("Miscellaneous exception in conn, " + str(e))
if tries >= 9:
raise e
tries += 1
PchumLog.debug("Possibly retrying recv. (attempt %s)" % str(tries))
PchumLog.debug(
"Possibly retrying recv. (attempt %s)" % str(tries)
)
time.sleep(0.1)
except socket.timeout as e:
PchumLog.warning("timeout in client.py, " + str(e))
if self._end:
@ -303,7 +312,7 @@ class IRCClient:
try: # a little dance of compatibility to get the errno
errno = e.errno
except AttributeError:
errno = e[0]
errno = e[0]
if not self.blocking and errno == 11:
pass
else:
@ -322,7 +331,7 @@ class IRCClient:
for el in data:
tags, prefix, command, args = parse_raw_irc_command(el)
#print(tags, prefix, command, args)
# print(tags, prefix, command, args)
try:
# Only need tags with tagmsg
if command.upper() == "TAGMSG":
@ -339,7 +348,7 @@ class IRCClient:
except (OSError, ssl.SSLEOFError) as se:
PchumLog.debug("problem: %s" % (str(se)))
if self.socket:
PchumLog.info('error: closing socket')
PchumLog.info("error: closing socket")
self.socket.close()
raise se
except Exception as e:
@ -347,27 +356,33 @@ class IRCClient:
raise e
else:
PchumLog.debug("ending while, end is %s" % self._end)
if self.socket:
PchumLog.info('finished: closing socket')
if self.socket:
PchumLog.info("finished: closing socket")
self.socket.close()
yield False
def close(self):
# with extreme prejudice
if self.socket:
PchumLog.info('shutdown socket')
#print("shutdown socket")
PchumLog.info("shutdown socket")
# print("shutdown socket")
self._end = True
try:
self.socket.shutdown(socket.SHUT_RDWR)
except OSError as e:
PchumLog.debug("Error while shutting down socket, already broken? %s" % str(e))
PchumLog.debug(
"Error while shutting down socket, already broken? %s" % str(e)
)
try:
self.socket.close()
except OSError as e:
PchumLog.debug("Error while closing socket, already broken? %s" % str(e))
PchumLog.debug(
"Error while closing socket, already broken? %s" % str(e)
)
class IRCApp:
""" This class manages several IRCClient instances without the use of threads.
"""This class manages several IRCClient instances without the use of threads.
(Non-threaded) Timer functionality is also included.
"""
@ -384,27 +399,27 @@ class IRCApp:
self.sleep_time = 0.5
def addClient(self, client, autoreconnect=False):
""" add a client object to the application. setting autoreconnect
"""add a client object to the application. setting autoreconnect
to true will mean the application will attempt to reconnect the client
after every disconnect. you can also set autoreconnect to a number
after every disconnect. you can also set autoreconnect to a number
to specify how many reconnects should happen.
warning: if you add a client that has blocking set to true,
timers will no longer function properly """
PchumLog.info('added client %s (ar=%s)' % (client, autoreconnect))
timers will no longer function properly"""
PchumLog.info("added client %s (ar=%s)" % (client, autoreconnect))
self._clients[client] = self._ClientDesc(autoreconnect=autoreconnect)
def addTimer(self, seconds, cb):
""" add a timed callback. accuracy is not specified, you can only
"""add a timed callback. accuracy is not specified, you can only
garuntee the callback will be called after seconds has passed.
( the only advantage to these timers is they dont use threads )
"""
assert callable(cb)
PchumLog.info('added timer to call %s in %ss' % (cb, seconds))
PchumLog.info("added timer to call %s in %ss" % (cb, seconds))
self._timers.append((time.time() + seconds, cb))
def run(self):
""" run the application. this will block until stop() is called """
"""run the application. this will block until stop() is called"""
# TODO: convert this to use generators too?
self.running = True
while self.running:
@ -413,42 +428,38 @@ class IRCApp:
for client, clientdesc in self._clients.items():
if clientdesc.con is None:
clientdesc.con = client.connect()
try:
next(clientdesc.con)
except Exception as e:
PchumLog.error('client error %s' % str(e))
PchumLog.error("client error %s" % str(e))
PchumLog.error(traceback.format_exc())
if clientdesc.autoreconnect:
clientdesc.con = None
clientdesc.con = None
if isinstance(clientdesc.autoreconnect, (int, float)):
clientdesc.autoreconnect -= 1
found_one_alive = True
else:
clientdesc.con = False
clientdesc.con = False
else:
found_one_alive = True
if not found_one_alive:
PchumLog.info('nothing left alive... quiting')
self.stop()
PchumLog.info("nothing left alive... quiting")
self.stop()
now = time.time()
timers = self._timers[:]
self._timers = []
for target_time, cb in timers:
if now > target_time:
PchumLog.info('calling timer cb %s' % cb)
PchumLog.info("calling timer cb %s" % cb)
cb()
else:
else:
self._timers.append((target_time, cb))
time.sleep(self.sleep_time)
def stop(self):
""" stop the application """
"""stop the application"""
self.running = False

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,46 +118,52 @@ class CommandHandler(object):
"""The default handler for commands. Override this method to
apply custom behavior (example, printing) unhandled commands.
"""
PchumLog.debug('unhandled command %s(%s)' % (cmd, args))
PchumLog.debug("unhandled command %s(%s)" % (cmd, args))
class DefaultCommandHandler(CommandHandler):
""" CommandHandler that provides methods for the normal operation of IRC.
"""CommandHandler that provides methods for the normal operation of IRC.
If you want your bot to properly respond to pings, etc, you should subclass this.
"""
def ping(self, prefix, server):
self.client.send('PONG', server)
self.client.send("PONG", server)
class DefaultBotCommandHandler(CommandHandler):
""" default command handler for bots. methods/attributes are made
available as commands """
"""default command handler for bots. methods/attributes are made
available as commands"""
@protected
def getVisibleCommands(self, obj=None):
test = (lambda x: isinstance(x, CommandHandler) or \
inspect.ismethod(x) or inspect.isfunction(x))
members = inspect.getmembers(obj or self, test)
return [m for m, _ in members
if (not m.startswith('_') and
not hasattr(getattr(obj, m), 'protected'))]
test = (
lambda x: isinstance(x, CommandHandler)
or inspect.ismethod(x)
or inspect.isfunction(x)
)
members = inspect.getmembers(obj or self, test)
return [
m
for m, _ in members
if (not m.startswith("_") and not hasattr(getattr(obj, m), "protected"))
]
def help(self, sender, dest, arg=None):
"""list all available commands or get help on a specific command"""
PchumLog.info('help sender=%s dest=%s arg=%s' % (sender, dest, arg))
PchumLog.info("help sender=%s dest=%s arg=%s" % (sender, dest, arg))
if not arg:
commands = self.getVisibleCommands()
commands.sort()
helpers.msg(self.client, dest,
"available commands: %s" % " ".join(commands))
helpers.msg(
self.client, dest, "available commands: %s" % " ".join(commands)
)
else:
try:
f = self.get(arg)
except CommandError as e:
helpers.msg(self.client, dest, str(e))
return
doc = f.__doc__.strip() if f.__doc__ else "No help available"
if not inspect.ismethod(f):
@ -159,11 +171,11 @@ class DefaultBotCommandHandler(CommandHandler):
if subcommands:
doc += " [sub commands: %s]" % " ".join(subcommands)
helpers.msg(self.client, dest, "%s: %s" % (arg, doc))
helpers.msg(self.client, dest, "%s: %s" % (arg, doc))
class BotCommandHandler(DefaultCommandHandler):
""" complete command handler for bots """
"""complete command handler for bots"""
def __init__(self, client, command_handler):
DefaultCommandHandler.__init__(self, client)
@ -174,22 +186,22 @@ class BotCommandHandler(DefaultCommandHandler):
@protected
def tryBotCommand(self, prefix, dest, msg):
""" tests a command to see if its a command for the bot, returns True
"""tests a command to see if its a command for the bot, returns True
and calls self.processBotCommand(cmd, sender) if its is.
"""
PchumLog.debug("tryBotCommand('%s' '%s' '%s')" % (prefix, dest, msg))
if dest == self.client.nick:
dest = parse_nick(prefix)[0]
elif msg.startswith(self.client.nick):
msg = msg[len(self.client.nick)+1:]
else:
msg = msg[len(self.client.nick) + 1 :]
else:
return False
msg = msg.strip()
parts = msg.split(' ', 1)
parts = msg.split(" ", 1)
command = parts[0]
arg = parts[1:]
@ -198,13 +210,3 @@ class BotCommandHandler(DefaultCommandHandler):
except CommandError as e:
helpers.msg(self.client, dest, str(e))
return True

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=="":
# Space breaks protocol if msg is absent
if msg == "":
cli.send("PRIVMSG", handle, "\x01%s\x01" % (cmd))
else:
cli.send("PRIVMSG", handle, "\x01%s %s\x01" % (cmd, msg))
@ -63,77 +69,96 @@ def ctcp(cli, handle, cmd, msg=""):
def ctcp_reply(cli, handle, cmd, msg=""):
notice(cli, str(handle), "\x01%s %s\x01" % (cmd.upper(), msg))
def metadata(cli, target, subcommand, *params):
# IRC metadata draft specification
# https://gist.github.com/k4bek4be/92c2937cefd49990fbebd001faf2b237
cli.send("METADATA", target, subcommand, *params)
def cap(cli, subcommand, *params):
# Capability Negotiation
# https://ircv3.net/specs/extensions/capability-negotiation.html
cli.send("CAP", subcommand, *params)
def msgrandom(cli, choices, dest, user=None):
o = "%s: " % user if user else ""
o += random.choice(choices)
msg(cli, dest, o)
def _makeMsgRandomFunc(choices):
def func(cli, dest, user=None):
msgrandom(cli, choices, dest, user)
return func
msgYes = _makeMsgRandomFunc(['yes', 'alright', 'ok'])
msgOK = _makeMsgRandomFunc(['ok', 'done'])
msgNo = _makeMsgRandomFunc(['no', 'no-way'])
msgYes = _makeMsgRandomFunc(["yes", "alright", "ok"])
msgOK = _makeMsgRandomFunc(["ok", "done"])
msgNo = _makeMsgRandomFunc(["no", "no-way"])
def ns(cli, *args):
msg(cli, "NickServ", " ".join(args))
def cs(cli, *args):
msg(cli, "ChanServ", " ".join(args))
def identify(cli, passwd, authuser="NickServ"):
msg(cli, authuser, "IDENTIFY %s" % passwd)
def quit(cli, msg):
cli.send("QUIT %s" % (msg))
def user(cli, username, realname):
cli.send("USER",
username,
'0',
'*',
':' + realname)
cli.send("USER", username, "0", "*", ":" + realname)
_simple = (
'join',
'part',
'nick',
'notice',
'invite',
"join",
"part",
"nick",
"notice",
"invite",
)
def _addsimple():
import sys
def simplecmd(cmd_name):
def f(cli, *args):
cli.send(cmd_name, *args)
return f
m = sys.modules[__name__]
for t in _simple:
setattr(m, t, simplecmd(t.upper()))
_addsimple()
def _addNumerics():
import sys
from oyoyo import ircevents
def numericcmd(cmd_num, cmd_name):
def f(cli, *args):
cli.send(cmd_num, *args)
return f
m = sys.modules[__name__]
for num, name in ircevents.numeric_events.items():
setattr(m, name, numericcmd(num, name))
_addNumerics()

View file

@ -116,7 +116,7 @@ numeric_events = {
"374": "endofinfo",
"375": "motdstart",
"376": "endofmotd",
"377": "motd2", # 1997-10-16 -- tkil
"377": "motd2", # 1997-10-16 -- tkil
"381": "youreoper",
"382": "rehashing",
"384": "myportis",
@ -143,7 +143,7 @@ numeric_events = {
"423": "noadmininfo",
"424": "fileerror",
"431": "nonicknamegiven",
"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
"433": "nicknameinuse",
"436": "nickcollision",
"437": "unavailresource", # "Nick temporally unavailable"
@ -158,7 +158,7 @@ numeric_events = {
"462": "alreadyregistered",
"463": "nopermforhost",
"464": "passwdmismatch",
"465": "yourebannedcreep", # I love this one...
"465": "yourebannedcreep", # I love this one...
"466": "youwillbebanned",
"467": "keyset",
"471": "channelisfull",
@ -172,7 +172,7 @@ numeric_events = {
"481": "noprivileges",
"482": "chanoprivsneeded",
"483": "cantkillserver",
"484": "restricted", # Connection is restricted
"484": "restricted", # Connection is restricted
"485": "uniqopprivsneeded",
"491": "nooperhost",
"492": "noservicehost",
@ -195,7 +195,7 @@ metadata_numeric_events = {
"768": "keynotset",
"769": "keynopermission",
"770": "metadatasubok",
}
}
numeric_events.update(metadata_numeric_events)
generated_events = [
@ -226,10 +226,7 @@ protocol_events = [
"nick", # We can get svsnicked
"metadata", # Metadata specification
"tagmsg", # IRCv3 message tags extension
"cap" # IRCv3 Client Capability Negotiation
"cap", # IRCv3 Client Capability Negotiation
]
all_events = (generated_events
+ protocol_events
+ list(numeric_events.values()))
all_events = generated_events + protocol_events + list(numeric_events.values())

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):
"""
@ -55,20 +56,20 @@ def parse_raw_irc_command(element):
"""
try:
element = element.decode("utf-8")
except UnicodeDecodeError as e:
PchumLog.debug("utf-8 error %s" % str(e))
element = element.decode("latin-1", 'replace')
element = element.decode("latin-1", "replace")
parts = element.strip().split(" ")
if parts[0].startswith(':'):
if parts[0].startswith(":"):
tags = None
prefix = parts[0][1:]
command = parts[1]
args = parts[2:]
elif parts[0].startswith('@'):
elif parts[0].startswith("@"):
# Message tag
tags = parts[0]
prefix = parts[1][1:]
@ -84,14 +85,14 @@ def parse_raw_irc_command(element):
try:
command = numeric_events[command]
except KeyError:
PchumLog.info('unknown numeric event %s' % command)
PchumLog.info("unknown numeric event %s" % command)
command = command.lower()
if args[0].startswith(':'):
if args[0].startswith(":"):
args = [" ".join(args)[1:]]
else:
for idx, arg in enumerate(args):
if arg.startswith(':'):
if arg.startswith(":"):
args = args[:idx] + [" ".join(args[idx:])[1:]]
break
@ -99,23 +100,22 @@ def parse_raw_irc_command(element):
def parse_nick(name):
""" parse a nickname and return a tuple of (nick, mode, user, host)
"""parse a nickname and return a tuple of (nick, mode, user, host)
<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,84 +202,104 @@ class imagelink(lexercon.Chunk):
else:
return ""
class memolex(lexercon.Chunk):
def __init__(self, string, space, channel):
self.string = string
self.space = space
self.channel = channel
def convert(self, format):
if format == "html":
return "%s<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),
(colorEnd, _ctag_end),
# karxi: Disabled this for now. No common versions of Pesterchum
# actually use it, save for Chumdroid...which shouldn't.
# When I change out parsers, I might add it back in.
##(formatBegin, _format_begin), (formatEnd, _format_end),
(imagelink, _imgre),
(hyperlink, _urlre),
(memolex, _memore),
(chumhandlelex, _handlere),
(smiley, _smilere),
(honker, _honk)]
lexlist = [
(mecmd, _mecmdre),
(colorBegin, _ctag_begin),
(colorBegin, _gtag_begin),
(colorEnd, _ctag_end),
# karxi: Disabled this for now. No common versions of Pesterchum
# actually use it, save for Chumdroid...which shouldn't.
# When I change out parsers, I might add it back in.
##(formatBegin, _format_begin), (formatEnd, _format_end),
(imagelink, _imgre),
(hyperlink, _urlre),
(memolex, _memore),
(chumhandlelex, _handlere),
(smiley, _smilere),
(honker, _honk),
]
string = str(string)
string = string.replace("\n", " ").replace("\r", " ")
@ -282,14 +321,15 @@ def lexMessage(string):
else:
balanced.append(o)
if beginc > endc:
for i in range(0, beginc-endc):
for i in range(0, beginc - endc):
balanced.append(colorEnd("</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.
@ -330,7 +373,7 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None):
# Since we should always be able to fetch this
# karxi: ... Which we can't, right now, unlike in the old script.
# TODO: Resolve this issue, give it the necessary information.
# If we CAN'T, stick with a length of 30, since that seems to be
# the average maximum nowadays
limit -= len(nick) if nick is not None else 30
@ -339,7 +382,7 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None):
# ident length
limit -= len(ident) if nick is not None else 10
# Maximum (?) host length
limit -= 63 # RFC 2812
limit -= 63 # RFC 2812
# The target is the place this is getting sent to - a channel or a nick
if target is not None:
limit -= len(target)
@ -351,10 +394,11 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None):
return limit
def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
"""Split messages so that they don't go over the length limit.
Returns a list of the messages, neatly split.
Keep in mind that there's a little bit of magic involved in this at the
moment; some unsafe assumptions are made."""
@ -384,11 +428,21 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
curlen = 0
# Maximum number of characters *to* use.
if not maxlen:
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname)
maxlen = _max_msg_len(
None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname
)
elif maxlen < 0:
# Subtract the (negative) length, giving us less leeway in this
# function.
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname) + maxlen
maxlen = (
_max_msg_len(
None,
None,
ctx.mainwindow.profile().handle,
ctx.mainwindow.irc.cli.realname,
)
+ maxlen
)
# Defined here, but modified in the loop.
msglen = 0
@ -398,7 +452,7 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
tags that will be needed."""
return maxlen - curlen - (len(open_ctags) * 4)
#safekeeping = lexed[:]
# safekeeping = lexed[:]
lexed = collections.deque(lexed)
rounds = 0
# NOTE: This entire mess is due for a rewrite. I'll start splitting it into
@ -448,14 +502,14 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
# instead?
subround += 1
if debug:
PchumLog.info("[Splitting round {}-{}...]".format(
rounds, subround
))
point = msg.rfind(' ', 0, lenl)
PchumLog.info(
"[Splitting round {}-{}...]".format(rounds, subround)
)
point = msg.rfind(" ", 0, lenl)
if point < 0:
# No spaces to break on...ugh. Break at the last space
# we can instead.
point = lenl ## - 1
point = lenl ## - 1
# NOTE: The - 1 is for safety (but probably isn't
# actually necessary.)
# Split and push what we have.
@ -509,16 +563,20 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
cte = lexercon.CTagEnd("</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
@ -680,7 +736,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
# I'm pretty sure that putting a space before a /me *should* break the
# /me, but in practice, that's not the case.
is_action = msg.startswith("/me")
# Begin message processing.
# We use 'text' despite its lack of processing because it's simpler.
if should_quirk and not (is_action or is_ooc):
@ -706,8 +762,8 @@ def kxhandleInput(ctx, text=None, flavor=None):
msgbox.setInformativeText(err_info)
msgbox.exec()
return
PchumLog.info("--> recv \"%s\"" % msg)
PchumLog.info('--> recv "%s"' % msg)
# karxi: We have a list...but I'm not sure if we ever get anything else, so
# best to play it safe. I may remove this during later refactoring.
if isinstance(msg, list):
@ -718,23 +774,21 @@ def kxhandleInput(ctx, text=None, flavor=None):
# an object type I provided - just so I could pluck them out
# later.
msg[i] = m.convert(format="ctag")
msg = ''.join(msg)
msg = "".join(msg)
# Quirks have been applied. Lex the messages (finally).
msg = kxlexMsg(msg)
# Debug output.
#try:
# try:
# print(repr(msg))
#except Exception as err:
# except Exception as err:
# print("(Couldn't print lexed message: {!s})".format(err))
# Remove coloring if this is a /me!
if is_action:
# Filter out formatting specifiers (just ctags, at the moment).
msg = [m for m in msg if not isinstance(m,
(lexercon.CTag, lexercon.CTagEnd)
)]
msg = [m for m in msg if not isinstance(m, (lexercon.CTag, lexercon.CTagEnd))]
# We'll also add /me to the beginning of any new messages, later.
# Put what's necessary in before splitting.
@ -752,9 +806,11 @@ def kxhandleInput(ctx, text=None, flavor=None):
# We'll use those later.
# Split the messages so we don't go over the buffer and lose text.
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname)
# ctx.mainwindow.profile().handle ==> Get handle
# ctx.mainwindow.irc.cli.realname ==> Get ident (Same as realname in this case.)
maxlen = _max_msg_len(
None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname
)
# ctx.mainwindow.profile().handle ==> Get handle
# ctx.mainwindow.irc.cli.realname ==> Get ident (Same as realname in this case.)
# Since we have to do some post-processing, we need to adjust the maximum
# length we can use.
if flavor == "convo":
@ -775,10 +831,8 @@ def kxhandleInput(ctx, text=None, flavor=None):
# Pester message handling.
if flavor == "convo":
# if ceased, rebegin
if hasattr(ctx, 'chumopen') and not ctx.chumopen:
ctx.mainwindow.newConvoStarted.emit(
QString(ctx.title()), True
)
if hasattr(ctx, "chumopen") and not ctx.chumopen:
ctx.mainwindow.newConvoStarted.emit(QString(ctx.title()), True)
ctx.setChumOpen(True)
# Post-process and send the messages.
@ -804,11 +858,10 @@ def kxhandleInput(ctx, text=None, flavor=None):
# construct the messages.
clientMsg = "<c={1}>{2}{3}{4}: {0}</c>".format(
clientMsg, colorcmd, grammar.pcf, initials, grammar.number
)
clientMsg, colorcmd, grammar.pcf, initials, grammar.number
)
# Not sure if this needs a space at the end...?
serverMsg = "<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,31 +896,32 @@ def timeProtocol(cmd):
try:
l = [int(x) for x in cmd.split(":")]
except ValueError:
l = [0,0]
timed = timedelta(0, l[0]*3600+l[1]*60)
l = [0, 0]
timed = timedelta(0, l[0] * 3600 + l[1] * 60)
if dir == "P":
timed = timed*-1
timed = timed * -1
return timed
def timeDifference(td):
if td == timedelta(microseconds=1): # mysteryTime replacement :(
if td == timedelta(microseconds=1): # mysteryTime replacement :(
return "??:?? FROM ????"
if td < timedelta(0):
when = "AGO"
else:
when = "FROM NOW"
atd = abs(td)
minutes = (atd.days*86400 + atd.seconds) // 60
minutes = (atd.days * 86400 + atd.seconds) // 60
hours = minutes // 60
leftoverminutes = minutes % 60
if atd == timedelta(0):
timetext = "RIGHT NOW"
elif atd < timedelta(0,3600):
elif atd < timedelta(0, 3600):
if minutes == 1:
timetext = "%d MINUTE %s" % (minutes, when)
else:
timetext = "%d MINUTES %s" % (minutes, when)
elif atd < timedelta(0,3600*100):
elif atd < timedelta(0, 3600 * 100):
if hours == 1 and leftoverminutes == 0:
timetext = "%d:%02d HOUR %s" % (hours, leftoverminutes, when)
else:
@ -869,16 +930,20 @@ def timeDifference(td):
timetext = "%d HOURS %s" % (hours, when)
return timetext
def nonerep(text):
return text
class parseLeaf(object):
def __init__(self, function, parent):
self.nodes = []
self.function = function
self.parent = parent
def append(self, node):
self.nodes.append(node)
def expand(self, mo):
out = ""
for n in self.nodes:
@ -891,12 +956,15 @@ class parseLeaf(object):
out = self.function(out)
return out
class backreference(object):
def __init__(self, number):
self.number = number
def __str__(self):
return self.number
def parseRegexpFunctions(to):
parsed = parseLeaf(nonerep, None)
current = parsed
@ -907,7 +975,7 @@ def parseRegexpFunctions(to):
mo = _functionre.search(tmp)
if mo is not None:
if mo.start() > 0:
current.append(to[curi:curi+mo.start()])
current.append(to[curi : curi + mo.start()])
backr = _groupre.search(mo.group())
if backr is not None:
current.append(backreference(backr.group(1)))
@ -920,7 +988,7 @@ def parseRegexpFunctions(to):
current = current.parent
else:
current.append(")")
curi = mo.end()+curi
curi = mo.end() + curi
else:
current.append(to[curi:])
curi = len(to)
@ -929,11 +997,14 @@ def parseRegexpFunctions(to):
def img2smiley(string):
string = str(string)
def imagerep(mo):
return reverse_smiley[mo.group(1)]
string = re.sub(r'<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

@ -5,4 +5,4 @@
##from __future__ import division
##from __future__ import absolute_import # JUST in case.
# No __all__ or similar, for now.
# No __all__ or similar, for now.

View file

@ -4,29 +4,31 @@
class AttrDict(dict):
"""A dictionary with attribute-style access. It maps attribute access to
the real dictionary.
Note that accesses to preexisting (e.g. class inherited) or reserved
attributes are handled as they would be normally, and will not be
overwritten.
Overload _is_reserved if you want to change this."""
def __init__(self, init={}):
super(AttrDict, self).__init__(init)
def __getstate__(self):
return list(self.__dict__.items())
def __setstate__(self, items):
for key, val in items: self.__dict__[key] = val
for key, val in items:
self.__dict__[key] = val
def __repr__(self):
return "{0}({1})".format(
type(self).__name__,
super(AttrDict, self).__repr__()
)
return "{0}({1})".format(type(self).__name__, super(AttrDict, self).__repr__())
def __setitem__(self, name, value):
return super(AttrDict, self).__setitem__(name, value)
def __getitem__(self, name):
return super(AttrDict, self).__getitem__(name)
def __delitem__(self, name):
return super(AttrDict, self).__delitem__(name)
@ -78,7 +80,8 @@ class AttrDict(dict):
# See __setattr__.
return object.__delattr__(self, name)
else:
try: del self[name]
try:
del self[name]
except KeyError as err:
raise AttributeError(str(err))
@ -86,13 +89,18 @@ class AttrDict(dict):
def _is_reserved(name):
"""Check if an attribute name is reserved for system use."""
# A very simple method.
result = name[:2] == name[-2:] == '__'
result = name[:2] == name[-2:] == "__"
return result
def copy(self): return type(self)(self)
def copy(self):
return type(self)(self)
__copy__ = copy
## end of http://code.activestate.com/recipes/473786/ }}}
class DefAttrDict(AttrDict):
default_factory = None
@ -106,8 +114,8 @@ class DefAttrDict(AttrDict):
self.default_factory,
# We skip normal processing here, since AttrDict provides basic
# repr for classes in general, which we don't want.
dict.__repr__(self)
)
dict.__repr__(self),
)
def __getitem__(self, name):
try:
@ -130,7 +138,10 @@ class DefAttrDict(AttrDict):
raise
return result
def copy(self): return type(self)(self.default_factory, self)
def copy(self):
return type(self)(self.default_factory, self)
__copy__ = copy
# vim: set autoindent ts=4 sts=4 sw=4 textwidth=79 expandtab:

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)
@ -281,11 +329,11 @@ class Pesterchum(Lexer):
def lex(self, string):
lexlist = [
##(mecmd, self._mecmdre),
(CTag, self._ctag_begin),
##(CTag, self._ctag_end)
(CTagEnd, self._ctag_end)
]
##(mecmd, self._mecmdre),
(CTag, self._ctag_begin),
##(CTag, self._ctag_end)
(CTagEnd, self._ctag_end),
]
lexed = self.breakdown(string, lexlist)
@ -324,7 +372,7 @@ class Pesterchum(Lexer):
## balanced.append(o)
# This will need to be re-evaluated to support the line end lexeme/etc.
if beginc > endc:
for i in range(0, beginc-endc):
for i in range(0, beginc - endc):
##balanced.append(colorEnd("</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,8 +487,8 @@ class RelayChat(Lexer):
lexlist = [
(CTag, self._ccode_rxp),
(CTagEnd, self._ccode_end_rxp),
(ResetTag, self._reset_rxp)
]
(ResetTag, self._reset_rxp),
]
lexed = self.breakdown(string, lexlist)
@ -444,7 +496,8 @@ class RelayChat(Lexer):
return lexed
def list_convert(self, target, format=None):
if format is None: format = self.ref
if format is None:
format = self.ref
converted = []
cstack = []
@ -525,98 +578,99 @@ class RelayChat(Lexer):
return converted
def _list_convert_new(self, target, format=None):
if format is None: format = self.ref
converted = []
cstack = []
if format is None:
format = self.ref
converted = []
cstack = []
for elt in target:
if isinstance(elt, LineColorEnd):
# Go down the stack until we have a line color TO end
while cstack:
# Add a </c> since we'll need one anyway
for elt in target:
if isinstance(elt, LineColorEnd):
# Go down the stack until we have a line color TO end
while cstack:
# Add a </c> since we'll need one anyway
# Is closecolor accessible here?
try:
closecolor()
except Exception as e:
print(e)
##if isinstance(color, LineColor):
if isinstance(cstack.pop(), LineColor):
# We found what we wanted, and the color
# was already popped from the stack, so
# we're good
# Breaking here allows it to be appended
break
continue
elif isinstance(elt, ResetTag):
# If it says reset, reset - which means go down the
# stack to the most recent line color.
while cstack:
color = cstack[-1]
if not isinstance(color, LineColor):
# It's not a line color, so remove it
del cstack[-1]
# Add a </c>
# Is closecolor accessible here?
try:
closecolor()
except Exception as e:
print(e)
##if isinstance(color, LineColor):
if isinstance(cstack.pop(), LineColor):
# We found what we wanted, and the color
# was already popped from the stack, so
# we're good
# Breaking here allows it to be appended
break
continue
elif isinstance(elt, ResetTag):
# If it says reset, reset - which means go down the
# stack to the most recent line color.
while cstack:
color = cstack[-1]
if not isinstance(color, LineColor):
# It's not a line color, so remove it
del cstack[-1]
# Add a </c>
# Is closecolor accessible here?
try:
closecolor()
except Exception as e:
print(e)
else:
# It's a line color, so stop searching.
# Using break here prevents the 'else'
# clause of this while statement from
# executing.
break
else:
# We don't have any more entries in the stack;
# just continue.
continue
## We found the line color, so add it and continue
##converted.append(color.convert(format))
# It's a line color, so stop searching.
# Using break here prevents the 'else'
# clause of this while statement from
# executing.
break
else:
# We don't have any more entries in the stack;
# just continue.
continue
## TODO: Make this actually add the reset char
# The above shouldn't be necessary because this is Pesterchum's
# format, not IRC's
elif isinstance(elt, CTagEnd):
try:
## We found the line color, so add it and continue
##converted.append(color.convert(format))
continue
## TODO: Make this actually add the reset char
# The above shouldn't be necessary because this is Pesterchum's
# format, not IRC's
elif isinstance(elt, CTagEnd):
try:
color = cstack[-1]
# Remove the oldest color, the one we're exiting
if not isinstance(color, LineColor):
# If we got here, we don't have a line color,
# so we're free to act as usual
cstack.pop()
# Fetch the current nested color
color = cstack[-1]
# Remove the oldest color, the one we're exiting
if not isinstance(color, LineColor):
# If we got here, we don't have a line color,
# so we're free to act as usual
cstack.pop()
# Fetch the current nested color
color = cstack[-1]
else:
# We have a line color and the current lexeme
# is NOT a line color end; don't even bother
# adding it to the processed result
continue
except LookupError:
# We aren't nested in a color anymore
# Passing here causes us to fall through to normal
# handling
pass
# Not necessary due to Pesterchum's format
##else:
## # We're still nested....
## ##converted.append(elt.convert(format))
## converted.append(color.convert(format))
## # We already added to the working list, so just
## # skip the rest
## continue
elif isinstance(elt, CTag):
# Push the color onto the stack - we're nested in it now
cstack.append(elt)
# Falling through adds it to the converted result
else:
# We have a line color and the current lexeme
# is NOT a line color end; don't even bother
# adding it to the processed result
continue
except LookupError:
# We aren't nested in a color anymore
# Passing here causes us to fall through to normal
# handling
pass
# Not necessary due to Pesterchum's format
##else:
## # We're still nested....
## ##converted.append(elt.convert(format))
## converted.append(color.convert(format))
## # We already added to the working list, so just
## # skip the rest
## continue
elif isinstance(elt, CTag):
# Push the color onto the stack - we're nested in it now
cstack.append(elt)
# Falling through adds it to the converted result
if isinstance(elt, Lexeme):
elt = elt.convert(format)
elif not isinstance(elt, basestr):
# Tempted to make this toss an error, but for now, we'll be
# safe and make it convert to str
elt = str(elt)
converted.append(elt)
return converted
if isinstance(elt, Lexeme):
elt = elt.convert(format)
elif not isinstance(elt, basestr):
# Tempted to make this toss an error, but for now, we'll be
# safe and make it convert to str
elt = str(elt)
converted.append(elt)
return converted

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,228 +388,232 @@ class Color(object):
elif strlen == 3:
# We have a short (CSS style) code; duplicate all of the characters
hexstr = [c + c for c in hexstr]
hexstr = ''.join(hexstr)
hexstr = "".join(hexstr)
else:
raise ValueError(
"Invalid hexadecimal value provided: %s" % orig
)
raise ValueError("Invalid hexadecimal value provided: %s" % orig)
try:
# Make sure it works/is readable (no invalid characters).
int(hexstr, 16)
except ValueError:
raise ValueError(
"Invalid hexadecimal value provided: %s" % orig
)
return '#' + hexstr
raise ValueError("Invalid hexadecimal value provided: %s" % orig)
return "#" + hexstr
def to_cielab_tuple(self):
# For now, just return the stored CIELAB tuple
return self.cielab
def to_rgb_tuple(self): return (self.red, self.green, self.blue)
def to_rgb_tuple(self):
return (self.red, self.green, self.blue)
# 2012-12-05T17:40:39-07:00: Changed 'self.blue' to 'self.z' like it SHOULD
# have been in the FIRST place. Ugh. How did I fuck THAT one up?
def to_xyz_tuple(self): return (self.x, self.y, self.z)
def to_xyz_tuple(self):
return (self.x, self.y, self.z)
# All of these are effectively equivalent to the Qt-provided colors, so they
# could be phased out - but there's no need to, yet.
_svg_colors = AttrDict()
_irc_colors = {}
_svg_colors.update({
"aliceblue": Color(240, 248, 255),
"antiquewhite": Color(250, 235, 215),
"aqua": Color( 0, 255, 255),
"aquamarine": Color(127, 255, 212),
"azure": Color(240, 255, 255),
"beige": Color(245, 245, 220),
"bisque": Color(255, 228, 196),
"black": Color( 0, 0, 0),
"blanchedalmond": Color(255, 235, 205),
"blue": Color( 0, 0, 255),
"blueviolet": Color(138, 43, 226),
"brown": Color(165, 42, 42),
"burlywood": Color(222, 184, 135),
"cadetblue": Color( 95, 158, 160),
"chartreuse": Color(127, 255, 0),
"chocolate": Color(210, 105, 30),
"coral": Color(255, 127, 80),
"cornflowerblue": Color(100, 149, 237),
"cornsilk": Color(255, 248, 220),
"crimson": Color(220, 20, 60),
"cyan": Color( 0, 255, 255),
"darkblue": Color( 0, 0, 139),
"darkcyan": Color( 0, 139, 139),
"darkgoldenrod": Color(184, 134, 11),
"darkgray": Color(169, 169, 169),
"darkgreen": Color( 0, 100, 0),
"darkgrey": Color(169, 169, 169),
"darkkhaki": Color(189, 183, 107),
"darkmagenta": Color(139, 0, 139),
"darkolivegreen": Color( 85, 107, 47),
"darkorange": Color(255, 140, 0),
"darkorchid": Color(153, 50, 204),
"darkred": Color(139, 0, 0),
"darksalmon": Color(233, 150, 122),
"darkseagreen": Color(143, 188, 143),
"darkslateblue": Color( 72, 61, 139),
"darkslategray": Color( 47, 79, 79),
"darkslategrey": Color( 47, 79, 79),
"darkturquoise": Color( 0, 206, 209),
"darkviolet": Color(148, 0, 211),
"deeppink": Color(255, 20, 147),
"deepskyblue": Color( 0, 191, 255),
"dimgray": Color(105, 105, 105),
"dimgrey": Color(105, 105, 105),
"dodgerblue": Color( 30, 144, 255),
"firebrick": Color(178, 34, 34),
"floralwhite": Color(255, 250, 240),
"forestgreen": Color( 34, 139, 34),
"fuchsia": Color(255, 0, 255),
"gainsboro": Color(220, 220, 220),
"ghostwhite": Color(248, 248, 255),
"gold": Color(255, 215, 0),
"goldenrod": Color(218, 165, 32),
"gray": Color(128, 128, 128),
"grey": Color(128, 128, 128),
"green": Color( 0, 128, 0),
"greenyellow": Color(173, 255, 47),
"honeydew": Color(240, 255, 240),
"hotpink": Color(255, 105, 180),
"indianred": Color(205, 92, 92),
"indigo": Color( 75, 0, 130),
"ivory": Color(255, 255, 240),
"khaki": Color(240, 230, 140),
"lavender": Color(230, 230, 250),
"lavenderblush": Color(255, 240, 245),
"lawngreen": Color(124, 252, 0),
"lemonchiffon": Color(255, 250, 205),
"lightblue": Color(173, 216, 230),
"lightcoral": Color(240, 128, 128),
"lightcyan": Color(224, 255, 255),
"lightgoldenrodyellow": Color(250, 250, 210),
"lightgray": Color(211, 211, 211),
"lightgreen": Color(144, 238, 144),
"lightgrey": Color(211, 211, 211),
"lightpink": Color(255, 182, 193),
"lightsalmon": Color(255, 160, 122),
"lightseagreen": Color( 32, 178, 170),
"lightskyblue": Color(135, 206, 250),
"lightslategray": Color(119, 136, 153),
"lightslategrey": Color(119, 136, 153),
"lightsteelblue": Color(176, 196, 222),
"lightyellow": Color(255, 255, 224),
"lime": Color( 0, 255, 0),
"limegreen": Color( 50, 205, 50),
"linen": Color(250, 240, 230),
"magenta": Color(255, 0, 255),
"maroon": Color(128, 0, 0),
"mediumaquamarine": Color(102, 205, 170),
"mediumblue": Color( 0, 0, 205),
"mediumorchid": Color(186, 85, 211),
"mediumpurple": Color(147, 112, 219),
"mediumseagreen": Color( 60, 179, 113),
"mediumslateblue": Color(123, 104, 238),
"mediumspringgreen": Color( 0, 250, 154),
"mediumturquoise": Color( 72, 209, 204),
"mediumvioletred": Color(199, 21, 133),
"midnightblue": Color( 25, 25, 112),
"mintcream": Color(245, 255, 250),
"mistyrose": Color(255, 228, 225),
"moccasin": Color(255, 228, 181),
"navajowhite": Color(255, 222, 173),
"navy": Color( 0, 0, 128),
"oldlace": Color(253, 245, 230),
"olive": Color(128, 128, 0),
"olivedrab": Color(107, 142, 35),
"orange": Color(255, 165, 0),
"orangered": Color(255, 69, 0),
"orchid": Color(218, 112, 214),
"palegoldenrod": Color(238, 232, 170),
"palegreen": Color(152, 251, 152),
"paleturquoise": Color(175, 238, 238),
"palevioletred": Color(219, 112, 147),
"papayawhip": Color(255, 239, 213),
"peachpuff": Color(255, 218, 185),
"peru": Color(205, 133, 63),
"pink": Color(255, 192, 203),
"plum": Color(221, 160, 221),
"powderblue": Color(176, 224, 230),
"purple": Color(128, 0, 128),
"red": Color(255, 0, 0),
"rosybrown": Color(188, 143, 143),
"royalblue": Color( 65, 105, 225),
"saddlebrown": Color(139, 69, 19),
"salmon": Color(250, 128, 114),
"sandybrown": Color(244, 164, 96),
"seagreen": Color( 46, 139, 87),
"seashell": Color(255, 245, 238),
"sienna": Color(160, 82, 45),
"silver": Color(192, 192, 192),
"skyblue": Color(135, 206, 235),
"slateblue": Color(106, 90, 205),
"slategray": Color(112, 128, 144),
"slategrey": Color(112, 128, 144),
"snow": Color(255, 250, 250),
"springgreen": Color( 0, 255, 127),
"steelblue": Color( 70, 130, 180),
"tan": Color(210, 180, 140),
"teal": Color( 0, 128, 128),
"thistle": Color(216, 191, 216),
"tomato": Color(255, 99, 71),
"turquoise": Color( 64, 224, 208),
"violet": Color(238, 130, 238),
"wheat": Color(245, 222, 179),
"white": Color(255, 255, 255),
"whitesmoke": Color(245, 245, 245),
"yellow": Color(255, 255, 0),
"yellowgreen": Color(154, 205, 50)
})
_svg_colors.update(
{
"aliceblue": Color(240, 248, 255),
"antiquewhite": Color(250, 235, 215),
"aqua": Color(0, 255, 255),
"aquamarine": Color(127, 255, 212),
"azure": Color(240, 255, 255),
"beige": Color(245, 245, 220),
"bisque": Color(255, 228, 196),
"black": Color(0, 0, 0),
"blanchedalmond": Color(255, 235, 205),
"blue": Color(0, 0, 255),
"blueviolet": Color(138, 43, 226),
"brown": Color(165, 42, 42),
"burlywood": Color(222, 184, 135),
"cadetblue": Color(95, 158, 160),
"chartreuse": Color(127, 255, 0),
"chocolate": Color(210, 105, 30),
"coral": Color(255, 127, 80),
"cornflowerblue": Color(100, 149, 237),
"cornsilk": Color(255, 248, 220),
"crimson": Color(220, 20, 60),
"cyan": Color(0, 255, 255),
"darkblue": Color(0, 0, 139),
"darkcyan": Color(0, 139, 139),
"darkgoldenrod": Color(184, 134, 11),
"darkgray": Color(169, 169, 169),
"darkgreen": Color(0, 100, 0),
"darkgrey": Color(169, 169, 169),
"darkkhaki": Color(189, 183, 107),
"darkmagenta": Color(139, 0, 139),
"darkolivegreen": Color(85, 107, 47),
"darkorange": Color(255, 140, 0),
"darkorchid": Color(153, 50, 204),
"darkred": Color(139, 0, 0),
"darksalmon": Color(233, 150, 122),
"darkseagreen": Color(143, 188, 143),
"darkslateblue": Color(72, 61, 139),
"darkslategray": Color(47, 79, 79),
"darkslategrey": Color(47, 79, 79),
"darkturquoise": Color(0, 206, 209),
"darkviolet": Color(148, 0, 211),
"deeppink": Color(255, 20, 147),
"deepskyblue": Color(0, 191, 255),
"dimgray": Color(105, 105, 105),
"dimgrey": Color(105, 105, 105),
"dodgerblue": Color(30, 144, 255),
"firebrick": Color(178, 34, 34),
"floralwhite": Color(255, 250, 240),
"forestgreen": Color(34, 139, 34),
"fuchsia": Color(255, 0, 255),
"gainsboro": Color(220, 220, 220),
"ghostwhite": Color(248, 248, 255),
"gold": Color(255, 215, 0),
"goldenrod": Color(218, 165, 32),
"gray": Color(128, 128, 128),
"grey": Color(128, 128, 128),
"green": Color(0, 128, 0),
"greenyellow": Color(173, 255, 47),
"honeydew": Color(240, 255, 240),
"hotpink": Color(255, 105, 180),
"indianred": Color(205, 92, 92),
"indigo": Color(75, 0, 130),
"ivory": Color(255, 255, 240),
"khaki": Color(240, 230, 140),
"lavender": Color(230, 230, 250),
"lavenderblush": Color(255, 240, 245),
"lawngreen": Color(124, 252, 0),
"lemonchiffon": Color(255, 250, 205),
"lightblue": Color(173, 216, 230),
"lightcoral": Color(240, 128, 128),
"lightcyan": Color(224, 255, 255),
"lightgoldenrodyellow": Color(250, 250, 210),
"lightgray": Color(211, 211, 211),
"lightgreen": Color(144, 238, 144),
"lightgrey": Color(211, 211, 211),
"lightpink": Color(255, 182, 193),
"lightsalmon": Color(255, 160, 122),
"lightseagreen": Color(32, 178, 170),
"lightskyblue": Color(135, 206, 250),
"lightslategray": Color(119, 136, 153),
"lightslategrey": Color(119, 136, 153),
"lightsteelblue": Color(176, 196, 222),
"lightyellow": Color(255, 255, 224),
"lime": Color(0, 255, 0),
"limegreen": Color(50, 205, 50),
"linen": Color(250, 240, 230),
"magenta": Color(255, 0, 255),
"maroon": Color(128, 0, 0),
"mediumaquamarine": Color(102, 205, 170),
"mediumblue": Color(0, 0, 205),
"mediumorchid": Color(186, 85, 211),
"mediumpurple": Color(147, 112, 219),
"mediumseagreen": Color(60, 179, 113),
"mediumslateblue": Color(123, 104, 238),
"mediumspringgreen": Color(0, 250, 154),
"mediumturquoise": Color(72, 209, 204),
"mediumvioletred": Color(199, 21, 133),
"midnightblue": Color(25, 25, 112),
"mintcream": Color(245, 255, 250),
"mistyrose": Color(255, 228, 225),
"moccasin": Color(255, 228, 181),
"navajowhite": Color(255, 222, 173),
"navy": Color(0, 0, 128),
"oldlace": Color(253, 245, 230),
"olive": Color(128, 128, 0),
"olivedrab": Color(107, 142, 35),
"orange": Color(255, 165, 0),
"orangered": Color(255, 69, 0),
"orchid": Color(218, 112, 214),
"palegoldenrod": Color(238, 232, 170),
"palegreen": Color(152, 251, 152),
"paleturquoise": Color(175, 238, 238),
"palevioletred": Color(219, 112, 147),
"papayawhip": Color(255, 239, 213),
"peachpuff": Color(255, 218, 185),
"peru": Color(205, 133, 63),
"pink": Color(255, 192, 203),
"plum": Color(221, 160, 221),
"powderblue": Color(176, 224, 230),
"purple": Color(128, 0, 128),
"red": Color(255, 0, 0),
"rosybrown": Color(188, 143, 143),
"royalblue": Color(65, 105, 225),
"saddlebrown": Color(139, 69, 19),
"salmon": Color(250, 128, 114),
"sandybrown": Color(244, 164, 96),
"seagreen": Color(46, 139, 87),
"seashell": Color(255, 245, 238),
"sienna": Color(160, 82, 45),
"silver": Color(192, 192, 192),
"skyblue": Color(135, 206, 235),
"slateblue": Color(106, 90, 205),
"slategray": Color(112, 128, 144),
"slategrey": Color(112, 128, 144),
"snow": Color(255, 250, 250),
"springgreen": Color(0, 255, 127),
"steelblue": Color(70, 130, 180),
"tan": Color(210, 180, 140),
"teal": Color(0, 128, 128),
"thistle": Color(216, 191, 216),
"tomato": Color(255, 99, 71),
"turquoise": Color(64, 224, 208),
"violet": Color(238, 130, 238),
"wheat": Color(245, 222, 179),
"white": Color(255, 255, 255),
"whitesmoke": Color(245, 245, 245),
"yellow": Color(255, 255, 0),
"yellowgreen": Color(154, 205, 50),
}
)
for k, v in list(_svg_colors.items()):
v.closest_name = v.name = k
# 2012-12-08T14:29-07:00: Copied over from Colors.hexstr_for_ccodes in the main
# textsub file, and subsequently modified.
_irc_colors.update({
# These are all taken from *MY* XChat settings - they aren't guaranteed to
# please everyone!
0: Color(0xFFFFFF),
1: Color(0x1F1F1F),
2: Color(0x00007F),
3: Color(0x007F00),
4: Color(0xFF0000),
5: Color(0x7F0000),
6: Color(0x9C009C),
7: Color(0xFC7F00),
8: Color(0xFFFF00),
9: Color(0x00FC00),
##10: Color(0x009393),
10: Color(0x008282),
11: Color(0x00FFFF),
12: Color(0x0000FC),
13: Color(0xFF00FF),
14: Color(0x7F7F7F),
15: Color(0xD2D2D2),
# My local colors
16: Color(0xCCCCCC),
##17: Color(0x000000), # Commented out 'til readability checks are in
17: Color(0x1F1F1F),
18: Color(0x000056),
19: Color(0x008141),
20: Color(0xE00707),
21: Color(0xA10000),
22: Color(0x6A006A),
23: Color(0xA15000),
24: Color(0xA1A100),
25: Color(0x416600),
##26: Color(0x008282),
26: Color(0x005682),
27: Color(0x00D5F2),
28: Color(0x0715CD),
29: Color(0x99004D),
30: Color(0x323232),
31: Color(0x929292),
99: Color(0x999999) # Until I think of a better solution to this
})
_irc_colors.update(
{
# These are all taken from *MY* XChat settings - they aren't guaranteed to
# please everyone!
0: Color(0xFFFFFF),
1: Color(0x1F1F1F),
2: Color(0x00007F),
3: Color(0x007F00),
4: Color(0xFF0000),
5: Color(0x7F0000),
6: Color(0x9C009C),
7: Color(0xFC7F00),
8: Color(0xFFFF00),
9: Color(0x00FC00),
##10: Color(0x009393),
10: Color(0x008282),
11: Color(0x00FFFF),
12: Color(0x0000FC),
13: Color(0xFF00FF),
14: Color(0x7F7F7F),
15: Color(0xD2D2D2),
# My local colors
16: Color(0xCCCCCC),
##17: Color(0x000000), # Commented out 'til readability checks are in
17: Color(0x1F1F1F),
18: Color(0x000056),
19: Color(0x008141),
20: Color(0xE00707),
21: Color(0xA10000),
22: Color(0x6A006A),
23: Color(0xA15000),
24: Color(0xA1A100),
25: Color(0x416600),
##26: Color(0x008282),
26: Color(0x005682),
27: Color(0x00D5F2),
28: Color(0x0715CD),
29: Color(0x99004D),
30: Color(0x323232),
31: Color(0x929292),
99: Color(0x999999), # Until I think of a better solution to this
}
)
for k, v in list(_irc_colors.items()):
v.ccode = "%02d" % k
del k, v

File diff suppressed because it is too large Load diff

View file

@ -5,425 +5,647 @@ import shutil
import PyInstaller.__main__
is_64bit = sys.maxsize > 2**32
#is_linux = sys.platform.startswith("linux")
# is_linux = sys.platform.startswith("linux")
exclude_modules = []
#if is_linux == False:
# if is_linux == False:
# print("Not Linux, excluding pygame.")
# exclude_modules.append('pygame')
add_data = ['quirks;quirks',
'smilies;smilies',
'themes;themes',
'docs;docs',
'README.md;.',
'LICENSE;.',
'CHANGELOG.md;.',
'PCskins.png;.',
'Pesterchum.png;.']
data_folders = {'quirks': 'quirks',
'smilies': 'smilies',
'themes': 'themes',
'docs': 'docs'}
data_files = {'README.md': 'README.md.txt',
'LICENSE': 'LICENSE.txt',
'CHANGELOG.md': 'CHANGELOG.md.txt',
'PCskins.png': '.',
'Pesterchum.png': '.'}
data_files_linux = {'README.md': 'README.md',
'LICENSE': 'LICENSE.txt',
'CHANGELOG.md': 'CHANGELOG.md',
'PCskins.png': '.',
'Pesterchum.png': '.'}
add_data = [
"quirks;quirks",
"smilies;smilies",
"themes;themes",
"docs;docs",
"README.md;.",
"LICENSE;.",
"CHANGELOG.md;.",
"PCskins.png;.",
"Pesterchum.png;.",
]
data_folders = {
"quirks": "quirks",
"smilies": "smilies",
"themes": "themes",
"docs": "docs",
}
data_files = {
"README.md": "README.md.txt",
"LICENSE": "LICENSE.txt",
"CHANGELOG.md": "CHANGELOG.md.txt",
"PCskins.png": ".",
"Pesterchum.png": ".",
}
data_files_linux = {
"README.md": "README.md",
"LICENSE": "LICENSE.txt",
"CHANGELOG.md": "CHANGELOG.md",
"PCskins.png": ".",
"Pesterchum.png": ".",
}
# Some of these might not be required anymore,
# newer versions of PyInstaller claim to exclude certain problematic DDLs automatically.
upx_exclude = ["qwindows.dll",
upx_exclude = [
"qwindows.dll",
"Qt6Core.dll",
"Qt6Gui.dll",
"vcruntime140.dll",
"MSVCP140.dll",
"MSVCP140_1.dll"
"api-ms-win-core-console-l1-1-0.dll",\
"api-ms-win-core-console-l1-1-0.dll",\
"api-ms-win-core-console-l1-2-0.dll",\
"api-ms-win-core-datetime-l1-1-0.dll",\
"api-ms-win-core-debug-l1-1-0.dll",\
"api-ms-win-core-errorhandling-l1-1-0.dll",\
"api-ms-win-core-file-l1-1-0.dll",\
"api-ms-win-core-file-l1-2-0.dll",\
"api-ms-win-core-file-l2-1-0.dll",\
"api-ms-win-core-handle-l1-1-0.dll",\
"api-ms-win-core-heap-l1-1-0.dll",\
"api-ms-win-core-interlocked-l1-1-0.dll",\
"api-ms-win-core-libraryloader-l1-1-0.dll",\
"api-ms-win-core-localization-l1-2-0.dll",\
"api-ms-win-core-memory-l1-1-0.dll",\
"api-ms-win-core-namedpipe-l1-1-0.dll",\
"api-ms-win-core-processenvironment-l1-1-0.dll",\
"api-ms-win-core-processthreads-l1-1-0.dll",\
"api-ms-win-core-processthreads-l1-1-1.dll",\
"api-ms-win-core-profile-l1-1-0.dll",\
"api-ms-win-core-rtlsupport-l1-1-0.dll",\
"api-ms-win-core-string-l1-1-0.dll",\
"api-ms-win-core-synch-l1-1-0.dll",\
"api-ms-win-core-synch-l1-2-0.dll",\
"api-ms-win-core-sysinfo-l1-1-0.dll",\
"api-ms-win-core-timezone-l1-1-0.dll",\
"api-ms-win-core-util-l1-1-0.dll",\
"API-MS-Win-core-xstate-l2-1-0.dll",\
"api-ms-win-crt-conio-l1-1-0.dll",\
"api-ms-win-crt-convert-l1-1-0.dll",\
"api-ms-win-crt-environment-l1-1-0.dll",\
"api-ms-win-crt-filesystem-l1-1-0.dll",\
"api-ms-win-crt-heap-l1-1-0.dll",\
"api-ms-win-crt-locale-l1-1-0.dll",\
"api-ms-win-crt-math-l1-1-0.dll",\
"api-ms-win-crt-multibyte-l1-1-0.dll",\
"api-ms-win-crt-private-l1-1-0.dll",\
"api-ms-win-crt-process-l1-1-0.dll",\
"api-ms-win-crt-runtime-l1-1-0.dll",\
"api-ms-win-crt-stdio-l1-1-0.dll",\
"api-ms-win-crt-string-l1-1-0.dll",\
"api-ms-win-crt-time-l1-1-0.dll",\
"api-ms-win-crt-utility-l1-1-0.dll",\
"ucrtbase.dll"]
"MSVCP140_1.dll" "api-ms-win-core-console-l1-1-0.dll",
"api-ms-win-core-console-l1-1-0.dll",
"api-ms-win-core-console-l1-2-0.dll",
"api-ms-win-core-datetime-l1-1-0.dll",
"api-ms-win-core-debug-l1-1-0.dll",
"api-ms-win-core-errorhandling-l1-1-0.dll",
"api-ms-win-core-file-l1-1-0.dll",
"api-ms-win-core-file-l1-2-0.dll",
"api-ms-win-core-file-l2-1-0.dll",
"api-ms-win-core-handle-l1-1-0.dll",
"api-ms-win-core-heap-l1-1-0.dll",
"api-ms-win-core-interlocked-l1-1-0.dll",
"api-ms-win-core-libraryloader-l1-1-0.dll",
"api-ms-win-core-localization-l1-2-0.dll",
"api-ms-win-core-memory-l1-1-0.dll",
"api-ms-win-core-namedpipe-l1-1-0.dll",
"api-ms-win-core-processenvironment-l1-1-0.dll",
"api-ms-win-core-processthreads-l1-1-0.dll",
"api-ms-win-core-processthreads-l1-1-1.dll",
"api-ms-win-core-profile-l1-1-0.dll",
"api-ms-win-core-rtlsupport-l1-1-0.dll",
"api-ms-win-core-string-l1-1-0.dll",
"api-ms-win-core-synch-l1-1-0.dll",
"api-ms-win-core-synch-l1-2-0.dll",
"api-ms-win-core-sysinfo-l1-1-0.dll",
"api-ms-win-core-timezone-l1-1-0.dll",
"api-ms-win-core-util-l1-1-0.dll",
"API-MS-Win-core-xstate-l2-1-0.dll",
"api-ms-win-crt-conio-l1-1-0.dll",
"api-ms-win-crt-convert-l1-1-0.dll",
"api-ms-win-crt-environment-l1-1-0.dll",
"api-ms-win-crt-filesystem-l1-1-0.dll",
"api-ms-win-crt-heap-l1-1-0.dll",
"api-ms-win-crt-locale-l1-1-0.dll",
"api-ms-win-crt-math-l1-1-0.dll",
"api-ms-win-crt-multibyte-l1-1-0.dll",
"api-ms-win-crt-private-l1-1-0.dll",
"api-ms-win-crt-process-l1-1-0.dll",
"api-ms-win-crt-runtime-l1-1-0.dll",
"api-ms-win-crt-stdio-l1-1-0.dll",
"api-ms-win-crt-string-l1-1-0.dll",
"api-ms-win-crt-time-l1-1-0.dll",
"api-ms-win-crt-utility-l1-1-0.dll",
"ucrtbase.dll",
]
delete_builddist = ''
upx_enabled = ''
package_universal_crt = ''
onefile = ''
windowed = ''
delete_builddist = ""
upx_enabled = ""
package_universal_crt = ""
onefile = ""
windowed = ""
try:
print("This is a script to make building with Pyinstaller a bit more conventient.")
while (delete_builddist != 'y') and (delete_builddist != 'n'):
while (delete_builddist != "y") and (delete_builddist != "n"):
delete_builddist = input("Delete build & dist folders? (Y/N): ").lower()
if delete_builddist == "y":
try:
shutil.rmtree('dist')
shutil.rmtree("dist")
except FileNotFoundError as e:
print(e)
try:
shutil.rmtree('build')
shutil.rmtree("build")
except FileNotFoundError as e:
print(e)
while (upx_enabled != 'y') and (upx_enabled != 'n'):
while (upx_enabled != "y") and (upx_enabled != "n"):
upx_enabled = input("Enable UPX? (Y/N): ").lower()
if upx_enabled == 'y':
if upx_enabled == "y":
print("If upx is on your path you don't need to include anything here.")
if is_64bit == True:
upx_dir = input("UPX directory [D:\\upx-3.96-win64]: ")
if upx_dir == '':
upx_dir = "D:\\upx-3.96-win64" # Default dir for me :)
if upx_dir == "":
upx_dir = "D:\\upx-3.96-win64" # Default dir for me :)
else:
upx_dir = input("UPX directory [D:\\upx-3.96-win32]: ")
if upx_dir == '':
upx_dir = "D:\\upx-3.96-win32" # Default dir for me :)
if upx_dir == "":
upx_dir = "D:\\upx-3.96-win32" # Default dir for me :)
print("upx_dir = " + upx_dir)
elif upx_enabled == 'n':
upx_dir = ''
while (windowed != 'y') and (windowed != 'n'):
elif upx_enabled == "n":
upx_dir = ""
while (windowed != "y") and (windowed != "n"):
windowed = input("Build with '--windowed'? (Y/N): ").lower()
if sys.platform == 'win32':
print("(https://pyinstaller.readthedocs.io/en/stable/usage.html?highlight=sdk#windows)")
while (package_universal_crt != 'y') and (package_universal_crt != 'n'):
package_universal_crt = input("Try to include universal CRT? (Y/N): ").lower()
if package_universal_crt == 'y':
if sys.platform == "win32":
print(
"(https://pyinstaller.readthedocs.io/en/stable/usage.html?highlight=sdk#windows)"
)
while (package_universal_crt != "y") and (package_universal_crt != "n"):
package_universal_crt = input(
"Try to include universal CRT? (Y/N): "
).lower()
if package_universal_crt == "y":
if is_64bit == True:
crt_path = input("Universal CRT: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64]: ")
if crt_path == '':
#crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64" # Default directory.
crt_path = os.path.join('C:%s' % os.sep, 'Program Files (x86)', 'Windows Kits', '10', '10.0.19041.0', 'ucrt', 'DLLs', 'x64')
crt_path = input(
"Universal CRT: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64]: "
)
if crt_path == "":
# crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64" # Default directory.
crt_path = os.path.join(
"C:%s" % os.sep,
"Program Files (x86)",
"Windows Kits",
"10",
"10.0.19041.0",
"ucrt",
"DLLs",
"x64",
)
else:
crt_path = input("Extra path: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86]: ")
if crt_path == '':
#crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86" # Default directory.
crt_path = os.path.join('C:%s' % os.sep, 'Program Files (x86)', 'Windows Kits', '10', '10.0.19041.0', 'ucrt', 'DLLs', 'x86')
crt_path = input(
"Extra path: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86]: "
)
if crt_path == "":
# crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86" # Default directory.
crt_path = os.path.join(
"C:%s" % os.sep,
"Program Files (x86)",
"Windows Kits",
"10",
"10.0.19041.0",
"ucrt",
"DLLs",
"x86",
)
print("crt_path = " + crt_path)
if (sys.platform == 'win32') or (sys.platform == 'linux'):
while (onefile != 'y') and (onefile != 'n'):
if (sys.platform == "win32") or (sys.platform == "linux"):
while (onefile != "y") and (onefile != "n"):
onefile = input("Build with '--onefile'? (Y/N): ").lower()
except KeyboardInterrupt:
sys.exit("KeyboardInterrupt")
#Windows
if sys.platform == 'win32':
# Windows
if sys.platform == "win32":
run_win32 = [
'pesterchum.py',
'--name=Pesterchum',
"pesterchum.py",
"--name=Pesterchum",
#'--noconfirm', # Overwrite output directory.
#'--windowed', # Hide console
'--icon=pesterchum.ico',
'--clean', # Clear cache
"--icon=pesterchum.ico",
"--clean", # Clear cache
]
if (sys.version_info.major == 3) & (sys.version_info.minor == 8):
exclude_modules.append('tkinter')
if upx_enabled == 'y':
exclude_modules.append("tkinter")
if upx_enabled == "y":
if os.path.isdir(upx_dir):
run_win32.append('--upx-dir=%s' % upx_dir)
run_win32.append("--upx-dir=%s" % upx_dir)
for x in upx_exclude:
run_win32.append('--upx-exclude=%s' % x )
run_win32.append("--upx-exclude=%s" % x)
# Lower case variants are required.
run_win32.append('--upx-exclude=%s' % x.lower() )
elif upx_enabled == 'n':
run_win32.append('--noupx')
run_win32.append("--upx-exclude=%s" % x.lower())
elif upx_enabled == "n":
run_win32.append("--noupx")
for x in exclude_modules:
run_win32.append('--exclude-module=%s' % x )
if windowed == 'y':
run_win32.append('--windowed')
if onefile == 'y':
run_win32.append('--onefile')
elif onefile == 'n':
run_win32.append("--exclude-module=%s" % x)
if windowed == "y":
run_win32.append("--windowed")
if onefile == "y":
run_win32.append("--onefile")
elif onefile == "n":
for x in add_data:
run_win32.append('--add-data=%s' % x )
run_win32.append("--add-data=%s" % x)
if package_universal_crt == 'y':
run_win32.append('--paths=\"%s\"' % crt_path)
if package_universal_crt == "y":
run_win32.append('--paths="%s"' % crt_path)
if os.path.exists(crt_path):
if is_64bit == False:
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\API-MS-Win-core-xstate-l2-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\ucrtbase.dll;.' % crt_path)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\API-MS-Win-core-xstate-l2-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;." % crt_path
)
run_win32.append("--add-binary=%s\\ucrtbase.dll;." % crt_path)
elif is_64bit == True:
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;.' % crt_path)
run_win32.append('--add-binary=%s\\ucrtbase.dll;.' % crt_path)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;."
% crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;." % crt_path
)
run_win32.append(
"--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;." % crt_path
)
run_win32.append("--add-binary=%s\\ucrtbase.dll;." % crt_path)
print(run_win32)
PyInstaller.__main__.run(run_win32)
if onefile == 'y':
if onefile == "y":
# There's more proper ways to do this, but this doesn't require changing our paths
for x in data_folders:
print(x)
shutil.copytree(x, os.path.join('dist', data_folders[x]),
ignore=shutil.ignore_patterns('*.psd',
'*.xcf*',
'ebg2.png',
'ebg1.png'))
shutil.copytree(
x,
os.path.join("dist", data_folders[x]),
ignore=shutil.ignore_patterns(
"*.psd", "*.xcf*", "ebg2.png", "ebg1.png"
),
)
for x in data_files:
print(x)
shutil.copy(x, os.path.join('dist', data_files[x]))
files = os.listdir('dist')
shutil.copy(x, os.path.join("dist", data_files[x]))
files = os.listdir("dist")
try:
os.mkdir(os.path.join('dist', 'Pesterchum'))
os.mkdir(os.path.join("dist", "Pesterchum"))
except FileExistsError:
pass
for x in files:
shutil.move(os.path.join('dist',x), os.path.join('dist', 'Pesterchum'))
#shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
shutil.move(os.path.join("dist", x), os.path.join("dist", "Pesterchum"))
# shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
# os.path.join('dist', 'Pesterchum', 'xref-Pesterchum.html'))
#shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
# shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
# os.path.join('dist', 'Pesterchum', 'Analysis-00.toc'))
#MacOS
elif sys.platform == 'darwin' :
run_darwin =[
'pesterchum.py',
'--name=Pesterchum',
# MacOS
elif sys.platform == "darwin":
run_darwin = [
"pesterchum.py",
"--name=Pesterchum",
#'--windowed', # Hide console
#'--noconfirm', # Overwrite output directory.
'--icon=trayicon32.icns', # Icon
'--onedir',
'--clean', # Clear cache
"--icon=trayicon32.icns", # Icon
"--onedir",
"--clean", # Clear cache
#'--noupx'
]
if upx_enabled == 'y':
if upx_enabled == "y":
if os.path.isdir(upx_dir):
run_darwin.append('--upx-dir=%s' % upx_dir)
run_darwin.append("--upx-dir=%s" % upx_dir)
for x in upx_exclude:
run_darwin.append('--upx-exclude=%s' % x )
run_darwin.append("--upx-exclude=%s" % x)
# Lower case variants are required.
run_darwin.append('--upx-exclude=%s' % x.lower() )
elif upx_enabled == 'n':
run_darwin.append('--noupx')
run_darwin.append("--upx-exclude=%s" % x.lower())
elif upx_enabled == "n":
run_darwin.append("--noupx")
if os.path.isdir(upx_dir):
run_darwin.append('--upx-dir=%s' % upx_dir)
run_darwin.append("--upx-dir=%s" % upx_dir)
for x in exclude_modules:
run_darwin.append('--exclude-module=%s' % x )
run_darwin.append("--exclude-module=%s" % x)
for x in add_data:
run_darwin.append('--add-data=%s' % x.replace(';',':') )
if windowed == 'y':
run_darwin.append('--windowed')
run_darwin.append("--add-data=%s" % x.replace(";", ":"))
if windowed == "y":
run_darwin.append("--windowed")
PyInstaller.__main__.run(run_darwin)
#Linux
elif sys.platform == 'linux':
run_linux =[
'pesterchum.py',
'--name=Pesterchum',
# Linux
elif sys.platform == "linux":
run_linux = [
"pesterchum.py",
"--name=Pesterchum",
#'--windowed', # Hide console
#'--noconfirm', # Overwrite output directory.
'--icon=trayicon32.icns', # Icon
'--clean', # Clear cache
"--icon=trayicon32.icns", # Icon
"--clean", # Clear cache
]
if upx_enabled == 'y':
if upx_enabled == "y":
if os.path.isdir(upx_dir):
run_linux.append('--upx-dir=%s' % upx_dir)
run_linux.append("--upx-dir=%s" % upx_dir)
for x in upx_exclude:
run_linux.append('--upx-exclude=%s' % x )
run_linux.append("--upx-exclude=%s" % x)
# Lower case variants are required.
run_linux.append('--upx-exclude=%s' % x.lower() )
elif upx_enabled == 'n':
run_linux.append('--noupx')
run_linux.append("--upx-exclude=%s" % x.lower())
elif upx_enabled == "n":
run_linux.append("--noupx")
for x in exclude_modules:
run_linux.append('--exclude-module=%s' % x )
if onefile == 'y':
run_linux.append('--onefile')
elif onefile == 'n':
run_linux.append("--exclude-module=%s" % x)
if onefile == "y":
run_linux.append("--onefile")
elif onefile == "n":
for x in add_data:
run_linux.append('--add-data=%s' % x.replace(';',':') )
if windowed == 'y':
run_linux.append('--windowed')
run_linux.append("--add-data=%s" % x.replace(";", ":"))
if windowed == "y":
run_linux.append("--windowed")
print(run_linux)
PyInstaller.__main__.run(run_linux)
if onefile == 'y':
if onefile == "y":
# There's more proper ways to do this, but this doesn't require changing our paths
for x in data_folders:
print(x)
shutil.copytree(x, os.path.join('dist', data_folders[x]),
ignore=shutil.ignore_patterns('*.psd',
'*.xcf*',
'ebg2.png',
'ebg1.png'))
shutil.copytree(
x,
os.path.join("dist", data_folders[x]),
ignore=shutil.ignore_patterns(
"*.psd", "*.xcf*", "ebg2.png", "ebg1.png"
),
)
for x in data_files_linux:
print(x)
shutil.copy(x, os.path.join('dist', data_files_linux[x]))
files = os.listdir('dist')
shutil.copy(x, os.path.join("dist", data_files_linux[x]))
files = os.listdir("dist")
try:
os.mkdir(os.path.join('dist', '.cache'))
os.mkdir(os.path.join("dist", ".cache"))
except FileExistsError as e:
print(e)
for x in files:
try:
shutil.move(os.path.join('dist',x), os.path.join('dist', '.cache', x))
shutil.move(os.path.join("dist", x), os.path.join("dist", ".cache", x))
except FileExistsError as e:
print(e)
shutil.move(os.path.join('dist', '.cache'), os.path.join('dist', 'Pesterchum'))
#shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
shutil.move(os.path.join("dist", ".cache"), os.path.join("dist", "Pesterchum"))
# shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
# os.path.join('dist', 'Pesterchum', 'xref-Pesterchum.html'))
#shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
# shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
# os.path.join('dist', 'Pesterchum', 'Analysis-00.toc'))
else:
print("Unknown platform.")
run_generic =[
'pesterchum.py',
'--name=Pesterchum'
'--clean', # Clear cache
run_generic = [
"pesterchum.py",
"--name=Pesterchum" "--clean", # Clear cache
]
if upx_enabled == 'y':
if upx_enabled == "y":
if os.path.isdir(upx_dir):
run_generic.append('--upx-dir=%s' % upx_dir)
run_generic.append("--upx-dir=%s" % upx_dir)
else:
run_generic.append('--noupx')
run_generic.append("--noupx")
for x in upx_exclude:
run_generic.append('--upx-exclude=%s' % x )
run_generic.append("--upx-exclude=%s" % x)
# Lower case variants are required.
run_generic.append('--upx-exclude=%s' % x.lower() )
run_generic.append("--upx-exclude=%s" % x.lower())
for x in exclude_modules:
run_generic.append('--exclude-module=%s' % x )
run_generic.append("--exclude-module=%s" % x)
for x in add_data:
run_generic.append('--add-data=%s' % x.replace(';',':') )
if windowed == 'y':
run_win32.append('--windowed')
run_generic.append("--add-data=%s" % x.replace(";", ":"))
if windowed == "y":
run_win32.append("--windowed")
print(run_generic)
PyInstaller.__main__.run(run_generic)

View file

@ -10,44 +10,45 @@ except ImportError:
import ostools
from quirks import ScriptQuirks
PchumLog = logging.getLogger('pchumLogger')
PchumLog = logging.getLogger("pchumLogger")
class PythonQuirks(ScriptQuirks):
def loadModule(self, name, filename):
# imp is deprecated since Python 3.4
#return imp.load_source(name, filename)
# return imp.load_source(name, filename)
spec = importlib.util.spec_from_file_location(name, filename)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def getExtension(self):
return '.py'
return ".py"
def modHas(self, module, attr):
if attr == 'commands':
if attr == "commands":
variables = vars(module)
for name, obj in variables.items():
if self.modHas(obj, 'command'):
if self.modHas(obj, "command"):
return True
return hasattr(module, attr)
def register(self, module):
variables = vars(module)
for name, obj in variables.items():
if self.modHas(obj, 'command'):
if self.modHas(obj, "command"):
try:
if not isinstance(obj("test"), str):
raise Exception
except:
#print("Quirk malformed: %s" % (obj.command))
# print("Quirk malformed: %s" % (obj.command))
PchumLog.error("Quirk malformed: %s" % (obj.command))
# Since this is executed before QApplication is constructed,
# This prevented pesterchum from starting entirely when a quirk was malformed :/
# (QWidget: Must construct a QApplication before a QWidget)
if QtWidgets.QApplication.instance() != None:
msgbox = QtWidgets.QMessageBox()
msgbox.setWindowTitle("Error!")
@ -55,4 +56,3 @@ class PythonQuirks(ScriptQuirks):
msgbox.exec()
else:
self.quirks[obj.command] = obj

View file

@ -1,12 +1,15 @@
import os
import socket
class TwmnError(Exception):
UNWN_ERR = -1
NO_TWMND = -2
NO_CONF = -3
NO_CONF = -3
def __init__(self, code):
self.code = code
def __str__(self):
if self.code == TwmnError.NO_TWMND:
return "Unable to connect to twmnd"
@ -19,10 +22,12 @@ class TwmnError(Exception):
def confExists():
try:
from xdg import BaseDirectory
return os.path.join(BaseDirectory.xdg_config_home,"twmn/twmn.conf")
return os.path.join(BaseDirectory.xdg_config_home, "twmn/twmn.conf")
except:
return False
def init(host="127.0.0.1", port=None):
if not port:
port = 9797
@ -32,8 +37,7 @@ def init(host="127.0.0.1", port=None):
return False
with open(fn) as f:
for line in f.readlines():
if line.startswith("port=") and \
line[5:-1].isdigit():
if line.startswith("port=") and line[5:-1].isdigit():
port = int(line[5:-1])
break
except IOError:
@ -44,6 +48,7 @@ def init(host="127.0.0.1", port=None):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((host, port))
class Notification(object):
def __init__(self, title="", msg="", icon=""):
self.title = str(title)
@ -59,17 +64,22 @@ class Notification(object):
def show(self):
try:
if self.time is None:
s.send("<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,
@ -9,66 +10,62 @@ def rainbow(text):
Regexp Replace
Regexp: ^(.*)$
Replace With: rainbow(\1)
To customize it:
1. Copy this function.
2. Replace the hex colors in 'gradient' below with your own colors.
3. Replace 'rainbow' above and below with something more fitting :3
There's lots of implementations of this that predate mine,
see: https://paste.0xfc.de/?e60df5a155e93583#AmcgN9cRnCcBycmVMvw6KJ1YLKPXGbaSzZLbgAhoNCQD
^ There's more useful info here too :3c
"""
# Values of 'gradient' can be any amount of hex/RGB colors.
gradient = ["#ff0000",
"#ff8000",
"#ffff00",
"#80ff00",
"#00ff00",
"#00ff80",
"#00ffff",
"#0080ff",
"#0000ff",
"#8000ff",
"#ff00ff",
"#ff0080"]
gradient = [
"#ff0000",
"#ff8000",
"#ffff00",
"#80ff00",
"#00ff00",
"#00ff80",
"#00ffff",
"#0080ff",
"#0000ff",
"#8000ff",
"#ff00ff",
"#ff0080",
]
# Set base distribution of colors over text,
# stored as list of lists.
color_and_position = []
for color in range(0, len(gradient)):
ratio = len(text) / len(gradient) # To account for text length.
color_and_position.append(
[gradient[color],
round(color * ratio)])
color_and_position.append([gradient[color], round(color * ratio)])
# Iterate through match object representing all links/smilies in text,
# if a color tag is going to be placed within it,
# move its position to after the link.
for match in re.finditer(_urlre, text):
for cp in color_and_position:
if ((cp[1] >= match.start()) # cp[1] is pos
and (cp[1] <= match.end())):
if (cp[1] >= match.start()) and (cp[1] <= match.end()): # cp[1] is pos
cp[1] = match.end() + 1 # Move to 1 character after link.
for match in re.finditer(_smilere, text):
for cp in color_and_position:
if ((cp[1] >= match.start())
and (cp[1] <= match.end())):
if (cp[1] >= match.start()) and (cp[1] <= match.end()):
cp[1] = match.end() + 1
for match in re.finditer(_memore, text):
for cp in color_and_position:
if ((cp[1] >= match.start())
and (cp[1] <= match.end())):
if (cp[1] >= match.start()) and (cp[1] <= match.end()):
cp[1] = match.end() + 1
for match in re.finditer(_handlere, text):
for cp in color_and_position:
if ((cp[1] >= match.start())
and (cp[1] <= match.end())):
if (cp[1] >= match.start()) and (cp[1] <= match.end()):
cp[1] = match.end() + 1
# Iterate through characters in text and write them to the output,
# if a color tag should be placed, add it before the character.
output = ''
output = ""
for char in range(0, len(text)):
# Add color if at position.
for cp in color_and_position:
@ -82,6 +79,8 @@ def rainbow(text):
# Add character.
output += text[char]
return output
rainbow.command = "rainbow"
# These can't always be imported from their original functions,
@ -152,10 +151,10 @@ smiledict = {
":scorpio:": "scorpio.gif",
":shades:": "shades.png",
":honk:": "honk.png",
}
}
# Regular expression templates for detecting links/smilies.
_smilere = re.compile("|".join(list(smiledict.keys())))
_urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+")
#_url2re = re.compile(r"(?i)(?<!//)\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)
@ -50,20 +55,20 @@ class RandomHandler(QtCore.QObject):
l = msg.split("=")
code = l[0][0]
if code not in self.queue:
return # Ignore if we didn't request this
return # Ignore if we didn't request this
self.queue.remove(code)
if code == "?":
if l[1][0] == "y":
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,13 +7,13 @@ 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)
os.chdir(os.path.dirname(__file__))
@ -22,5 +22,6 @@ def main(argv):
retcode = call("python3 pesterchum.py " + " " + str(arguments), shell=True)
print(retcode)
if __name__ == "__main__":
main(sys.argv)

209
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,120 +21,131 @@ if sys.platform == "win32":
path = sys.path
if is_64bit() == True:
path.append(r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x64")
path.append(
r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x64"
)
elif is_64bit() == False:
path.append(r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x86")
path.append(
r"C:\Program Files (x86)\Windows Kits\10\Redist\10.0.22000.0\ucrt\DLLs\x86"
)
print("Path = " + str(path))
includefiles = ["quirks",
"smilies",
"themes",
"docs",
"README.md",
"LICENSE",
"CHANGELOG.md",
"PCskins.png",
"Pesterchum.png"]
includefiles = [
"quirks",
"smilies",
"themes",
"docs",
"README.md",
"LICENSE",
"CHANGELOG.md",
"PCskins.png",
"Pesterchum.png",
]
build_exe_options = {
# "includes": ['PyQt6.QtCore',
# 'PyQt6.QtGui',
# 'PyQt6.QtWidgets'],
"excludes": ['collections.sys',
'collections._sre',
'collections._json',
'collections._locale',
'collections._struct',
'collections.array',
'collections._weakref',
'PyQt6.QtMultimedia',
'PyQt6.QtDBus',
'PyQt6.QtDeclarative',
'PyQt6.QtHelp',
'PyQt6.QtNetwork',
'PyQt6.QtSql',
'PyQt6.QtSvg',
'PyQt6.QtTest',
'PyQt6.QtWebKit',
'PyQt6.QtXml',
'PyQt6.QtXmlPatterns',
'PyQt6.phonon',
'PyQt6.QtAssistant',
'PyQt6.QtDesigner',
'PyQt6.QAxContainer',
'pygame.docs' # Hopefully we can just not have pygame at all at some point =3
# (when QtMultimedia stops relying on local codecs </3)
'pygame.examples',
'pygame.tests',
'pydoc_data'],
# "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",
],
"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
"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
"DesktopFolder", # Directory_
"Pesterchum", # Name
"TARGETDIR", # Component_
"[TARGETDIR]pesterchum.exe",# Target
None, # Arguments
description, # Description
None, # Hotkey
None, # Icon (Is inherited from pesterchum.exe)
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # WkDir
),
("StartMenuShortcut", # Shortcut
"StartMenuFolder", # Directory_
"Pesterchum", # Name
"TARGETDIR", # Component_
"[TARGETDIR]pesterchum.exe",# Target
None, # Arguments
description, # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # WkDir
)
]
(
"DesktopShortcut", # Shortcut
"DesktopFolder", # Directory_
"Pesterchum", # Name
"TARGETDIR", # Component_
"[TARGETDIR]pesterchum.exe", # Target
None, # Arguments
description, # Description
None, # Hotkey
None, # Icon (Is inherited from pesterchum.exe)
None, # IconIndex
None, # ShowCmd
"TARGETDIR", # WkDir
),
(
"StartMenuShortcut", # Shortcut
"StartMenuFolder", # Directory_
"Pesterchum", # Name
"TARGETDIR", # Component_
"[TARGETDIR]pesterchum.exe", # Target
None, # Arguments
description, # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
"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,
"bdist_msi": bdist_msi_options,
"bdist_mac": bdist_mac_options},
packages="",
executables = [Executable("pesterchum.py",
base=base,
icon=icon
)])
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,
},
packages="",
executables=[Executable("pesterchum.py", base=base, icon=icon)],
)

237
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
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,16 +42,17 @@ 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):
self.machine = machine
self.title = title
self.msg = msg
self.time = time
self.machine = machine
self.title = title
self.msg = msg
self.time = time
if icon:
icon = os.path.abspath(icon)
self.icon = icon
self.icon = icon
self.importance = importance
if inspect.ismethod(self.title) or inspect.isfunction(self.title):
self.title = self.title()
@ -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,28 +129,48 @@ 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={}):
self.parent = parent
self.name = name
self.on = on
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
types.update(extras)
self.types = types
self.type = "default"
self.quit = False
self.types = types
self.type = "default"
self.quit = False
self.displaying = False
self.setCurrentType(type)
@ -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,13 +298,17 @@ 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(),
str(self.msg.text()),
self.parent().theme["toasts/width"],
self.parent().theme["toasts/content/style"]))
self.msg.setText(
PesterToast.wrapText(
self.msg.font(),
str(self.msg.text()),
self.parent().theme["toasts/width"],
self.parent().theme["toasts/content/style"],
)
)
screens = QtWidgets.QApplication.screens()
screen = screens[0] # Should be the main one right???
screen = screens[0] # Should be the main one right???
# This 100% doesn't work with multiple screens.
p = screen.availableGeometry().bottomRight()
o = screen.geometry().bottomRight()
@ -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()