Reformat codebase (#97)
* Reformat codebase with black * Create black.yml and add black style badge to README.md
This commit is contained in:
parent
c8a8097673
commit
a51e4dd69e
38 changed files with 6139 additions and 3682 deletions
10
.github/workflows/black.yml
vendored
Normal file
10
.github/workflows/black.yml
vendored
Normal 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
|
|
@ -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">
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
104
console.py
104
console.py
|
@ -1,7 +1,7 @@
|
|||
# vim: set autoindent ts=4 sts=4 sw=4 textwidth=79 expandtab:
|
||||
# -*- coding=UTF-8; tab-width: 4 -*-
|
||||
#import os
|
||||
#from os import remove
|
||||
# import os
|
||||
# from os import remove
|
||||
import sys
|
||||
import traceback
|
||||
import time
|
||||
|
@ -17,17 +17,20 @@ except ImportError:
|
|||
from PyQt5.QtWidgets import QAction
|
||||
|
||||
import dataobjs
|
||||
#import generic
|
||||
#import memos
|
||||
#import parsetools
|
||||
|
||||
# import generic
|
||||
# import memos
|
||||
# import parsetools
|
||||
import ostools
|
||||
#from version import _pcVersion
|
||||
|
||||
# from version import _pcVersion
|
||||
from pnc.dep.attrdict import AttrDict
|
||||
|
||||
PchumLog = logging.getLogger('pchumLogger')
|
||||
PchumLog = logging.getLogger("pchumLogger")
|
||||
|
||||
|
||||
class ConsoleWindow(QtWidgets.QDialog):
|
||||
#~class ConsoleWindow(styler.PesterBaseWindow):
|
||||
# ~class ConsoleWindow(styler.PesterBaseWindow):
|
||||
# A simple console class, cobbled together from the corpse of another.
|
||||
|
||||
stylesheet_path = "main/defaultwindow/style"
|
||||
|
@ -127,7 +130,7 @@ class ConsoleWindow(QtWidgets.QDialog):
|
|||
def initTheme(self, theme):
|
||||
# Set up our style/window specifics
|
||||
self.changeTheme(theme)
|
||||
self.resize(400,600)
|
||||
self.resize(400, 600)
|
||||
|
||||
def changeTheme(self, theme):
|
||||
self.setStyleSheet(theme[self.stylesheet_path])
|
||||
|
@ -150,18 +153,23 @@ class ConsoleWindow(QtWidgets.QDialog):
|
|||
wgt = QtWidgets.QApplication.widgetAt(pos)
|
||||
if wgt is None:
|
||||
# Don't set None, for now. May change this later.
|
||||
self.addMessage("You need to have your cursor over something " + \
|
||||
"in Pesterchum to use that.",
|
||||
direction=direction)
|
||||
self.addMessage(
|
||||
"You need to have your cursor over something "
|
||||
+ "in Pesterchum to use that.",
|
||||
direction=direction,
|
||||
)
|
||||
return
|
||||
|
||||
self.selected_widget = wgt
|
||||
nchild = len(wgt.children())
|
||||
output = []
|
||||
output.append("CONSOLE.selected_widget = {0!r}".format(wgt))
|
||||
output.append("{0: <4}Parent: {1!r}".format('', wgt.parent()))
|
||||
output.append("{0: <4}{1:4d} child{2}".format('',
|
||||
nchild, ("ren" if abs(nchild) != 1 else "") ))
|
||||
output.append("{0: <4}Parent: {1!r}".format("", wgt.parent()))
|
||||
output.append(
|
||||
"{0: <4}{1:4d} child{2}".format(
|
||||
"", nchild, ("ren" if abs(nchild) != 1 else "")
|
||||
)
|
||||
)
|
||||
if self.show_info_on_select:
|
||||
qtss = None
|
||||
uses_ss = None
|
||||
|
@ -211,12 +219,13 @@ class ConsoleWindow(QtWidgets.QDialog):
|
|||
else:
|
||||
# We got a stylesheet out of this!
|
||||
uses_ss, ss_msg = True, "Yes"
|
||||
#~ss_par_msg = "{0: <4}...on parent ↑{1:d}: {2!r}".format('',
|
||||
ss_par_msg = "{0: <4}...on parent #{1:d}: {2!r}".format('',
|
||||
i, ss_par)
|
||||
# ~ss_par_msg = "{0: <4}...on parent ↑{1:d}: {2!r}".format('',
|
||||
ss_par_msg = "{0: <4}...on parent #{1:d}: {2!r}".format(
|
||||
"", i, ss_par
|
||||
)
|
||||
|
||||
msg = []
|
||||
msg.append("{0: <4}QtSS?: {1}".format('', ss_msg))
|
||||
msg.append("{0: <4}QtSS?: {1}".format("", ss_msg))
|
||||
# A stylesheet analyzer would be wonderful here. Perhaps something
|
||||
# that tells us how many parent classes define stylesheets?
|
||||
if uses_ss:
|
||||
|
@ -224,16 +233,15 @@ class ConsoleWindow(QtWidgets.QDialog):
|
|||
# We got this stylesheet from a parent object somewhere.
|
||||
msg.append(ss_par_msg)
|
||||
msg.append("{0: <4}".format("Stylesheet:"))
|
||||
for ln in qtss.split('\n'):
|
||||
for ln in qtss.split("\n"):
|
||||
msg.append("{0: <8}".format(ln))
|
||||
|
||||
# Actually add this stuff to the result we're constructing
|
||||
output.extend(msg)
|
||||
|
||||
output = '\n'.join(output)
|
||||
output = "\n".join(output)
|
||||
self.addMessage(output, direction=direction)
|
||||
|
||||
|
||||
# Actual console stuff.
|
||||
def execInConsole(self, scriptstr, env=None):
|
||||
# Since that's what imports *us*, this should be okay
|
||||
|
@ -247,16 +255,16 @@ class ConsoleWindow(QtWidgets.QDialog):
|
|||
# Fetch from the class/instance first.
|
||||
_CUSTOM_ENV = self._CUSTOM_ENV.copy()
|
||||
# Modify with some hard-coded environmental additions.
|
||||
_CUSTOM_ENV.update({
|
||||
_CUSTOM_ENV.update(
|
||||
{
|
||||
"CONSOLE": self,
|
||||
"MAINWIN": self.mainwindow,
|
||||
"PCONFIG": self.mainwindow.config,
|
||||
"exit": lambda: self.mainwindow.exitaction.trigger()
|
||||
})
|
||||
"exit": lambda: self.mainwindow.exitaction.trigger(),
|
||||
}
|
||||
)
|
||||
# Aliases.
|
||||
_CUSTOM_ENV.update({
|
||||
"quit": _CUSTOM_ENV["exit"]
|
||||
})
|
||||
_CUSTOM_ENV.update({"quit": _CUSTOM_ENV["exit"]})
|
||||
# Add whatever additions were set in the main pesterchum file.
|
||||
_CUSTOM_ENV.update(pchum._CONSOLE_ENV)
|
||||
|
||||
|
@ -284,7 +292,7 @@ class ConsoleWindow(QtWidgets.QDialog):
|
|||
# Replace the old writer (for now)
|
||||
sysout, sys.stdout = sys.stdout, self
|
||||
try:
|
||||
code = compile(scriptstr + '\n', "<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)
|
||||
|
|
418
dataobjs.py
418
dataobjs.py
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
import ostools
|
||||
PchumLog = logging.getLogger('pchumLogger')
|
||||
|
||||
PchumLog = logging.getLogger("pchumLogger")
|
||||
try:
|
||||
from PyQt6 import QtGui
|
||||
except ImportError:
|
||||
|
@ -11,15 +12,17 @@ import re
|
|||
import random
|
||||
|
||||
from mood import Mood
|
||||
from parsetools import (timeDifference,
|
||||
convertTags,
|
||||
lexMessage,
|
||||
parseRegexpFunctions,
|
||||
smiledict)
|
||||
from parsetools import (
|
||||
timeDifference,
|
||||
convertTags,
|
||||
lexMessage,
|
||||
parseRegexpFunctions,
|
||||
smiledict,
|
||||
)
|
||||
from mispeller import mispeller
|
||||
|
||||
_urlre = re.compile(r"(?i)(?:^|(?<=\s))(?:(?:https?|ftp)://|magnet:)[^\s]+")
|
||||
#_url2re = re.compile(r"(?i)(?<!//)\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()
|
||||
|
|
43
generic.py
43
generic.py
|
@ -5,30 +5,40 @@ except ImportError:
|
|||
from PyQt5 import QtGui, QtWidgets
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
class mysteryTime(timedelta):
|
||||
def __sub__(self, other):
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
return (type(other) is mysteryTime)
|
||||
return type(other) is mysteryTime
|
||||
|
||||
def __neq__(self, other):
|
||||
return (type(other) is not mysteryTime)
|
||||
return type(other) is not mysteryTime
|
||||
|
||||
|
||||
class CaseInsensitiveDict(dict):
|
||||
def __setitem__(self, key, value):
|
||||
super(CaseInsensitiveDict, self).__setitem__(key.lower(), value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return super(CaseInsensitiveDict, self).__getitem__(key.lower())
|
||||
|
||||
def __contains__(self, key):
|
||||
return super(CaseInsensitiveDict, self).__contains__(key.lower())
|
||||
|
||||
def has_key(self, key):
|
||||
return key.lower() in super(CaseInsensitiveDict, self)
|
||||
|
||||
def __delitem__(self, key):
|
||||
super(CaseInsensitiveDict, self).__delitem__(key.lower())
|
||||
|
||||
|
||||
class PesterList(list):
|
||||
def __init__(self, l):
|
||||
self.extend(l)
|
||||
|
||||
|
||||
class PesterIcon(QtGui.QIcon):
|
||||
def __init__(self, *x):
|
||||
super(PesterIcon, self).__init__(x[0])
|
||||
|
@ -36,6 +46,7 @@ class PesterIcon(QtGui.QIcon):
|
|||
self.icon_pixmap = QtGui.QPixmap(x[0])
|
||||
else:
|
||||
self.icon_pixmap = None
|
||||
|
||||
def realsize(self):
|
||||
if self.icon_pixmap:
|
||||
return self.icon_pixmap.size()
|
||||
|
@ -45,18 +56,21 @@ class PesterIcon(QtGui.QIcon):
|
|||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
class RightClickList(QtWidgets.QListWidget):
|
||||
def contextMenuEvent(self, event):
|
||||
#fuckin Qt <--- I feel that </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):
|
||||
|
|
143
logviewer.py
143
logviewer.py
|
@ -4,6 +4,7 @@ import codecs
|
|||
import re
|
||||
import ostools
|
||||
from time import strftime, strptime
|
||||
|
||||
try:
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
except ImportError:
|
||||
|
@ -15,13 +16,15 @@ from convo import PesterText
|
|||
|
||||
_datadir = ostools.getDataDir()
|
||||
|
||||
|
||||
class PesterLogSearchInput(QtWidgets.QLineEdit):
|
||||
def __init__(self, theme, parent=None):
|
||||
QtWidgets.QLineEdit.__init__(self, parent)
|
||||
self.setStyleSheet(theme["convo/input/style"] + "; margin-right:0px;")
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
QtWidgets.QLineEdit.keyPressEvent(self, event)
|
||||
if hasattr(self.parent(), 'textArea'):
|
||||
if hasattr(self.parent(), "textArea"):
|
||||
if event.key() == QtCore.Qt.Key.Key_Return:
|
||||
self.parent().logSearch(self.text())
|
||||
if self.parent().textArea.find(self.text()):
|
||||
|
@ -29,6 +32,7 @@ class PesterLogSearchInput(QtWidgets.QLineEdit):
|
|||
else:
|
||||
self.parent().logSearch(self.text())
|
||||
|
||||
|
||||
class PesterLogHighlighter(QtGui.QSyntaxHighlighter):
|
||||
def __init__(self, parent):
|
||||
QtGui.QSyntaxHighlighter.__init__(self, parent)
|
||||
|
@ -36,11 +40,16 @@ class PesterLogHighlighter(QtGui.QSyntaxHighlighter):
|
|||
self.hilightstyle = QtGui.QTextCharFormat()
|
||||
self.hilightstyle.setBackground(QtGui.QBrush(QtCore.Qt.GlobalColor.green))
|
||||
self.hilightstyle.setForeground(QtGui.QBrush(QtCore.Qt.GlobalColor.black))
|
||||
|
||||
def highlightBlock(self, text):
|
||||
for i in range(0, len(text)-(len(self.searchTerm)-1)):
|
||||
if str(text[i:i+len(self.searchTerm)]).lower() == str(self.searchTerm).lower():
|
||||
for i in range(0, len(text) - (len(self.searchTerm) - 1)):
|
||||
if (
|
||||
str(text[i : i + len(self.searchTerm)]).lower()
|
||||
== str(self.searchTerm).lower()
|
||||
):
|
||||
self.setFormat(i, len(self.searchTerm), self.hilightstyle)
|
||||
|
||||
|
||||
class PesterLogUserSelect(QtWidgets.QDialog):
|
||||
def __init__(self, config, theme, parent):
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
|
@ -49,7 +58,7 @@ class PesterLogUserSelect(QtWidgets.QDialog):
|
|||
self.theme = theme
|
||||
self.parent = parent
|
||||
self.handle = parent.profile().handle
|
||||
self.logpath = _datadir+"logs"
|
||||
self.logpath = _datadir + "logs"
|
||||
|
||||
self.setStyleSheet(self.theme["main/defaultwindow/style"])
|
||||
self.setWindowTitle("Pesterlogs")
|
||||
|
@ -72,7 +81,9 @@ class PesterLogUserSelect(QtWidgets.QDialog):
|
|||
|
||||
for (i, t) in enumerate(chumMemoList):
|
||||
item = QtWidgets.QListWidgetItem(t)
|
||||
item.setForeground(QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"])))
|
||||
item.setForeground(
|
||||
QtGui.QBrush(QtGui.QColor(self.theme["main/chums/userlistcolor"]))
|
||||
)
|
||||
self.chumsBox.addItem(item)
|
||||
|
||||
self.search = PesterLogSearchInput(theme, self)
|
||||
|
@ -109,10 +120,12 @@ class PesterLogUserSelect(QtWidgets.QDialog):
|
|||
@QtCore.pyqtSlot()
|
||||
def viewActivatedLog(self):
|
||||
selectedchum = self.selectedchum().text()
|
||||
if not hasattr(self, 'pesterlogviewer'):
|
||||
if not hasattr(self, "pesterlogviewer"):
|
||||
self.pesterlogviewer = None
|
||||
if not self.pesterlogviewer:
|
||||
self.pesterlogviewer = PesterLogViewer(selectedchum, self.config, self.theme, self.parent)
|
||||
self.pesterlogviewer = PesterLogViewer(
|
||||
selectedchum, self.config, self.theme, self.parent
|
||||
)
|
||||
self.pesterlogviewer.rejected.connect(self.closeActiveLog)
|
||||
self.pesterlogviewer.show()
|
||||
self.pesterlogviewer.raise_()
|
||||
|
@ -126,7 +139,13 @@ class PesterLogUserSelect(QtWidgets.QDialog):
|
|||
|
||||
@QtCore.pyqtSlot()
|
||||
def openDir(self):
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + os.path.join(_datadir, "logs"), QtCore.QUrl.ParsingMode.TolerantMode))
|
||||
QtGui.QDesktopServices.openUrl(
|
||||
QtCore.QUrl(
|
||||
"file:///" + os.path.join(_datadir, "logs"),
|
||||
QtCore.QUrl.ParsingMode.TolerantMode,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PesterLogViewer(QtWidgets.QDialog):
|
||||
def __init__(self, chum, config, theme, parent):
|
||||
|
@ -140,18 +159,27 @@ class PesterLogViewer(QtWidgets.QDialog):
|
|||
self.handle = parent.profile().handle
|
||||
self.chum = chum
|
||||
self.convos = {}
|
||||
self.logpath = _datadir+"logs"
|
||||
self.logpath = _datadir + "logs"
|
||||
|
||||
self.setStyleSheet(self.theme["main/defaultwindow/style"])
|
||||
self.setWindowTitle("Pesterlogs with " + self.chum)
|
||||
|
||||
self.format = "bbcode"
|
||||
if os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)):
|
||||
self.logList = os.listdir("%s/%s/%s/%s/" % (self.logpath, self.handle, self.chum, self.format))
|
||||
if os.path.exists(
|
||||
"%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)
|
||||
):
|
||||
self.logList = os.listdir(
|
||||
"%s/%s/%s/%s/" % (self.logpath, self.handle, self.chum, self.format)
|
||||
)
|
||||
else:
|
||||
self.logList = []
|
||||
|
||||
if not os.path.exists("%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)) or len(self.logList) == 0:
|
||||
if (
|
||||
not os.path.exists(
|
||||
"%s/%s/%s/%s" % (self.logpath, self.handle, chum, self.format)
|
||||
)
|
||||
or len(self.logList) == 0
|
||||
):
|
||||
instructions = QtWidgets.QLabel("No Pesterlogs were found")
|
||||
|
||||
self.ok = QtWidgets.QPushButton("CLOSE", self)
|
||||
|
@ -166,15 +194,28 @@ class PesterLogViewer(QtWidgets.QDialog):
|
|||
|
||||
self.setLayout(layout_0)
|
||||
else:
|
||||
self.instructions = QtWidgets.QLabel("Pesterlog with " +self.chum+ " on")
|
||||
self.instructions = QtWidgets.QLabel("Pesterlog with " + self.chum + " on")
|
||||
|
||||
self.textArea = PesterLogText(theme, self.parent)
|
||||
self.textArea.setReadOnly(True)
|
||||
self.textArea.setFixedWidth(600)
|
||||
if "convo/scrollbar" in theme:
|
||||
self.textArea.setStyleSheet("QTextEdit { width:500px; %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] ))
|
||||
self.textArea.setStyleSheet(
|
||||
"QTextEdit { width:500px; %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }"
|
||||
% (
|
||||
theme["convo/textarea/style"],
|
||||
theme["convo/scrollbar/style"],
|
||||
theme["convo/scrollbar/handle"],
|
||||
theme["convo/scrollbar/downarrow"],
|
||||
theme["convo/scrollbar/uparrow"],
|
||||
theme["convo/scrollbar/uarrowstyle"],
|
||||
theme["convo/scrollbar/darrowstyle"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.textArea.setStyleSheet("QTextEdit { width:500px; %s }" % (theme["convo/textarea/style"]))
|
||||
self.textArea.setStyleSheet(
|
||||
"QTextEdit { width:500px; %s }" % (theme["convo/textarea/style"])
|
||||
)
|
||||
|
||||
self.logList.sort()
|
||||
self.logList.reverse()
|
||||
|
@ -184,20 +225,31 @@ class PesterLogViewer(QtWidgets.QDialog):
|
|||
self.tree.setFixedSize(260, 300)
|
||||
self.tree.header().hide()
|
||||
if "convo/scrollbar" in theme:
|
||||
self.tree.setStyleSheet("QTreeWidget { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["convo/textarea/style"], theme["convo/scrollbar/style"], theme["convo/scrollbar/handle"], theme["convo/scrollbar/downarrow"], theme["convo/scrollbar/uparrow"], theme["convo/scrollbar/uarrowstyle"], theme["convo/scrollbar/darrowstyle"] ))
|
||||
self.tree.setStyleSheet(
|
||||
"QTreeWidget { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }"
|
||||
% (
|
||||
theme["convo/textarea/style"],
|
||||
theme["convo/scrollbar/style"],
|
||||
theme["convo/scrollbar/handle"],
|
||||
theme["convo/scrollbar/downarrow"],
|
||||
theme["convo/scrollbar/uparrow"],
|
||||
theme["convo/scrollbar/uarrowstyle"],
|
||||
theme["convo/scrollbar/darrowstyle"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.tree.setStyleSheet("%s" % (theme["convo/textarea/style"]))
|
||||
self.tree.itemSelectionChanged.connect(self.loadSelectedLog)
|
||||
self.tree.setSortingEnabled(False)
|
||||
|
||||
child_1 = None
|
||||
last = ["",""]
|
||||
#blackbrush = QtGui.QBrush(QtCore.Qt.GlobalColor.black)
|
||||
for (i,l) in enumerate(self.logList):
|
||||
last = ["", ""]
|
||||
# blackbrush = QtGui.QBrush(QtCore.Qt.GlobalColor.black)
|
||||
for (i, l) in enumerate(self.logList):
|
||||
my = self.fileToMonthYear(l)
|
||||
if my[0] != last[0]:
|
||||
child_1 = QtWidgets.QTreeWidgetItem(["%s %s" % (my[0], my[1])])
|
||||
#child_1.setForeground(0, blackbrush)
|
||||
# child_1.setForeground(0, blackbrush)
|
||||
self.tree.addTopLevelItem(child_1)
|
||||
if i == 0:
|
||||
child_1.setExpanded(True)
|
||||
|
@ -205,7 +257,8 @@ class PesterLogViewer(QtWidgets.QDialog):
|
|||
last = self.fileToMonthYear(l)
|
||||
|
||||
self.hilight = PesterLogHighlighter(self.textArea)
|
||||
if len(self.logList) > 0: self.loadLog(self.logList[0])
|
||||
if len(self.logList) > 0:
|
||||
self.loadLog(self.logList[0])
|
||||
|
||||
self.search = PesterLogSearchInput(theme, self)
|
||||
self.search.setFocus()
|
||||
|
@ -246,29 +299,48 @@ class PesterLogViewer(QtWidgets.QDialog):
|
|||
self.loadLog(self.timeToFile(self.tree.currentItem().text(0)))
|
||||
|
||||
def loadLog(self, fname):
|
||||
fp = codecs.open("%s/%s/%s/%s/%s" % (self.logpath, self.handle, self.chum, self.format, fname), encoding='utf-8', mode='r')
|
||||
fp = codecs.open(
|
||||
"%s/%s/%s/%s/%s"
|
||||
% (self.logpath, self.handle, self.chum, self.format, fname),
|
||||
encoding="utf-8",
|
||||
mode="r",
|
||||
)
|
||||
self.textArea.clear()
|
||||
for line in fp:
|
||||
cline = line.replace("\r\n", "").replace("[/color]","</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))
|
||||
|
||||
|
|
78
mispeller.py
78
mispeller.py
|
@ -1,29 +1,55 @@
|
|||
import random
|
||||
|
||||
kbloc = [[x for x in "1234567890-="],
|
||||
[x for x in "qwertyuiop[]"],
|
||||
[x for x in "asdfghjkl:;'"],
|
||||
[x for x in "zxcvbnm,.>/?"]]
|
||||
kbloc = [
|
||||
[x for x in "1234567890-="],
|
||||
[x for x in "qwertyuiop[]"],
|
||||
[x for x in "asdfghjkl:;'"],
|
||||
[x for x in "zxcvbnm,.>/?"],
|
||||
]
|
||||
kbdict = {}
|
||||
for (i, l) in enumerate(kbloc):
|
||||
for (j, k) in enumerate(l):
|
||||
kbdict[k] = (i, j)
|
||||
|
||||
sounddict = {"a": "e", "b": "d", "c": "k", "d": "g", "e": "eh",
|
||||
"f": "ph", "g": "j", "h": "h", "i": "ai", "j": "ge",
|
||||
"k": "c", "l": "ll", "m": "n", "n": "m", "o": "oa",
|
||||
"p": "b", "q": "kw", "r": "ar", "s": "ss", "t": "d",
|
||||
"u": "you", "v": "w", "w": "wn", "x": "cks", "y": "uy", "z": "s"}
|
||||
|
||||
sounddict = {
|
||||
"a": "e",
|
||||
"b": "d",
|
||||
"c": "k",
|
||||
"d": "g",
|
||||
"e": "eh",
|
||||
"f": "ph",
|
||||
"g": "j",
|
||||
"h": "h",
|
||||
"i": "ai",
|
||||
"j": "ge",
|
||||
"k": "c",
|
||||
"l": "ll",
|
||||
"m": "n",
|
||||
"n": "m",
|
||||
"o": "oa",
|
||||
"p": "b",
|
||||
"q": "kw",
|
||||
"r": "ar",
|
||||
"s": "ss",
|
||||
"t": "d",
|
||||
"u": "you",
|
||||
"v": "w",
|
||||
"w": "wn",
|
||||
"x": "cks",
|
||||
"y": "uy",
|
||||
"z": "s",
|
||||
}
|
||||
|
||||
|
||||
def mispeller(word):
|
||||
if len(word) <= 6:
|
||||
num = 1
|
||||
else:
|
||||
num = random.choice([1,2])
|
||||
num = random.choice([1, 2])
|
||||
wordseq = list(range(0, len(word)))
|
||||
random.shuffle(wordseq)
|
||||
letters = wordseq[0:num]
|
||||
|
||||
def mistype(string, i):
|
||||
l = string[i]
|
||||
if l not in kbdict:
|
||||
|
@ -31,30 +57,42 @@ def mispeller(word):
|
|||
lpos = kbdict[l]
|
||||
newpos = lpos
|
||||
while newpos == lpos:
|
||||
newpos = ((lpos[0] + random.choice([-1, 0, 1])) % len(kbloc),
|
||||
(lpos[1] + random.choice([-1,0,1])) % len(kbloc[0]))
|
||||
string = string[0:i]+kbloc[newpos[0]][newpos[1]]+string[i+1:]
|
||||
newpos = (
|
||||
(lpos[0] + random.choice([-1, 0, 1])) % len(kbloc),
|
||||
(lpos[1] + random.choice([-1, 0, 1])) % len(kbloc[0]),
|
||||
)
|
||||
string = string[0:i] + kbloc[newpos[0]][newpos[1]] + string[i + 1 :]
|
||||
return string
|
||||
|
||||
def transpose(string, i):
|
||||
j = (i + random.choice([-1,1])) % len(string)
|
||||
j = (i + random.choice([-1, 1])) % len(string)
|
||||
l = [c for c in string]
|
||||
l[i], l[j] = l[j], l[i]
|
||||
return "".join(l)
|
||||
|
||||
def randomletter(string, i):
|
||||
string = string[0:i+1]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:]
|
||||
string = (
|
||||
string[0 : i + 1]
|
||||
+ random.choice("abcdefghijklmnopqrstuvwxyz")
|
||||
+ string[i + 1 :]
|
||||
)
|
||||
return string
|
||||
|
||||
def randomreplace(string, i):
|
||||
string = string[0:i]+random.choice("abcdefghijklmnopqrstuvwxyz")+string[i+1:]
|
||||
string = (
|
||||
string[0:i] + random.choice("abcdefghijklmnopqrstuvwxyz") + string[i + 1 :]
|
||||
)
|
||||
return string
|
||||
|
||||
def soundalike(string, i):
|
||||
try:
|
||||
c = sounddict[string[i]]
|
||||
except:
|
||||
return string
|
||||
string = string[0:i]+c+string[i+1:]
|
||||
string = string[0:i] + c + string[i + 1 :]
|
||||
return string
|
||||
func = random.choice([mistype, transpose, randomletter, randomreplace,
|
||||
soundalike])
|
||||
|
||||
func = random.choice([mistype, transpose, randomletter, randomreplace, soundalike])
|
||||
for i in letters:
|
||||
word = func(word, i)
|
||||
return word
|
||||
|
|
74
mood.py
74
mood.py
|
@ -6,28 +6,76 @@ except ImportError:
|
|||
|
||||
from generic import PesterIcon
|
||||
|
||||
|
||||
class Mood(object):
|
||||
moods = ["chummy", "rancorous", "offline", "pleasant", "distraught",
|
||||
"pranky", "smooth", "ecstatic", "relaxed", "discontent",
|
||||
"devious", "sleek", "detestful", "mirthful", "manipulative",
|
||||
"vigorous", "perky", "acceptant", "protective", "mystified",
|
||||
"amazed", "insolent", "bemused" ]
|
||||
moods = [
|
||||
"chummy",
|
||||
"rancorous",
|
||||
"offline",
|
||||
"pleasant",
|
||||
"distraught",
|
||||
"pranky",
|
||||
"smooth",
|
||||
"ecstatic",
|
||||
"relaxed",
|
||||
"discontent",
|
||||
"devious",
|
||||
"sleek",
|
||||
"detestful",
|
||||
"mirthful",
|
||||
"manipulative",
|
||||
"vigorous",
|
||||
"perky",
|
||||
"acceptant",
|
||||
"protective",
|
||||
"mystified",
|
||||
"amazed",
|
||||
"insolent",
|
||||
"bemused",
|
||||
]
|
||||
moodcats = ["chums", "trolls", "other"]
|
||||
revmoodcats = {'discontent': 'trolls', 'insolent': 'chums', 'rancorous': 'chums', 'sleek': 'trolls', 'bemused': 'chums', 'mystified': 'chums', 'pranky': 'chums', 'distraught': 'chums', 'offline': 'chums', 'chummy': 'chums', 'protective': 'other', 'vigorous': 'trolls', 'ecstatic': 'trolls', 'relaxed': 'trolls', 'pleasant': 'chums', 'manipulative': 'trolls', 'detestful': 'trolls', 'smooth': 'chums', 'mirthful': 'trolls', 'acceptant': 'trolls', 'perky': 'trolls', 'devious': 'trolls', 'amazed': 'chums'}
|
||||
revmoodcats = {
|
||||
"discontent": "trolls",
|
||||
"insolent": "chums",
|
||||
"rancorous": "chums",
|
||||
"sleek": "trolls",
|
||||
"bemused": "chums",
|
||||
"mystified": "chums",
|
||||
"pranky": "chums",
|
||||
"distraught": "chums",
|
||||
"offline": "chums",
|
||||
"chummy": "chums",
|
||||
"protective": "other",
|
||||
"vigorous": "trolls",
|
||||
"ecstatic": "trolls",
|
||||
"relaxed": "trolls",
|
||||
"pleasant": "chums",
|
||||
"manipulative": "trolls",
|
||||
"detestful": "trolls",
|
||||
"smooth": "chums",
|
||||
"mirthful": "trolls",
|
||||
"acceptant": "trolls",
|
||||
"perky": "trolls",
|
||||
"devious": "trolls",
|
||||
"amazed": "chums",
|
||||
}
|
||||
|
||||
def __init__(self, mood):
|
||||
if type(mood) is int:
|
||||
self.mood = mood
|
||||
else:
|
||||
self.mood = self.moods.index(mood)
|
||||
|
||||
def value(self):
|
||||
return self.mood
|
||||
|
||||
def name(self):
|
||||
try:
|
||||
name = self.moods[self.mood]
|
||||
except IndexError:
|
||||
name = "chummy"
|
||||
return name
|
||||
|
||||
def icon(self, theme):
|
||||
try:
|
||||
f = theme["main/chums/moods"][self.name()]["icon"]
|
||||
|
@ -35,15 +83,18 @@ class Mood(object):
|
|||
return PesterIcon(theme["main/chums/moods/chummy/icon"])
|
||||
return PesterIcon(f)
|
||||
|
||||
|
||||
class PesterMoodAction(QtCore.QObject):
|
||||
def __init__(self, m, func):
|
||||
QtCore.QObject.__init__(self)
|
||||
self.mood = m
|
||||
self.func = func
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def updateMood(self):
|
||||
self.func(self.mood)
|
||||
|
||||
|
||||
class PesterMoodHandler(QtCore.QObject):
|
||||
def __init__(self, parent, *buttons):
|
||||
QtCore.QObject.__init__(self)
|
||||
|
@ -55,13 +106,16 @@ class PesterMoodHandler(QtCore.QObject):
|
|||
b.setSelected(True)
|
||||
b.clicked.connect(b.updateMood)
|
||||
b.moodUpdated[int].connect(self.updateMood)
|
||||
|
||||
def removeButtons(self):
|
||||
for b in list(self.buttons.values()):
|
||||
b.close()
|
||||
|
||||
def showButtons(self):
|
||||
for b in list(self.buttons.values()):
|
||||
b.show()
|
||||
b.raise_()
|
||||
|
||||
@QtCore.pyqtSlot(int)
|
||||
def updateMood(self, m):
|
||||
# update MY mood
|
||||
|
@ -81,12 +135,15 @@ class PesterMoodHandler(QtCore.QObject):
|
|||
self.mainwindow.userprofile.setLastMood(newmood)
|
||||
if self.mainwindow.currentMoodIcon:
|
||||
moodicon = newmood.icon(self.mainwindow.theme)
|
||||
self.mainwindow.currentMoodIcon.setPixmap(moodicon.pixmap(moodicon.realsize()))
|
||||
self.mainwindow.currentMoodIcon.setPixmap(
|
||||
moodicon.pixmap(moodicon.realsize())
|
||||
)
|
||||
if oldmood.name() != newmood.name():
|
||||
for c in list(self.mainwindow.convos.values()):
|
||||
c.myUpdateMood(newmood)
|
||||
self.mainwindow.moodUpdated.emit()
|
||||
|
||||
|
||||
class PesterMoodButton(QtWidgets.QPushButton):
|
||||
def __init__(self, parent, **options):
|
||||
icon = PesterIcon(options["icon"])
|
||||
|
@ -100,13 +157,16 @@ class PesterMoodButton(QtWidgets.QPushButton):
|
|||
self.setStyleSheet(self.unselectedSheet)
|
||||
self.mainwindow = parent
|
||||
self.mood = Mood(options["mood"])
|
||||
|
||||
def setSelected(self, selected):
|
||||
if selected:
|
||||
self.setStyleSheet(self.selectedSheet)
|
||||
else:
|
||||
self.setStyleSheet(self.unselectedSheet)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def updateMood(self):
|
||||
# updates OUR mood
|
||||
self.moodUpdated.emit(self.mood.value())
|
||||
|
||||
moodUpdated = QtCore.pyqtSignal(int)
|
||||
|
|
|
@ -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] == "":
|
||||
|
|
41
ostools.py
41
ostools.py
|
@ -1,37 +1,44 @@
|
|||
import os
|
||||
import sys
|
||||
import platform
|
||||
|
||||
|
||||
try:
|
||||
from PyQt6.QtCore import QStandardPaths
|
||||
except ImportError:
|
||||
print("PyQt5 fallback (ostools.py)")
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
|
||||
|
||||
def isOSX():
|
||||
return sys.platform == "darwin"
|
||||
|
||||
|
||||
def isWin32():
|
||||
return sys.platform == "win32"
|
||||
|
||||
|
||||
def isLinux():
|
||||
return sys.platform.startswith("linux")
|
||||
|
||||
|
||||
def isOSXBundle():
|
||||
return isOSX() and (os.path.abspath('.').find(".app") != -1)
|
||||
return isOSX() and (os.path.abspath(".").find(".app") != -1)
|
||||
|
||||
|
||||
def isOSXLeopard():
|
||||
return isOSX() and platform.mac_ver()[0].startswith("10.5")
|
||||
|
||||
|
||||
def osVer():
|
||||
if isWin32():
|
||||
return " ".join(platform.win32_ver())
|
||||
elif isOSX():
|
||||
ver = platform.mac_ver();
|
||||
ver = platform.mac_ver()
|
||||
return " ".join((ver[0], " (", ver[2], ")"))
|
||||
elif isLinux():
|
||||
return " ".join(platform.linux_distribution())
|
||||
|
||||
|
||||
def validateDataDir():
|
||||
"""Checks if data directory is present"""
|
||||
# Define paths
|
||||
|
@ -42,7 +49,7 @@ def validateDataDir():
|
|||
errorlogs = os.path.join(datadir, "errorlogs")
|
||||
backup = os.path.join(datadir, "backup")
|
||||
js_pchum = os.path.join(datadir, "pesterchum.js")
|
||||
|
||||
|
||||
dirs = [datadir, profile, quirks, logs, errorlogs, backup]
|
||||
for d in dirs:
|
||||
if (os.path.isdir(d) == False) or (os.path.exists(d) == False):
|
||||
|
@ -50,20 +57,36 @@ def validateDataDir():
|
|||
|
||||
# pesterchum.js
|
||||
if not os.path.exists(js_pchum):
|
||||
with open(js_pchum, 'w') as f:
|
||||
with open(js_pchum, "w") as f:
|
||||
f.write("{}")
|
||||
|
||||
|
||||
def getDataDir():
|
||||
# Temporary fix for non-ascii usernames
|
||||
# If username has non-ascii characters, just store userdata
|
||||
# in the Pesterchum install directory (like before)
|
||||
try:
|
||||
if isOSX():
|
||||
return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation), "Pesterchum/")
|
||||
return os.path.join(
|
||||
QStandardPaths.writableLocation(
|
||||
QStandardPaths.StandardLocation.AppLocalDataLocation
|
||||
),
|
||||
"Pesterchum/",
|
||||
)
|
||||
elif isLinux():
|
||||
return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation), ".pesterchum/")
|
||||
return os.path.join(
|
||||
QStandardPaths.writableLocation(
|
||||
QStandardPaths.StandardLocation.HomeLocation
|
||||
),
|
||||
".pesterchum/",
|
||||
)
|
||||
else:
|
||||
return os.path.join(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation), "pesterchum/")
|
||||
return os.path.join(
|
||||
QStandardPaths.writableLocation(
|
||||
QStandardPaths.StandardLocation.AppLocalDataLocation
|
||||
),
|
||||
"pesterchum/",
|
||||
)
|
||||
except UnicodeDecodeError as e:
|
||||
print(e)
|
||||
return ''
|
||||
return ""
|
||||
|
|
175
oyoyo/client.py
175
oyoyo/client.py
|
@ -16,7 +16,8 @@
|
|||
# THE SOFTWARE.
|
||||
|
||||
import logging
|
||||
PchumLog = logging.getLogger('pchumLogger')
|
||||
|
||||
PchumLog = logging.getLogger("pchumLogger")
|
||||
|
||||
import logging
|
||||
import socket
|
||||
|
@ -29,32 +30,33 @@ from oyoyo.parse import parse_raw_irc_command
|
|||
from oyoyo import helpers
|
||||
from oyoyo.cmdhandler import CommandError
|
||||
|
||||
|
||||
class IRCClientError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class IRCClient:
|
||||
""" IRC Client class. This handles one connection to a server.
|
||||
"""IRC Client class. This handles one connection to a server.
|
||||
This can be used either with or without IRCApp ( see connect() docs )
|
||||
"""
|
||||
|
||||
def __init__(self, cmd_handler, **kwargs):
|
||||
""" the first argument should be an object with attributes/methods named
|
||||
as the irc commands. You may subclass from one of the classes in
|
||||
oyoyo.cmdhandler for convenience but it is not required. The
|
||||
methods should have arguments (prefix, args). prefix is
|
||||
"""the first argument should be an object with attributes/methods named
|
||||
as the irc commands. You may subclass from one of the classes in
|
||||
oyoyo.cmdhandler for convenience but it is not required. The
|
||||
methods should have arguments (prefix, args). prefix is
|
||||
normally the sender of the command. args is a list of arguments.
|
||||
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
|
||||
this class provides defaults for callbacks that are required for
|
||||
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
|
||||
this class provides defaults for callbacks that are required for
|
||||
normal IRC operation.
|
||||
|
||||
all other arguments should be keyword arguments. The most commonly
|
||||
used will be nick, host and port. You can also specify an "on connect"
|
||||
callback. ( check the source for others )
|
||||
|
||||
Warning: By default this class will not block on socket operations, this
|
||||
Warning: By default this class will not block on socket operations, this
|
||||
means if you use a plain while loop your app will consume 100% cpu.
|
||||
To enable blocking pass blocking=True.
|
||||
To enable blocking pass blocking=True.
|
||||
|
||||
>>> class My_Handler(DefaultCommandHandler):
|
||||
... def privmsg(self, prefix, command, args):
|
||||
|
@ -76,7 +78,7 @@ class IRCClient:
|
|||
"""
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
|
||||
self.nick = None
|
||||
self.realname = None
|
||||
self.username = None
|
||||
|
@ -93,9 +95,9 @@ class IRCClient:
|
|||
self._end = False
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
""" send a message to the connected server. all arguments are joined
|
||||
with a space for convenience, for example the following are identical
|
||||
|
||||
"""send a message to the connected server. all arguments are joined
|
||||
with a space for convenience, for example the following are identical
|
||||
|
||||
>>> cli.send("JOIN %s" % some_room)
|
||||
>>> cli.send("JOIN", some_room)
|
||||
|
||||
|
@ -104,24 +106,29 @@ class IRCClient:
|
|||
the 'encoding' keyword argument (default 'utf8').
|
||||
In python 3, all args must be of type str or bytes, *BUT* if they are
|
||||
str they will be converted to bytes with the encoding specified by the
|
||||
'encoding' keyword argument (default 'utf8').
|
||||
'encoding' keyword argument (default 'utf8').
|
||||
"""
|
||||
if self._end == True:
|
||||
return
|
||||
# Convert all args to bytes if not already
|
||||
encoding = kwargs.get('encoding') or 'utf8'
|
||||
encoding = kwargs.get("encoding") or "utf8"
|
||||
bargs = []
|
||||
for arg in args:
|
||||
if isinstance(arg, str):
|
||||
bargs.append(bytes(arg, encoding))
|
||||
elif isinstance(arg, bytes):
|
||||
bargs.append(arg)
|
||||
elif type(arg).__name__ == 'unicode':
|
||||
elif type(arg).__name__ == "unicode":
|
||||
bargs.append(arg.encode(encoding))
|
||||
else:
|
||||
PchumLog.warning('Refusing to send one of the args from provided: %s'% repr([(type(arg), arg) for arg in args]))
|
||||
raise IRCClientError('Refusing to send one of the args from provided: %s'
|
||||
% repr([(type(arg), arg) for arg in args]))
|
||||
PchumLog.warning(
|
||||
"Refusing to send one of the args from provided: %s"
|
||||
% repr([(type(arg), arg) for arg in args])
|
||||
)
|
||||
raise IRCClientError(
|
||||
"Refusing to send one of the args from provided: %s"
|
||||
% repr([(type(arg), arg) for arg in args])
|
||||
)
|
||||
|
||||
msg = bytes(" ", "UTF-8").join(bargs)
|
||||
PchumLog.info('---> send "%s"' % msg)
|
||||
|
@ -135,7 +142,9 @@ class IRCClient:
|
|||
self._end = True
|
||||
break
|
||||
try:
|
||||
ready_to_read, ready_to_write, in_error = select.select([], [self.socket], [])
|
||||
ready_to_read, ready_to_write, in_error = select.select(
|
||||
[], [self.socket], []
|
||||
)
|
||||
for x in ready_to_write:
|
||||
x.sendall(msg + bytes("\r\n", "UTF-8"))
|
||||
break
|
||||
|
@ -157,35 +166,34 @@ class IRCClient:
|
|||
# socket.timeout is deprecated in 3.10
|
||||
PchumLog.warning("TimeoutError in on send, " + str(e))
|
||||
raise socket.timeout
|
||||
except (OSError,
|
||||
IndexError,
|
||||
ValueError,
|
||||
Exception) as e:
|
||||
except (OSError, IndexError, ValueError, Exception) as e:
|
||||
PchumLog.warning("Unkown error on send, " + str(e))
|
||||
if tries >= 9:
|
||||
raise e
|
||||
tries += 1
|
||||
PchumLog.warning("Retrying send. (attempt %s)" % str(tries))
|
||||
time.sleep(0.1)
|
||||
|
||||
PchumLog.debug("ready_to_write (len %s): " % str(len(ready_to_write)) + str(ready_to_write))
|
||||
|
||||
PchumLog.debug(
|
||||
"ready_to_write (len %s): " % str(len(ready_to_write))
|
||||
+ str(ready_to_write)
|
||||
)
|
||||
except Exception as se:
|
||||
PchumLog.warning("Send Exception %s" % str(se))
|
||||
try:
|
||||
if not self.blocking and se.errno == 11:
|
||||
pass
|
||||
else:
|
||||
#raise se
|
||||
# raise se
|
||||
self._end = True # This ok?
|
||||
except AttributeError:
|
||||
#raise se
|
||||
# raise se
|
||||
self._end = True # This ok?
|
||||
|
||||
def connect(self, verify_hostname=True):
|
||||
""" initiates the connection to the server set in self.host:self.port
|
||||
"""
|
||||
PchumLog.info('connecting to %s:%s' % (self.host, self.port))
|
||||
|
||||
"""initiates the connection to the server set in self.host:self.port"""
|
||||
PchumLog.info("connecting to %s:%s" % (self.host, self.port))
|
||||
|
||||
if self.ssl == True:
|
||||
context = ssl.create_default_context()
|
||||
if verify_hostname == False:
|
||||
|
@ -193,9 +201,9 @@ class IRCClient:
|
|||
context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
bare_sock = socket.create_connection((self.host, self.port))
|
||||
self.socket = context.wrap_socket(bare_sock,
|
||||
server_hostname=self.host,
|
||||
do_handshake_on_connect=False)
|
||||
self.socket = context.wrap_socket(
|
||||
bare_sock, server_hostname=self.host, do_handshake_on_connect=False
|
||||
)
|
||||
while True:
|
||||
try:
|
||||
self.socket.do_handshake()
|
||||
|
@ -208,8 +216,8 @@ class IRCClient:
|
|||
# Disconnect for now
|
||||
self.socket.close()
|
||||
bare_sock.close()
|
||||
raise e
|
||||
|
||||
raise e
|
||||
|
||||
PchumLog.info("secure sockets version is %s" % self.socket.version())
|
||||
|
||||
else:
|
||||
|
@ -224,7 +232,7 @@ class IRCClient:
|
|||
elif self.blocking:
|
||||
self.socket.setblocking(True)
|
||||
|
||||
#try:
|
||||
# try:
|
||||
# self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
# if hasattr(socket, "TCP_KEEPIDLE"):
|
||||
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
|
||||
|
@ -232,16 +240,16 @@ class IRCClient:
|
|||
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
|
||||
# if hasattr(socket, "TCP_KEEPCNT"):
|
||||
# self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 1)
|
||||
#except Exception as e:
|
||||
# except Exception as e:
|
||||
# print(e)
|
||||
|
||||
|
||||
helpers.nick(self, self.nick)
|
||||
helpers.user(self, self.username, self.realname)
|
||||
if self.connect_cb:
|
||||
self.connect_cb(self)
|
||||
|
||||
|
||||
def conn(self):
|
||||
"""returns a generator object. """
|
||||
"""returns a generator object."""
|
||||
try:
|
||||
buffer = bytes()
|
||||
while not self._end:
|
||||
|
@ -256,7 +264,9 @@ class IRCClient:
|
|||
self._end = True
|
||||
break
|
||||
try:
|
||||
ready_to_read, ready_to_write, in_error = select.select([self.socket], [], [])
|
||||
ready_to_read, ready_to_write, in_error = select.select(
|
||||
[self.socket], [], []
|
||||
)
|
||||
for x in ready_to_read:
|
||||
buffer += x.recv(1024)
|
||||
break
|
||||
|
@ -278,17 +288,16 @@ class IRCClient:
|
|||
# socket.timeout is deprecated in 3.10
|
||||
PchumLog.warning("TimeoutError in on send, " + str(e))
|
||||
raise socket.timeout
|
||||
except (OSError,
|
||||
IndexError,
|
||||
ValueError,
|
||||
Exception) as e:
|
||||
except (OSError, IndexError, ValueError, Exception) as e:
|
||||
PchumLog.debug("Miscellaneous exception in conn, " + str(e))
|
||||
if tries >= 9:
|
||||
raise e
|
||||
tries += 1
|
||||
PchumLog.debug("Possibly retrying recv. (attempt %s)" % str(tries))
|
||||
PchumLog.debug(
|
||||
"Possibly retrying recv. (attempt %s)" % str(tries)
|
||||
)
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
except socket.timeout as e:
|
||||
PchumLog.warning("timeout in client.py, " + str(e))
|
||||
if self._end:
|
||||
|
@ -303,7 +312,7 @@ class IRCClient:
|
|||
try: # a little dance of compatibility to get the errno
|
||||
errno = e.errno
|
||||
except AttributeError:
|
||||
errno = e[0]
|
||||
errno = e[0]
|
||||
if not self.blocking and errno == 11:
|
||||
pass
|
||||
else:
|
||||
|
@ -322,7 +331,7 @@ class IRCClient:
|
|||
|
||||
for el in data:
|
||||
tags, prefix, command, args = parse_raw_irc_command(el)
|
||||
#print(tags, prefix, command, args)
|
||||
# print(tags, prefix, command, args)
|
||||
try:
|
||||
# Only need tags with tagmsg
|
||||
if command.upper() == "TAGMSG":
|
||||
|
@ -339,7 +348,7 @@ class IRCClient:
|
|||
except (OSError, ssl.SSLEOFError) as se:
|
||||
PchumLog.debug("problem: %s" % (str(se)))
|
||||
if self.socket:
|
||||
PchumLog.info('error: closing socket')
|
||||
PchumLog.info("error: closing socket")
|
||||
self.socket.close()
|
||||
raise se
|
||||
except Exception as e:
|
||||
|
@ -347,27 +356,33 @@ class IRCClient:
|
|||
raise e
|
||||
else:
|
||||
PchumLog.debug("ending while, end is %s" % self._end)
|
||||
if self.socket:
|
||||
PchumLog.info('finished: closing socket')
|
||||
if self.socket:
|
||||
PchumLog.info("finished: closing socket")
|
||||
self.socket.close()
|
||||
yield False
|
||||
|
||||
def close(self):
|
||||
# with extreme prejudice
|
||||
if self.socket:
|
||||
PchumLog.info('shutdown socket')
|
||||
#print("shutdown socket")
|
||||
PchumLog.info("shutdown socket")
|
||||
# print("shutdown socket")
|
||||
self._end = True
|
||||
try:
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
except OSError as e:
|
||||
PchumLog.debug("Error while shutting down socket, already broken? %s" % str(e))
|
||||
PchumLog.debug(
|
||||
"Error while shutting down socket, already broken? %s" % str(e)
|
||||
)
|
||||
try:
|
||||
self.socket.close()
|
||||
except OSError as e:
|
||||
PchumLog.debug("Error while closing socket, already broken? %s" % str(e))
|
||||
|
||||
PchumLog.debug(
|
||||
"Error while closing socket, already broken? %s" % str(e)
|
||||
)
|
||||
|
||||
|
||||
class IRCApp:
|
||||
""" This class manages several IRCClient instances without the use of threads.
|
||||
"""This class manages several IRCClient instances without the use of threads.
|
||||
(Non-threaded) Timer functionality is also included.
|
||||
"""
|
||||
|
||||
|
@ -384,27 +399,27 @@ class IRCApp:
|
|||
self.sleep_time = 0.5
|
||||
|
||||
def addClient(self, client, autoreconnect=False):
|
||||
""" add a client object to the application. setting autoreconnect
|
||||
"""add a client object to the application. setting autoreconnect
|
||||
to true will mean the application will attempt to reconnect the client
|
||||
after every disconnect. you can also set autoreconnect to a number
|
||||
after every disconnect. you can also set autoreconnect to a number
|
||||
to specify how many reconnects should happen.
|
||||
|
||||
warning: if you add a client that has blocking set to true,
|
||||
timers will no longer function properly """
|
||||
PchumLog.info('added client %s (ar=%s)' % (client, autoreconnect))
|
||||
timers will no longer function properly"""
|
||||
PchumLog.info("added client %s (ar=%s)" % (client, autoreconnect))
|
||||
self._clients[client] = self._ClientDesc(autoreconnect=autoreconnect)
|
||||
|
||||
def addTimer(self, seconds, cb):
|
||||
""" add a timed callback. accuracy is not specified, you can only
|
||||
"""add a timed callback. accuracy is not specified, you can only
|
||||
garuntee the callback will be called after seconds has passed.
|
||||
( the only advantage to these timers is they dont use threads )
|
||||
"""
|
||||
assert callable(cb)
|
||||
PchumLog.info('added timer to call %s in %ss' % (cb, seconds))
|
||||
PchumLog.info("added timer to call %s in %ss" % (cb, seconds))
|
||||
self._timers.append((time.time() + seconds, cb))
|
||||
|
||||
def run(self):
|
||||
""" run the application. this will block until stop() is called """
|
||||
"""run the application. this will block until stop() is called"""
|
||||
# TODO: convert this to use generators too?
|
||||
self.running = True
|
||||
while self.running:
|
||||
|
@ -413,42 +428,38 @@ class IRCApp:
|
|||
for client, clientdesc in self._clients.items():
|
||||
if clientdesc.con is None:
|
||||
clientdesc.con = client.connect()
|
||||
|
||||
|
||||
try:
|
||||
next(clientdesc.con)
|
||||
except Exception as e:
|
||||
PchumLog.error('client error %s' % str(e))
|
||||
PchumLog.error("client error %s" % str(e))
|
||||
PchumLog.error(traceback.format_exc())
|
||||
if clientdesc.autoreconnect:
|
||||
clientdesc.con = None
|
||||
clientdesc.con = None
|
||||
if isinstance(clientdesc.autoreconnect, (int, float)):
|
||||
clientdesc.autoreconnect -= 1
|
||||
found_one_alive = True
|
||||
else:
|
||||
clientdesc.con = False
|
||||
clientdesc.con = False
|
||||
else:
|
||||
found_one_alive = True
|
||||
|
||||
|
||||
if not found_one_alive:
|
||||
PchumLog.info('nothing left alive... quiting')
|
||||
self.stop()
|
||||
PchumLog.info("nothing left alive... quiting")
|
||||
self.stop()
|
||||
|
||||
now = time.time()
|
||||
timers = self._timers[:]
|
||||
self._timers = []
|
||||
for target_time, cb in timers:
|
||||
if now > target_time:
|
||||
PchumLog.info('calling timer cb %s' % cb)
|
||||
PchumLog.info("calling timer cb %s" % cb)
|
||||
cb()
|
||||
else:
|
||||
else:
|
||||
self._timers.append((target_time, cb))
|
||||
|
||||
time.sleep(self.sleep_time)
|
||||
|
||||
def stop(self):
|
||||
""" stop the application """
|
||||
"""stop the application"""
|
||||
self.running = False
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
459
parsetools.py
459
parsetools.py
|
@ -12,51 +12,56 @@ except ImportError:
|
|||
|
||||
import dataobjs
|
||||
import ostools
|
||||
|
||||
# karxi: My own contribution to this - a proper lexer.
|
||||
import pnc.lexercon as lexercon
|
||||
from generic import mysteryTime
|
||||
from quirks import ScriptQuirks
|
||||
from pyquirks import PythonQuirks
|
||||
#from luaquirks import LuaQuirks
|
||||
|
||||
PchumLog = logging.getLogger('pchumLogger')
|
||||
# from luaquirks import LuaQuirks
|
||||
|
||||
PchumLog = logging.getLogger("pchumLogger")
|
||||
|
||||
# I'll clean up the things that are no longer needed once the transition is
|
||||
# actually finished.
|
||||
QString = str
|
||||
|
||||
_ctag_begin = re.compile(r'(?i)<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("&", "&").replace(">", ">").replace("<","<")
|
||||
escaped += (
|
||||
o.replace("&", "&").replace(">", ">").replace("<", "<")
|
||||
)
|
||||
else:
|
||||
escaped += o
|
||||
else:
|
||||
|
@ -309,6 +351,7 @@ def convertTags(lexed, format="html"):
|
|||
|
||||
return escaped
|
||||
|
||||
|
||||
def _max_msg_len(mask=None, target=None, nick=None, ident=None):
|
||||
# karxi: Copied from another file of mine, and modified to work with
|
||||
# Pesterchum.
|
||||
|
@ -330,7 +373,7 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None):
|
|||
# Since we should always be able to fetch this
|
||||
# karxi: ... Which we can't, right now, unlike in the old script.
|
||||
# TODO: Resolve this issue, give it the necessary information.
|
||||
|
||||
|
||||
# If we CAN'T, stick with a length of 30, since that seems to be
|
||||
# the average maximum nowadays
|
||||
limit -= len(nick) if nick is not None else 30
|
||||
|
@ -339,7 +382,7 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None):
|
|||
# ident length
|
||||
limit -= len(ident) if nick is not None else 10
|
||||
# Maximum (?) host length
|
||||
limit -= 63 # RFC 2812
|
||||
limit -= 63 # RFC 2812
|
||||
# The target is the place this is getting sent to - a channel or a nick
|
||||
if target is not None:
|
||||
limit -= len(target)
|
||||
|
@ -351,10 +394,11 @@ def _max_msg_len(mask=None, target=None, nick=None, ident=None):
|
|||
|
||||
return limit
|
||||
|
||||
|
||||
def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
|
||||
"""Split messages so that they don't go over the length limit.
|
||||
Returns a list of the messages, neatly split.
|
||||
|
||||
|
||||
Keep in mind that there's a little bit of magic involved in this at the
|
||||
moment; some unsafe assumptions are made."""
|
||||
|
||||
|
@ -384,11 +428,21 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
|
|||
curlen = 0
|
||||
# Maximum number of characters *to* use.
|
||||
if not maxlen:
|
||||
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname)
|
||||
maxlen = _max_msg_len(
|
||||
None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname
|
||||
)
|
||||
elif maxlen < 0:
|
||||
# Subtract the (negative) length, giving us less leeway in this
|
||||
# function.
|
||||
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, ctx.mainwindow.irc.cli.realname) + maxlen
|
||||
maxlen = (
|
||||
_max_msg_len(
|
||||
None,
|
||||
None,
|
||||
ctx.mainwindow.profile().handle,
|
||||
ctx.mainwindow.irc.cli.realname,
|
||||
)
|
||||
+ maxlen
|
||||
)
|
||||
|
||||
# Defined here, but modified in the loop.
|
||||
msglen = 0
|
||||
|
@ -398,7 +452,7 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
|
|||
tags that will be needed."""
|
||||
return maxlen - curlen - (len(open_ctags) * 4)
|
||||
|
||||
#safekeeping = lexed[:]
|
||||
# safekeeping = lexed[:]
|
||||
lexed = collections.deque(lexed)
|
||||
rounds = 0
|
||||
# NOTE: This entire mess is due for a rewrite. I'll start splitting it into
|
||||
|
@ -448,14 +502,14 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
|
|||
# instead?
|
||||
subround += 1
|
||||
if debug:
|
||||
PchumLog.info("[Splitting round {}-{}...]".format(
|
||||
rounds, subround
|
||||
))
|
||||
point = msg.rfind(' ', 0, lenl)
|
||||
PchumLog.info(
|
||||
"[Splitting round {}-{}...]".format(rounds, subround)
|
||||
)
|
||||
point = msg.rfind(" ", 0, lenl)
|
||||
if point < 0:
|
||||
# No spaces to break on...ugh. Break at the last space
|
||||
# we can instead.
|
||||
point = lenl ## - 1
|
||||
point = lenl ## - 1
|
||||
# NOTE: The - 1 is for safety (but probably isn't
|
||||
# actually necessary.)
|
||||
# Split and push what we have.
|
||||
|
@ -509,16 +563,20 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
|
|||
cte = lexercon.CTagEnd("</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:
|
||||
|
|
2151
pesterchum.py
2151
pesterchum.py
File diff suppressed because it is too large
Load diff
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
290
pnc/lexercon.py
290
pnc/lexercon.py
|
@ -20,7 +20,6 @@ except NameError:
|
|||
# function appropriate to the given format - e.g. CTag.convert_pchum.
|
||||
|
||||
|
||||
|
||||
class Lexeme(object):
|
||||
def __init__(self, string, origin):
|
||||
# The 'string' property is just what it came from; the original
|
||||
|
@ -28,21 +27,26 @@ class Lexeme(object):
|
|||
# shouldn't be.
|
||||
self.string = string
|
||||
self.origin = origin
|
||||
|
||||
def __str__(self):
|
||||
##return self.string
|
||||
return self.convert(self.origin)
|
||||
|
||||
def __len__(self):
|
||||
##return len(self.string)
|
||||
return len(str(self))
|
||||
|
||||
def convert(self, format):
|
||||
# This is supposed to be overwritten by subclasses
|
||||
raise NotImplementedError
|
||||
|
||||
def rebuild(self, format):
|
||||
"""Builds a copy of the owning Lexeme as if it had 'come from' a
|
||||
different original format, and returns the result."""
|
||||
# TODO: This. Need to decide whether overloading will be required for
|
||||
# nearly every single subclass....
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def from_mo(cls, mo, origin):
|
||||
raise NotImplementedError
|
||||
|
@ -52,6 +56,7 @@ class Message(Lexeme):
|
|||
"""An object designed to represent a message, possibly containing Lexeme
|
||||
objects in their native form as well. Intended to be a combination of a
|
||||
list and a string, combining the former with the latter's methods."""
|
||||
|
||||
def __init__(self, contents, origin):
|
||||
lexer = Lexer.lexer_for(origin)()
|
||||
working = lexer.lex(contents)
|
||||
|
@ -69,7 +74,8 @@ class Message(Lexeme):
|
|||
working[i] = elt
|
||||
self.origin = origin
|
||||
self.contents = working
|
||||
self.string = ''.join(lexer.list_convert(working))
|
||||
self.string = "".join(lexer.list_convert(working))
|
||||
|
||||
# TODO: Finish all the rest of this.
|
||||
|
||||
|
||||
|
@ -81,40 +87,51 @@ class Specifier(Lexeme):
|
|||
# If this form has a more compact form, use it
|
||||
compact = False
|
||||
|
||||
|
||||
# Made so that certain odd message-ish things have a place to go. May have its
|
||||
# class changed later.
|
||||
class Chunk(Specifier):
|
||||
pass
|
||||
|
||||
|
||||
class FTag(Specifier):
|
||||
pass
|
||||
|
||||
|
||||
class CTag(Specifier):
|
||||
"""Denotes the beginning or end of a color change."""
|
||||
|
||||
sets_color = True
|
||||
|
||||
def __init__(self, string, origin, color):
|
||||
super(CTag, self).__init__(string, origin)
|
||||
# So we can also have None
|
||||
if isinstance(color, tuple):
|
||||
if len(color) < 2: raise ValueError
|
||||
if len(color) < 2:
|
||||
raise ValueError
|
||||
self.color, self.bg_color = color[:2]
|
||||
else:
|
||||
self.color = color
|
||||
self.bg_color = None
|
||||
|
||||
def has_color(self):
|
||||
if self.color is not None or self.bg_color is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def convert(self, format):
|
||||
text = ''
|
||||
text = ""
|
||||
color = self.color
|
||||
bg = self.bg_color
|
||||
if format == "irc":
|
||||
# Put in the control character for a color code.
|
||||
text = '\x03'
|
||||
text = "\x03"
|
||||
if color:
|
||||
text += color.ccode
|
||||
if bg: text += ',' + bg.ccode
|
||||
elif bg: text += "99," + bg.ccode
|
||||
if bg:
|
||||
text += "," + bg.ccode
|
||||
elif bg:
|
||||
text += "99," + bg.ccode
|
||||
elif format == "pchum":
|
||||
if not color:
|
||||
text = "</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
|
||||
|
|
504
pnc/unicolor.py
504
pnc/unicolor.py
|
@ -7,7 +7,6 @@ __all__ = ["Color"]
|
|||
# in mind that this may be phased out in the future.
|
||||
|
||||
|
||||
|
||||
from .dep.attrdict import AttrDict
|
||||
|
||||
import collections
|
||||
|
@ -21,11 +20,12 @@ else:
|
|||
basestr = str
|
||||
|
||||
|
||||
|
||||
# A named tuple for containing CIE L*a*b* (CIELAB) information.
|
||||
# NOTE TO THOSE MAINTAINING: If you don't know what that means, you're going to
|
||||
# hate yourself *and* me if you try to edit this. I know I did when I wrote it.
|
||||
LabTuple = collections.namedtuple("LabTuple", ['L', 'a', 'b'])
|
||||
LabTuple = collections.namedtuple("LabTuple", ["L", "a", "b"])
|
||||
|
||||
|
||||
class Color(object):
|
||||
# The threshold at which to consider two colors noticeably different, even
|
||||
# if only barely
|
||||
|
@ -38,7 +38,7 @@ class Color(object):
|
|||
# TODO: Split __init__, partly using __new__, so the former just has to do
|
||||
# conversions
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.ccode = ''
|
||||
self.ccode = ""
|
||||
self.closest_name = self.name = None
|
||||
nargs = len(args)
|
||||
if nargs == 1:
|
||||
|
@ -54,20 +54,25 @@ class Color(object):
|
|||
if isinstance(arg, basestr):
|
||||
# If it's a string, we've probably got a hex code, but check
|
||||
# anyway just in case
|
||||
if arg.startswith('#'):
|
||||
if arg.startswith("#"):
|
||||
self.hexstr = self.sanitize_hex(arg)
|
||||
rgb = self.hexstr_to_rgb(self.hexstr)
|
||||
self.red, self.green, self.blue = rgb
|
||||
##return
|
||||
# TODO: This.
|
||||
elif (arg.startswith('\003') and len(arg) > 1
|
||||
or len(arg) < 3 and arg.isdigit()):
|
||||
elif (
|
||||
arg.startswith("\003")
|
||||
and len(arg) > 1
|
||||
or len(arg) < 3
|
||||
and arg.isdigit()
|
||||
):
|
||||
# We have an IRC-style color code
|
||||
arg = arg.lstrip('\003')
|
||||
arg = arg.lstrip("\003")
|
||||
# Just in case
|
||||
arg = arg.split(',')[0]
|
||||
arg = arg.split(",")[0]
|
||||
cnum = int(arg)
|
||||
try: color = _irc_colors[cnum]
|
||||
try:
|
||||
color = _irc_colors[cnum]
|
||||
except LookupError:
|
||||
raise ValueError("No color for ccode %r found" % cnum)
|
||||
# We found a color; fall through and so on
|
||||
|
@ -75,7 +80,8 @@ class Color(object):
|
|||
else:
|
||||
# Presumably we have a color name
|
||||
name = arg.lower()
|
||||
try: color = _svg_colors[name]
|
||||
try:
|
||||
color = _svg_colors[name]
|
||||
except LookupError:
|
||||
raise ValueError("No color with name %r found" % name)
|
||||
# We found a color; fall through so we make this one a copy
|
||||
|
@ -103,14 +109,20 @@ class Color(object):
|
|||
self.x, self.y, self.z = self.rgb_to_xyz(*self.to_rgb_tuple())
|
||||
# Calculate the LAB color
|
||||
self.cielab = LabTuple(*self.xyz_to_cielab(*self.to_xyz_tuple()))
|
||||
if not self.closest_name: self.closest_name = self.get_svg_name()
|
||||
if not self.ccode: self.ccode = self.get_ccode()
|
||||
if not self.closest_name:
|
||||
self.closest_name = self.get_svg_name()
|
||||
if not self.ccode:
|
||||
self.ccode = self.get_ccode()
|
||||
|
||||
def __eq__(self, other):
|
||||
return hash(self) == hash(other)
|
||||
def __ne__(self, other): return not self.__eq__(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
if not isinstance(other, Color): raise TypeError
|
||||
if not isinstance(other, Color):
|
||||
raise TypeError
|
||||
return self.distance(other)
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -130,23 +142,27 @@ class Color(object):
|
|||
##result ^= self.green
|
||||
##result ^= self.blue
|
||||
##return result
|
||||
|
||||
def __repr__(self):
|
||||
##return "%s(%r)" % (type(self).__name__, str(self))
|
||||
return "%s(%r)" % (type(self).__name__,
|
||||
self.reduce_hexstr(self.hexstr))
|
||||
return "%s(%r)" % (type(self).__name__, self.reduce_hexstr(self.hexstr))
|
||||
|
||||
def __str__(self):
|
||||
##return self.reduce_hexstr(self.hexstr)
|
||||
return self.name()
|
||||
|
||||
# Builtins
|
||||
# These were yanked from Hostmask and changed around a bit
|
||||
def __getitem__(self, ind): return (self.red, self.green, self.blue)[ind]
|
||||
def __getitem__(self, ind):
|
||||
return (self.red, self.green, self.blue)[ind]
|
||||
|
||||
def __iter__(self):
|
||||
targs = (self.red, self.green, self.blue)
|
||||
for t in targs:
|
||||
yield t
|
||||
# If we got here, we're out of attributes to provide
|
||||
raise StopIteration
|
||||
|
||||
##def __len__(self):
|
||||
## # Acceptable so long as we're returning RGB like we currently (at TOW)
|
||||
## # are
|
||||
|
@ -156,8 +172,8 @@ class Color(object):
|
|||
def from_ccode(cls, ccode):
|
||||
if isinstance(ccode, basestr):
|
||||
# We were passed a string
|
||||
ccode = ccode.lstrip('\003')
|
||||
ccode = ccode.split(',')
|
||||
ccode = ccode.lstrip("\003")
|
||||
ccode = ccode.split(",")
|
||||
if len(ccode) < 2:
|
||||
fg = ccode[0]
|
||||
bg = None
|
||||
|
@ -236,9 +252,9 @@ class Color(object):
|
|||
# http://en.wikipedia.org/wiki/Color_difference
|
||||
slab, olab = self.to_cielab_tuple(), other.to_cielab_tuple()
|
||||
# Calculate the distance between the points for each
|
||||
dist = list(map(lambda p1, p2: (p2 - p1)**2, slab, olab))
|
||||
dist = list(map(lambda p1, p2: (p2 - p1) ** 2, slab, olab))
|
||||
# Add the results up, and sqrt to compensate for earlier squaring
|
||||
dist = sum(dist) ** .5
|
||||
dist = sum(dist) ** 0.5
|
||||
return dist
|
||||
|
||||
def rgb_distance(self, other):
|
||||
|
@ -252,17 +268,17 @@ class Color(object):
|
|||
### Square the results from the above
|
||||
##dist = [x**2 for x in dist]
|
||||
# Do what we WOULD have done in those two lines with a single one
|
||||
dist = list(map(lambda x1, x2: (x1 - x2)**2, srgb, orgb))
|
||||
dist = list(map(lambda x1, x2: (x1 - x2) ** 2, srgb, orgb))
|
||||
# Add the results up
|
||||
dist = sum(dist)
|
||||
# Fetch the square root to compensate for the earlier squaring
|
||||
dist **= .5
|
||||
dist **= 0.5
|
||||
return dist
|
||||
|
||||
@classmethod
|
||||
def hexstr_to_rgb(cls, hexstr):
|
||||
hexstr = cls.sanitize_hex(hexstr)
|
||||
hexstr = hexstr.lstrip('#')
|
||||
hexstr = hexstr.lstrip("#")
|
||||
if len(hexstr) == 3:
|
||||
# NOTE: This will presently never happen, due to the way
|
||||
# sanitize_hex works.
|
||||
|
@ -270,17 +286,14 @@ class Color(object):
|
|||
# first.
|
||||
# Multiplying each element by 17 expands it. Dividing it does the
|
||||
# opposite.
|
||||
result = tuple( (int(h, 16) * 17) for h in hexstr )
|
||||
result = tuple((int(h, 16) * 17) for h in hexstr)
|
||||
else:
|
||||
# This is ugly, but the purpose is simple and it's accomplished in
|
||||
# a single line...it just runs through the string, picking two
|
||||
# characters at a time and converting them from hex values to ints.
|
||||
result = tuple(
|
||||
int(hexstr[i:i+2], 16) for i in range(0, len(hexstr), 2)
|
||||
)
|
||||
result = tuple(int(hexstr[i : i + 2], 16) for i in range(0, len(hexstr), 2))
|
||||
return result
|
||||
|
||||
|
||||
@staticmethod
|
||||
def rgb_to_hexstr(red, green, blue, compress=False):
|
||||
rgb = [red, green, blue]
|
||||
|
@ -303,7 +316,7 @@ class Color(object):
|
|||
# All of our codes were doubles; compress them all down.
|
||||
result = [h[0] for h in result]
|
||||
# Join and return the result
|
||||
return '#' + ''.join(result)
|
||||
return "#" + "".join(result)
|
||||
|
||||
# These next two are from http://www.easyrgb.com/index.php?X=MATH
|
||||
@staticmethod
|
||||
|
@ -311,8 +324,10 @@ class Color(object):
|
|||
rgb = [red, green, blue]
|
||||
for i, n in enumerate(rgb):
|
||||
n /= 255
|
||||
if n > 0.04045: n = ( ( n + 0.055 ) / 1.055 ) ** 2.4
|
||||
else: n /= 12.92
|
||||
if n > 0.04045:
|
||||
n = ((n + 0.055) / 1.055) ** 2.4
|
||||
else:
|
||||
n /= 12.92
|
||||
rgb[i] = n * 100
|
||||
r, g, b = rgb
|
||||
x = r * 0.4124 + g * 0.3576 + b * 0.1805
|
||||
|
@ -322,6 +337,7 @@ class Color(object):
|
|||
##y = 0.222491598 * r + 0.71688606 * g + 0.060621486 * b
|
||||
##z = 0.013929122 * r + 0.097097002 * g + 0.71418547 * b
|
||||
return x, y, z
|
||||
|
||||
@staticmethod
|
||||
def xyz_to_cielab(x, y, z):
|
||||
# Reference X, Y, and Z
|
||||
|
@ -331,13 +347,14 @@ class Color(object):
|
|||
xyz = [x, y, z]
|
||||
for i, n in enumerate(xyz):
|
||||
n /= refs[i]
|
||||
if n > 0.008856: n **= 1/3
|
||||
if n > 0.008856:
|
||||
n **= 1 / 3
|
||||
else:
|
||||
n *= 7.787
|
||||
n += 16/116
|
||||
n += 16 / 116
|
||||
xyz[i] = n
|
||||
x, y, z = xyz
|
||||
l = (y*116) - 16
|
||||
l = (y * 116) - 16
|
||||
a = (x - y) * 500
|
||||
b = (y - z) * 200
|
||||
return l, a, b
|
||||
|
@ -347,24 +364,23 @@ class Color(object):
|
|||
"""Attempt to reduce a six-character hexadecimal color code down to a
|
||||
four-character one."""
|
||||
orig = hexstr
|
||||
hexstr = hexstr.lstrip('#')
|
||||
hexstr = hexstr.lstrip("#")
|
||||
strlen = len(hexstr)
|
||||
h = hexstr.upper()
|
||||
for i in range(0, strlen, 2):
|
||||
if h[i] != h[i+1]:
|
||||
if h[i] != h[i + 1]:
|
||||
# We found a match that wouldn't work; give back the old value.
|
||||
return orig
|
||||
else:
|
||||
# All of these can be reduced; do so and return.
|
||||
return '#' + hexstr[::2]
|
||||
|
||||
return "#" + hexstr[::2]
|
||||
|
||||
@staticmethod
|
||||
def sanitize_hex(hexstr):
|
||||
orig = hexstr
|
||||
hexstr = hexstr.upper()
|
||||
# We don't need the leading hash mark for now
|
||||
hexstr = hexstr.lstrip('#')
|
||||
hexstr = hexstr.lstrip("#")
|
||||
strlen = len(hexstr)
|
||||
if strlen == 6:
|
||||
# We just need to test this for validity. Fall through to the end.
|
||||
|
@ -372,228 +388,232 @@ class Color(object):
|
|||
elif strlen == 3:
|
||||
# We have a short (CSS style) code; duplicate all of the characters
|
||||
hexstr = [c + c for c in hexstr]
|
||||
hexstr = ''.join(hexstr)
|
||||
hexstr = "".join(hexstr)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid hexadecimal value provided: %s" % orig
|
||||
)
|
||||
raise ValueError("Invalid hexadecimal value provided: %s" % orig)
|
||||
try:
|
||||
# Make sure it works/is readable (no invalid characters).
|
||||
int(hexstr, 16)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"Invalid hexadecimal value provided: %s" % orig
|
||||
)
|
||||
return '#' + hexstr
|
||||
raise ValueError("Invalid hexadecimal value provided: %s" % orig)
|
||||
return "#" + hexstr
|
||||
|
||||
def to_cielab_tuple(self):
|
||||
# For now, just return the stored CIELAB tuple
|
||||
return self.cielab
|
||||
def to_rgb_tuple(self): return (self.red, self.green, self.blue)
|
||||
|
||||
def to_rgb_tuple(self):
|
||||
return (self.red, self.green, self.blue)
|
||||
|
||||
# 2012-12-05T17:40:39-07:00: Changed 'self.blue' to 'self.z' like it SHOULD
|
||||
# have been in the FIRST place. Ugh. How did I fuck THAT one up?
|
||||
def to_xyz_tuple(self): return (self.x, self.y, self.z)
|
||||
def to_xyz_tuple(self):
|
||||
return (self.x, self.y, self.z)
|
||||
|
||||
|
||||
# All of these are effectively equivalent to the Qt-provided colors, so they
|
||||
# could be phased out - but there's no need to, yet.
|
||||
_svg_colors = AttrDict()
|
||||
_irc_colors = {}
|
||||
_svg_colors.update({
|
||||
"aliceblue": Color(240, 248, 255),
|
||||
"antiquewhite": Color(250, 235, 215),
|
||||
"aqua": Color( 0, 255, 255),
|
||||
"aquamarine": Color(127, 255, 212),
|
||||
"azure": Color(240, 255, 255),
|
||||
"beige": Color(245, 245, 220),
|
||||
"bisque": Color(255, 228, 196),
|
||||
"black": Color( 0, 0, 0),
|
||||
"blanchedalmond": Color(255, 235, 205),
|
||||
"blue": Color( 0, 0, 255),
|
||||
"blueviolet": Color(138, 43, 226),
|
||||
"brown": Color(165, 42, 42),
|
||||
"burlywood": Color(222, 184, 135),
|
||||
"cadetblue": Color( 95, 158, 160),
|
||||
"chartreuse": Color(127, 255, 0),
|
||||
"chocolate": Color(210, 105, 30),
|
||||
"coral": Color(255, 127, 80),
|
||||
"cornflowerblue": Color(100, 149, 237),
|
||||
"cornsilk": Color(255, 248, 220),
|
||||
"crimson": Color(220, 20, 60),
|
||||
"cyan": Color( 0, 255, 255),
|
||||
"darkblue": Color( 0, 0, 139),
|
||||
"darkcyan": Color( 0, 139, 139),
|
||||
"darkgoldenrod": Color(184, 134, 11),
|
||||
"darkgray": Color(169, 169, 169),
|
||||
"darkgreen": Color( 0, 100, 0),
|
||||
"darkgrey": Color(169, 169, 169),
|
||||
"darkkhaki": Color(189, 183, 107),
|
||||
"darkmagenta": Color(139, 0, 139),
|
||||
"darkolivegreen": Color( 85, 107, 47),
|
||||
"darkorange": Color(255, 140, 0),
|
||||
"darkorchid": Color(153, 50, 204),
|
||||
"darkred": Color(139, 0, 0),
|
||||
"darksalmon": Color(233, 150, 122),
|
||||
"darkseagreen": Color(143, 188, 143),
|
||||
"darkslateblue": Color( 72, 61, 139),
|
||||
"darkslategray": Color( 47, 79, 79),
|
||||
"darkslategrey": Color( 47, 79, 79),
|
||||
"darkturquoise": Color( 0, 206, 209),
|
||||
"darkviolet": Color(148, 0, 211),
|
||||
"deeppink": Color(255, 20, 147),
|
||||
"deepskyblue": Color( 0, 191, 255),
|
||||
"dimgray": Color(105, 105, 105),
|
||||
"dimgrey": Color(105, 105, 105),
|
||||
"dodgerblue": Color( 30, 144, 255),
|
||||
"firebrick": Color(178, 34, 34),
|
||||
"floralwhite": Color(255, 250, 240),
|
||||
"forestgreen": Color( 34, 139, 34),
|
||||
"fuchsia": Color(255, 0, 255),
|
||||
"gainsboro": Color(220, 220, 220),
|
||||
"ghostwhite": Color(248, 248, 255),
|
||||
"gold": Color(255, 215, 0),
|
||||
"goldenrod": Color(218, 165, 32),
|
||||
"gray": Color(128, 128, 128),
|
||||
"grey": Color(128, 128, 128),
|
||||
"green": Color( 0, 128, 0),
|
||||
"greenyellow": Color(173, 255, 47),
|
||||
"honeydew": Color(240, 255, 240),
|
||||
"hotpink": Color(255, 105, 180),
|
||||
"indianred": Color(205, 92, 92),
|
||||
"indigo": Color( 75, 0, 130),
|
||||
"ivory": Color(255, 255, 240),
|
||||
"khaki": Color(240, 230, 140),
|
||||
"lavender": Color(230, 230, 250),
|
||||
"lavenderblush": Color(255, 240, 245),
|
||||
"lawngreen": Color(124, 252, 0),
|
||||
"lemonchiffon": Color(255, 250, 205),
|
||||
"lightblue": Color(173, 216, 230),
|
||||
"lightcoral": Color(240, 128, 128),
|
||||
"lightcyan": Color(224, 255, 255),
|
||||
"lightgoldenrodyellow": Color(250, 250, 210),
|
||||
"lightgray": Color(211, 211, 211),
|
||||
"lightgreen": Color(144, 238, 144),
|
||||
"lightgrey": Color(211, 211, 211),
|
||||
"lightpink": Color(255, 182, 193),
|
||||
"lightsalmon": Color(255, 160, 122),
|
||||
"lightseagreen": Color( 32, 178, 170),
|
||||
"lightskyblue": Color(135, 206, 250),
|
||||
"lightslategray": Color(119, 136, 153),
|
||||
"lightslategrey": Color(119, 136, 153),
|
||||
"lightsteelblue": Color(176, 196, 222),
|
||||
"lightyellow": Color(255, 255, 224),
|
||||
"lime": Color( 0, 255, 0),
|
||||
"limegreen": Color( 50, 205, 50),
|
||||
"linen": Color(250, 240, 230),
|
||||
"magenta": Color(255, 0, 255),
|
||||
"maroon": Color(128, 0, 0),
|
||||
"mediumaquamarine": Color(102, 205, 170),
|
||||
"mediumblue": Color( 0, 0, 205),
|
||||
"mediumorchid": Color(186, 85, 211),
|
||||
"mediumpurple": Color(147, 112, 219),
|
||||
"mediumseagreen": Color( 60, 179, 113),
|
||||
"mediumslateblue": Color(123, 104, 238),
|
||||
"mediumspringgreen": Color( 0, 250, 154),
|
||||
"mediumturquoise": Color( 72, 209, 204),
|
||||
"mediumvioletred": Color(199, 21, 133),
|
||||
"midnightblue": Color( 25, 25, 112),
|
||||
"mintcream": Color(245, 255, 250),
|
||||
"mistyrose": Color(255, 228, 225),
|
||||
"moccasin": Color(255, 228, 181),
|
||||
"navajowhite": Color(255, 222, 173),
|
||||
"navy": Color( 0, 0, 128),
|
||||
"oldlace": Color(253, 245, 230),
|
||||
"olive": Color(128, 128, 0),
|
||||
"olivedrab": Color(107, 142, 35),
|
||||
"orange": Color(255, 165, 0),
|
||||
"orangered": Color(255, 69, 0),
|
||||
"orchid": Color(218, 112, 214),
|
||||
"palegoldenrod": Color(238, 232, 170),
|
||||
"palegreen": Color(152, 251, 152),
|
||||
"paleturquoise": Color(175, 238, 238),
|
||||
"palevioletred": Color(219, 112, 147),
|
||||
"papayawhip": Color(255, 239, 213),
|
||||
"peachpuff": Color(255, 218, 185),
|
||||
"peru": Color(205, 133, 63),
|
||||
"pink": Color(255, 192, 203),
|
||||
"plum": Color(221, 160, 221),
|
||||
"powderblue": Color(176, 224, 230),
|
||||
"purple": Color(128, 0, 128),
|
||||
"red": Color(255, 0, 0),
|
||||
"rosybrown": Color(188, 143, 143),
|
||||
"royalblue": Color( 65, 105, 225),
|
||||
"saddlebrown": Color(139, 69, 19),
|
||||
"salmon": Color(250, 128, 114),
|
||||
"sandybrown": Color(244, 164, 96),
|
||||
"seagreen": Color( 46, 139, 87),
|
||||
"seashell": Color(255, 245, 238),
|
||||
"sienna": Color(160, 82, 45),
|
||||
"silver": Color(192, 192, 192),
|
||||
"skyblue": Color(135, 206, 235),
|
||||
"slateblue": Color(106, 90, 205),
|
||||
"slategray": Color(112, 128, 144),
|
||||
"slategrey": Color(112, 128, 144),
|
||||
"snow": Color(255, 250, 250),
|
||||
"springgreen": Color( 0, 255, 127),
|
||||
"steelblue": Color( 70, 130, 180),
|
||||
"tan": Color(210, 180, 140),
|
||||
"teal": Color( 0, 128, 128),
|
||||
"thistle": Color(216, 191, 216),
|
||||
"tomato": Color(255, 99, 71),
|
||||
"turquoise": Color( 64, 224, 208),
|
||||
"violet": Color(238, 130, 238),
|
||||
"wheat": Color(245, 222, 179),
|
||||
"white": Color(255, 255, 255),
|
||||
"whitesmoke": Color(245, 245, 245),
|
||||
"yellow": Color(255, 255, 0),
|
||||
"yellowgreen": Color(154, 205, 50)
|
||||
})
|
||||
_svg_colors.update(
|
||||
{
|
||||
"aliceblue": Color(240, 248, 255),
|
||||
"antiquewhite": Color(250, 235, 215),
|
||||
"aqua": Color(0, 255, 255),
|
||||
"aquamarine": Color(127, 255, 212),
|
||||
"azure": Color(240, 255, 255),
|
||||
"beige": Color(245, 245, 220),
|
||||
"bisque": Color(255, 228, 196),
|
||||
"black": Color(0, 0, 0),
|
||||
"blanchedalmond": Color(255, 235, 205),
|
||||
"blue": Color(0, 0, 255),
|
||||
"blueviolet": Color(138, 43, 226),
|
||||
"brown": Color(165, 42, 42),
|
||||
"burlywood": Color(222, 184, 135),
|
||||
"cadetblue": Color(95, 158, 160),
|
||||
"chartreuse": Color(127, 255, 0),
|
||||
"chocolate": Color(210, 105, 30),
|
||||
"coral": Color(255, 127, 80),
|
||||
"cornflowerblue": Color(100, 149, 237),
|
||||
"cornsilk": Color(255, 248, 220),
|
||||
"crimson": Color(220, 20, 60),
|
||||
"cyan": Color(0, 255, 255),
|
||||
"darkblue": Color(0, 0, 139),
|
||||
"darkcyan": Color(0, 139, 139),
|
||||
"darkgoldenrod": Color(184, 134, 11),
|
||||
"darkgray": Color(169, 169, 169),
|
||||
"darkgreen": Color(0, 100, 0),
|
||||
"darkgrey": Color(169, 169, 169),
|
||||
"darkkhaki": Color(189, 183, 107),
|
||||
"darkmagenta": Color(139, 0, 139),
|
||||
"darkolivegreen": Color(85, 107, 47),
|
||||
"darkorange": Color(255, 140, 0),
|
||||
"darkorchid": Color(153, 50, 204),
|
||||
"darkred": Color(139, 0, 0),
|
||||
"darksalmon": Color(233, 150, 122),
|
||||
"darkseagreen": Color(143, 188, 143),
|
||||
"darkslateblue": Color(72, 61, 139),
|
||||
"darkslategray": Color(47, 79, 79),
|
||||
"darkslategrey": Color(47, 79, 79),
|
||||
"darkturquoise": Color(0, 206, 209),
|
||||
"darkviolet": Color(148, 0, 211),
|
||||
"deeppink": Color(255, 20, 147),
|
||||
"deepskyblue": Color(0, 191, 255),
|
||||
"dimgray": Color(105, 105, 105),
|
||||
"dimgrey": Color(105, 105, 105),
|
||||
"dodgerblue": Color(30, 144, 255),
|
||||
"firebrick": Color(178, 34, 34),
|
||||
"floralwhite": Color(255, 250, 240),
|
||||
"forestgreen": Color(34, 139, 34),
|
||||
"fuchsia": Color(255, 0, 255),
|
||||
"gainsboro": Color(220, 220, 220),
|
||||
"ghostwhite": Color(248, 248, 255),
|
||||
"gold": Color(255, 215, 0),
|
||||
"goldenrod": Color(218, 165, 32),
|
||||
"gray": Color(128, 128, 128),
|
||||
"grey": Color(128, 128, 128),
|
||||
"green": Color(0, 128, 0),
|
||||
"greenyellow": Color(173, 255, 47),
|
||||
"honeydew": Color(240, 255, 240),
|
||||
"hotpink": Color(255, 105, 180),
|
||||
"indianred": Color(205, 92, 92),
|
||||
"indigo": Color(75, 0, 130),
|
||||
"ivory": Color(255, 255, 240),
|
||||
"khaki": Color(240, 230, 140),
|
||||
"lavender": Color(230, 230, 250),
|
||||
"lavenderblush": Color(255, 240, 245),
|
||||
"lawngreen": Color(124, 252, 0),
|
||||
"lemonchiffon": Color(255, 250, 205),
|
||||
"lightblue": Color(173, 216, 230),
|
||||
"lightcoral": Color(240, 128, 128),
|
||||
"lightcyan": Color(224, 255, 255),
|
||||
"lightgoldenrodyellow": Color(250, 250, 210),
|
||||
"lightgray": Color(211, 211, 211),
|
||||
"lightgreen": Color(144, 238, 144),
|
||||
"lightgrey": Color(211, 211, 211),
|
||||
"lightpink": Color(255, 182, 193),
|
||||
"lightsalmon": Color(255, 160, 122),
|
||||
"lightseagreen": Color(32, 178, 170),
|
||||
"lightskyblue": Color(135, 206, 250),
|
||||
"lightslategray": Color(119, 136, 153),
|
||||
"lightslategrey": Color(119, 136, 153),
|
||||
"lightsteelblue": Color(176, 196, 222),
|
||||
"lightyellow": Color(255, 255, 224),
|
||||
"lime": Color(0, 255, 0),
|
||||
"limegreen": Color(50, 205, 50),
|
||||
"linen": Color(250, 240, 230),
|
||||
"magenta": Color(255, 0, 255),
|
||||
"maroon": Color(128, 0, 0),
|
||||
"mediumaquamarine": Color(102, 205, 170),
|
||||
"mediumblue": Color(0, 0, 205),
|
||||
"mediumorchid": Color(186, 85, 211),
|
||||
"mediumpurple": Color(147, 112, 219),
|
||||
"mediumseagreen": Color(60, 179, 113),
|
||||
"mediumslateblue": Color(123, 104, 238),
|
||||
"mediumspringgreen": Color(0, 250, 154),
|
||||
"mediumturquoise": Color(72, 209, 204),
|
||||
"mediumvioletred": Color(199, 21, 133),
|
||||
"midnightblue": Color(25, 25, 112),
|
||||
"mintcream": Color(245, 255, 250),
|
||||
"mistyrose": Color(255, 228, 225),
|
||||
"moccasin": Color(255, 228, 181),
|
||||
"navajowhite": Color(255, 222, 173),
|
||||
"navy": Color(0, 0, 128),
|
||||
"oldlace": Color(253, 245, 230),
|
||||
"olive": Color(128, 128, 0),
|
||||
"olivedrab": Color(107, 142, 35),
|
||||
"orange": Color(255, 165, 0),
|
||||
"orangered": Color(255, 69, 0),
|
||||
"orchid": Color(218, 112, 214),
|
||||
"palegoldenrod": Color(238, 232, 170),
|
||||
"palegreen": Color(152, 251, 152),
|
||||
"paleturquoise": Color(175, 238, 238),
|
||||
"palevioletred": Color(219, 112, 147),
|
||||
"papayawhip": Color(255, 239, 213),
|
||||
"peachpuff": Color(255, 218, 185),
|
||||
"peru": Color(205, 133, 63),
|
||||
"pink": Color(255, 192, 203),
|
||||
"plum": Color(221, 160, 221),
|
||||
"powderblue": Color(176, 224, 230),
|
||||
"purple": Color(128, 0, 128),
|
||||
"red": Color(255, 0, 0),
|
||||
"rosybrown": Color(188, 143, 143),
|
||||
"royalblue": Color(65, 105, 225),
|
||||
"saddlebrown": Color(139, 69, 19),
|
||||
"salmon": Color(250, 128, 114),
|
||||
"sandybrown": Color(244, 164, 96),
|
||||
"seagreen": Color(46, 139, 87),
|
||||
"seashell": Color(255, 245, 238),
|
||||
"sienna": Color(160, 82, 45),
|
||||
"silver": Color(192, 192, 192),
|
||||
"skyblue": Color(135, 206, 235),
|
||||
"slateblue": Color(106, 90, 205),
|
||||
"slategray": Color(112, 128, 144),
|
||||
"slategrey": Color(112, 128, 144),
|
||||
"snow": Color(255, 250, 250),
|
||||
"springgreen": Color(0, 255, 127),
|
||||
"steelblue": Color(70, 130, 180),
|
||||
"tan": Color(210, 180, 140),
|
||||
"teal": Color(0, 128, 128),
|
||||
"thistle": Color(216, 191, 216),
|
||||
"tomato": Color(255, 99, 71),
|
||||
"turquoise": Color(64, 224, 208),
|
||||
"violet": Color(238, 130, 238),
|
||||
"wheat": Color(245, 222, 179),
|
||||
"white": Color(255, 255, 255),
|
||||
"whitesmoke": Color(245, 245, 245),
|
||||
"yellow": Color(255, 255, 0),
|
||||
"yellowgreen": Color(154, 205, 50),
|
||||
}
|
||||
)
|
||||
for k, v in list(_svg_colors.items()):
|
||||
v.closest_name = v.name = k
|
||||
|
||||
# 2012-12-08T14:29-07:00: Copied over from Colors.hexstr_for_ccodes in the main
|
||||
# textsub file, and subsequently modified.
|
||||
_irc_colors.update({
|
||||
# These are all taken from *MY* XChat settings - they aren't guaranteed to
|
||||
# please everyone!
|
||||
0: Color(0xFFFFFF),
|
||||
1: Color(0x1F1F1F),
|
||||
2: Color(0x00007F),
|
||||
3: Color(0x007F00),
|
||||
4: Color(0xFF0000),
|
||||
5: Color(0x7F0000),
|
||||
6: Color(0x9C009C),
|
||||
7: Color(0xFC7F00),
|
||||
8: Color(0xFFFF00),
|
||||
9: Color(0x00FC00),
|
||||
##10: Color(0x009393),
|
||||
10: Color(0x008282),
|
||||
11: Color(0x00FFFF),
|
||||
12: Color(0x0000FC),
|
||||
13: Color(0xFF00FF),
|
||||
14: Color(0x7F7F7F),
|
||||
15: Color(0xD2D2D2),
|
||||
# My local colors
|
||||
16: Color(0xCCCCCC),
|
||||
##17: Color(0x000000), # Commented out 'til readability checks are in
|
||||
17: Color(0x1F1F1F),
|
||||
18: Color(0x000056),
|
||||
19: Color(0x008141),
|
||||
20: Color(0xE00707),
|
||||
21: Color(0xA10000),
|
||||
22: Color(0x6A006A),
|
||||
23: Color(0xA15000),
|
||||
24: Color(0xA1A100),
|
||||
25: Color(0x416600),
|
||||
##26: Color(0x008282),
|
||||
26: Color(0x005682),
|
||||
27: Color(0x00D5F2),
|
||||
28: Color(0x0715CD),
|
||||
29: Color(0x99004D),
|
||||
30: Color(0x323232),
|
||||
31: Color(0x929292),
|
||||
|
||||
99: Color(0x999999) # Until I think of a better solution to this
|
||||
})
|
||||
_irc_colors.update(
|
||||
{
|
||||
# These are all taken from *MY* XChat settings - they aren't guaranteed to
|
||||
# please everyone!
|
||||
0: Color(0xFFFFFF),
|
||||
1: Color(0x1F1F1F),
|
||||
2: Color(0x00007F),
|
||||
3: Color(0x007F00),
|
||||
4: Color(0xFF0000),
|
||||
5: Color(0x7F0000),
|
||||
6: Color(0x9C009C),
|
||||
7: Color(0xFC7F00),
|
||||
8: Color(0xFFFF00),
|
||||
9: Color(0x00FC00),
|
||||
##10: Color(0x009393),
|
||||
10: Color(0x008282),
|
||||
11: Color(0x00FFFF),
|
||||
12: Color(0x0000FC),
|
||||
13: Color(0xFF00FF),
|
||||
14: Color(0x7F7F7F),
|
||||
15: Color(0xD2D2D2),
|
||||
# My local colors
|
||||
16: Color(0xCCCCCC),
|
||||
##17: Color(0x000000), # Commented out 'til readability checks are in
|
||||
17: Color(0x1F1F1F),
|
||||
18: Color(0x000056),
|
||||
19: Color(0x008141),
|
||||
20: Color(0xE00707),
|
||||
21: Color(0xA10000),
|
||||
22: Color(0x6A006A),
|
||||
23: Color(0xA15000),
|
||||
24: Color(0xA1A100),
|
||||
25: Color(0x416600),
|
||||
##26: Color(0x008282),
|
||||
26: Color(0x005682),
|
||||
27: Color(0x00D5F2),
|
||||
28: Color(0x0715CD),
|
||||
29: Color(0x99004D),
|
||||
30: Color(0x323232),
|
||||
31: Color(0x929292),
|
||||
99: Color(0x999999), # Until I think of a better solution to this
|
||||
}
|
||||
)
|
||||
for k, v in list(_irc_colors.items()):
|
||||
v.ccode = "%02d" % k
|
||||
del k, v
|
||||
|
|
546
profile.py
546
profile.py
File diff suppressed because it is too large
Load diff
836
pyinstaller.py
836
pyinstaller.py
|
@ -5,425 +5,647 @@ import shutil
|
|||
import PyInstaller.__main__
|
||||
|
||||
is_64bit = sys.maxsize > 2**32
|
||||
#is_linux = sys.platform.startswith("linux")
|
||||
# is_linux = sys.platform.startswith("linux")
|
||||
exclude_modules = []
|
||||
#if is_linux == False:
|
||||
# if is_linux == False:
|
||||
# print("Not Linux, excluding pygame.")
|
||||
# exclude_modules.append('pygame')
|
||||
add_data = ['quirks;quirks',
|
||||
'smilies;smilies',
|
||||
'themes;themes',
|
||||
'docs;docs',
|
||||
'README.md;.',
|
||||
'LICENSE;.',
|
||||
'CHANGELOG.md;.',
|
||||
'PCskins.png;.',
|
||||
'Pesterchum.png;.']
|
||||
data_folders = {'quirks': 'quirks',
|
||||
'smilies': 'smilies',
|
||||
'themes': 'themes',
|
||||
'docs': 'docs'}
|
||||
data_files = {'README.md': 'README.md.txt',
|
||||
'LICENSE': 'LICENSE.txt',
|
||||
'CHANGELOG.md': 'CHANGELOG.md.txt',
|
||||
'PCskins.png': '.',
|
||||
'Pesterchum.png': '.'}
|
||||
data_files_linux = {'README.md': 'README.md',
|
||||
'LICENSE': 'LICENSE.txt',
|
||||
'CHANGELOG.md': 'CHANGELOG.md',
|
||||
'PCskins.png': '.',
|
||||
'Pesterchum.png': '.'}
|
||||
add_data = [
|
||||
"quirks;quirks",
|
||||
"smilies;smilies",
|
||||
"themes;themes",
|
||||
"docs;docs",
|
||||
"README.md;.",
|
||||
"LICENSE;.",
|
||||
"CHANGELOG.md;.",
|
||||
"PCskins.png;.",
|
||||
"Pesterchum.png;.",
|
||||
]
|
||||
data_folders = {
|
||||
"quirks": "quirks",
|
||||
"smilies": "smilies",
|
||||
"themes": "themes",
|
||||
"docs": "docs",
|
||||
}
|
||||
data_files = {
|
||||
"README.md": "README.md.txt",
|
||||
"LICENSE": "LICENSE.txt",
|
||||
"CHANGELOG.md": "CHANGELOG.md.txt",
|
||||
"PCskins.png": ".",
|
||||
"Pesterchum.png": ".",
|
||||
}
|
||||
data_files_linux = {
|
||||
"README.md": "README.md",
|
||||
"LICENSE": "LICENSE.txt",
|
||||
"CHANGELOG.md": "CHANGELOG.md",
|
||||
"PCskins.png": ".",
|
||||
"Pesterchum.png": ".",
|
||||
}
|
||||
# Some of these might not be required anymore,
|
||||
# newer versions of PyInstaller claim to exclude certain problematic DDLs automatically.
|
||||
upx_exclude = ["qwindows.dll",
|
||||
upx_exclude = [
|
||||
"qwindows.dll",
|
||||
"Qt6Core.dll",
|
||||
"Qt6Gui.dll",
|
||||
"vcruntime140.dll",
|
||||
"MSVCP140.dll",
|
||||
"MSVCP140_1.dll"
|
||||
"api-ms-win-core-console-l1-1-0.dll",\
|
||||
"api-ms-win-core-console-l1-1-0.dll",\
|
||||
"api-ms-win-core-console-l1-2-0.dll",\
|
||||
"api-ms-win-core-datetime-l1-1-0.dll",\
|
||||
"api-ms-win-core-debug-l1-1-0.dll",\
|
||||
"api-ms-win-core-errorhandling-l1-1-0.dll",\
|
||||
"api-ms-win-core-file-l1-1-0.dll",\
|
||||
"api-ms-win-core-file-l1-2-0.dll",\
|
||||
"api-ms-win-core-file-l2-1-0.dll",\
|
||||
"api-ms-win-core-handle-l1-1-0.dll",\
|
||||
"api-ms-win-core-heap-l1-1-0.dll",\
|
||||
"api-ms-win-core-interlocked-l1-1-0.dll",\
|
||||
"api-ms-win-core-libraryloader-l1-1-0.dll",\
|
||||
"api-ms-win-core-localization-l1-2-0.dll",\
|
||||
"api-ms-win-core-memory-l1-1-0.dll",\
|
||||
"api-ms-win-core-namedpipe-l1-1-0.dll",\
|
||||
"api-ms-win-core-processenvironment-l1-1-0.dll",\
|
||||
"api-ms-win-core-processthreads-l1-1-0.dll",\
|
||||
"api-ms-win-core-processthreads-l1-1-1.dll",\
|
||||
"api-ms-win-core-profile-l1-1-0.dll",\
|
||||
"api-ms-win-core-rtlsupport-l1-1-0.dll",\
|
||||
"api-ms-win-core-string-l1-1-0.dll",\
|
||||
"api-ms-win-core-synch-l1-1-0.dll",\
|
||||
"api-ms-win-core-synch-l1-2-0.dll",\
|
||||
"api-ms-win-core-sysinfo-l1-1-0.dll",\
|
||||
"api-ms-win-core-timezone-l1-1-0.dll",\
|
||||
"api-ms-win-core-util-l1-1-0.dll",\
|
||||
"API-MS-Win-core-xstate-l2-1-0.dll",\
|
||||
"api-ms-win-crt-conio-l1-1-0.dll",\
|
||||
"api-ms-win-crt-convert-l1-1-0.dll",\
|
||||
"api-ms-win-crt-environment-l1-1-0.dll",\
|
||||
"api-ms-win-crt-filesystem-l1-1-0.dll",\
|
||||
"api-ms-win-crt-heap-l1-1-0.dll",\
|
||||
"api-ms-win-crt-locale-l1-1-0.dll",\
|
||||
"api-ms-win-crt-math-l1-1-0.dll",\
|
||||
"api-ms-win-crt-multibyte-l1-1-0.dll",\
|
||||
"api-ms-win-crt-private-l1-1-0.dll",\
|
||||
"api-ms-win-crt-process-l1-1-0.dll",\
|
||||
"api-ms-win-crt-runtime-l1-1-0.dll",\
|
||||
"api-ms-win-crt-stdio-l1-1-0.dll",\
|
||||
"api-ms-win-crt-string-l1-1-0.dll",\
|
||||
"api-ms-win-crt-time-l1-1-0.dll",\
|
||||
"api-ms-win-crt-utility-l1-1-0.dll",\
|
||||
"ucrtbase.dll"]
|
||||
"MSVCP140_1.dll" "api-ms-win-core-console-l1-1-0.dll",
|
||||
"api-ms-win-core-console-l1-1-0.dll",
|
||||
"api-ms-win-core-console-l1-2-0.dll",
|
||||
"api-ms-win-core-datetime-l1-1-0.dll",
|
||||
"api-ms-win-core-debug-l1-1-0.dll",
|
||||
"api-ms-win-core-errorhandling-l1-1-0.dll",
|
||||
"api-ms-win-core-file-l1-1-0.dll",
|
||||
"api-ms-win-core-file-l1-2-0.dll",
|
||||
"api-ms-win-core-file-l2-1-0.dll",
|
||||
"api-ms-win-core-handle-l1-1-0.dll",
|
||||
"api-ms-win-core-heap-l1-1-0.dll",
|
||||
"api-ms-win-core-interlocked-l1-1-0.dll",
|
||||
"api-ms-win-core-libraryloader-l1-1-0.dll",
|
||||
"api-ms-win-core-localization-l1-2-0.dll",
|
||||
"api-ms-win-core-memory-l1-1-0.dll",
|
||||
"api-ms-win-core-namedpipe-l1-1-0.dll",
|
||||
"api-ms-win-core-processenvironment-l1-1-0.dll",
|
||||
"api-ms-win-core-processthreads-l1-1-0.dll",
|
||||
"api-ms-win-core-processthreads-l1-1-1.dll",
|
||||
"api-ms-win-core-profile-l1-1-0.dll",
|
||||
"api-ms-win-core-rtlsupport-l1-1-0.dll",
|
||||
"api-ms-win-core-string-l1-1-0.dll",
|
||||
"api-ms-win-core-synch-l1-1-0.dll",
|
||||
"api-ms-win-core-synch-l1-2-0.dll",
|
||||
"api-ms-win-core-sysinfo-l1-1-0.dll",
|
||||
"api-ms-win-core-timezone-l1-1-0.dll",
|
||||
"api-ms-win-core-util-l1-1-0.dll",
|
||||
"API-MS-Win-core-xstate-l2-1-0.dll",
|
||||
"api-ms-win-crt-conio-l1-1-0.dll",
|
||||
"api-ms-win-crt-convert-l1-1-0.dll",
|
||||
"api-ms-win-crt-environment-l1-1-0.dll",
|
||||
"api-ms-win-crt-filesystem-l1-1-0.dll",
|
||||
"api-ms-win-crt-heap-l1-1-0.dll",
|
||||
"api-ms-win-crt-locale-l1-1-0.dll",
|
||||
"api-ms-win-crt-math-l1-1-0.dll",
|
||||
"api-ms-win-crt-multibyte-l1-1-0.dll",
|
||||
"api-ms-win-crt-private-l1-1-0.dll",
|
||||
"api-ms-win-crt-process-l1-1-0.dll",
|
||||
"api-ms-win-crt-runtime-l1-1-0.dll",
|
||||
"api-ms-win-crt-stdio-l1-1-0.dll",
|
||||
"api-ms-win-crt-string-l1-1-0.dll",
|
||||
"api-ms-win-crt-time-l1-1-0.dll",
|
||||
"api-ms-win-crt-utility-l1-1-0.dll",
|
||||
"ucrtbase.dll",
|
||||
]
|
||||
|
||||
delete_builddist = ''
|
||||
upx_enabled = ''
|
||||
package_universal_crt = ''
|
||||
onefile = ''
|
||||
windowed = ''
|
||||
delete_builddist = ""
|
||||
upx_enabled = ""
|
||||
package_universal_crt = ""
|
||||
onefile = ""
|
||||
windowed = ""
|
||||
|
||||
try:
|
||||
print("This is a script to make building with Pyinstaller a bit more conventient.")
|
||||
|
||||
while (delete_builddist != 'y') and (delete_builddist != 'n'):
|
||||
while (delete_builddist != "y") and (delete_builddist != "n"):
|
||||
delete_builddist = input("Delete build & dist folders? (Y/N): ").lower()
|
||||
if delete_builddist == "y":
|
||||
try:
|
||||
shutil.rmtree('dist')
|
||||
shutil.rmtree("dist")
|
||||
except FileNotFoundError as e:
|
||||
print(e)
|
||||
try:
|
||||
shutil.rmtree('build')
|
||||
shutil.rmtree("build")
|
||||
except FileNotFoundError as e:
|
||||
print(e)
|
||||
|
||||
while (upx_enabled != 'y') and (upx_enabled != 'n'):
|
||||
|
||||
while (upx_enabled != "y") and (upx_enabled != "n"):
|
||||
upx_enabled = input("Enable UPX? (Y/N): ").lower()
|
||||
if upx_enabled == 'y':
|
||||
if upx_enabled == "y":
|
||||
print("If upx is on your path you don't need to include anything here.")
|
||||
if is_64bit == True:
|
||||
upx_dir = input("UPX directory [D:\\upx-3.96-win64]: ")
|
||||
if upx_dir == '':
|
||||
upx_dir = "D:\\upx-3.96-win64" # Default dir for me :)
|
||||
if upx_dir == "":
|
||||
upx_dir = "D:\\upx-3.96-win64" # Default dir for me :)
|
||||
else:
|
||||
upx_dir = input("UPX directory [D:\\upx-3.96-win32]: ")
|
||||
if upx_dir == '':
|
||||
upx_dir = "D:\\upx-3.96-win32" # Default dir for me :)
|
||||
if upx_dir == "":
|
||||
upx_dir = "D:\\upx-3.96-win32" # Default dir for me :)
|
||||
print("upx_dir = " + upx_dir)
|
||||
elif upx_enabled == 'n':
|
||||
upx_dir = ''
|
||||
|
||||
while (windowed != 'y') and (windowed != 'n'):
|
||||
elif upx_enabled == "n":
|
||||
upx_dir = ""
|
||||
|
||||
while (windowed != "y") and (windowed != "n"):
|
||||
windowed = input("Build with '--windowed'? (Y/N): ").lower()
|
||||
|
||||
if sys.platform == 'win32':
|
||||
print("(https://pyinstaller.readthedocs.io/en/stable/usage.html?highlight=sdk#windows)")
|
||||
while (package_universal_crt != 'y') and (package_universal_crt != 'n'):
|
||||
package_universal_crt = input("Try to include universal CRT? (Y/N): ").lower()
|
||||
if package_universal_crt == 'y':
|
||||
|
||||
if sys.platform == "win32":
|
||||
print(
|
||||
"(https://pyinstaller.readthedocs.io/en/stable/usage.html?highlight=sdk#windows)"
|
||||
)
|
||||
while (package_universal_crt != "y") and (package_universal_crt != "n"):
|
||||
package_universal_crt = input(
|
||||
"Try to include universal CRT? (Y/N): "
|
||||
).lower()
|
||||
if package_universal_crt == "y":
|
||||
if is_64bit == True:
|
||||
crt_path = input("Universal CRT: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64]: ")
|
||||
if crt_path == '':
|
||||
#crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64" # Default directory.
|
||||
crt_path = os.path.join('C:%s' % os.sep, 'Program Files (x86)', 'Windows Kits', '10', '10.0.19041.0', 'ucrt', 'DLLs', 'x64')
|
||||
crt_path = input(
|
||||
"Universal CRT: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64]: "
|
||||
)
|
||||
if crt_path == "":
|
||||
# crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x64" # Default directory.
|
||||
crt_path = os.path.join(
|
||||
"C:%s" % os.sep,
|
||||
"Program Files (x86)",
|
||||
"Windows Kits",
|
||||
"10",
|
||||
"10.0.19041.0",
|
||||
"ucrt",
|
||||
"DLLs",
|
||||
"x64",
|
||||
)
|
||||
else:
|
||||
crt_path = input("Extra path: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86]: ")
|
||||
if crt_path == '':
|
||||
#crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86" # Default directory.
|
||||
crt_path = os.path.join('C:%s' % os.sep, 'Program Files (x86)', 'Windows Kits', '10', '10.0.19041.0', 'ucrt', 'DLLs', 'x86')
|
||||
crt_path = input(
|
||||
"Extra path: [C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86]: "
|
||||
)
|
||||
if crt_path == "":
|
||||
# crt_path = "C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\10.0.19041.0\\ucrt\\DLLs\\x86" # Default directory.
|
||||
crt_path = os.path.join(
|
||||
"C:%s" % os.sep,
|
||||
"Program Files (x86)",
|
||||
"Windows Kits",
|
||||
"10",
|
||||
"10.0.19041.0",
|
||||
"ucrt",
|
||||
"DLLs",
|
||||
"x86",
|
||||
)
|
||||
print("crt_path = " + crt_path)
|
||||
|
||||
if (sys.platform == 'win32') or (sys.platform == 'linux'):
|
||||
while (onefile != 'y') and (onefile != 'n'):
|
||||
|
||||
if (sys.platform == "win32") or (sys.platform == "linux"):
|
||||
while (onefile != "y") and (onefile != "n"):
|
||||
onefile = input("Build with '--onefile'? (Y/N): ").lower()
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.exit("KeyboardInterrupt")
|
||||
|
||||
#Windows
|
||||
if sys.platform == 'win32':
|
||||
# Windows
|
||||
if sys.platform == "win32":
|
||||
run_win32 = [
|
||||
'pesterchum.py',
|
||||
'--name=Pesterchum',
|
||||
"pesterchum.py",
|
||||
"--name=Pesterchum",
|
||||
#'--noconfirm', # Overwrite output directory.
|
||||
#'--windowed', # Hide console
|
||||
'--icon=pesterchum.ico',
|
||||
'--clean', # Clear cache
|
||||
|
||||
"--icon=pesterchum.ico",
|
||||
"--clean", # Clear cache
|
||||
]
|
||||
|
||||
if (sys.version_info.major == 3) & (sys.version_info.minor == 8):
|
||||
exclude_modules.append('tkinter')
|
||||
if upx_enabled == 'y':
|
||||
exclude_modules.append("tkinter")
|
||||
if upx_enabled == "y":
|
||||
if os.path.isdir(upx_dir):
|
||||
run_win32.append('--upx-dir=%s' % upx_dir)
|
||||
run_win32.append("--upx-dir=%s" % upx_dir)
|
||||
for x in upx_exclude:
|
||||
run_win32.append('--upx-exclude=%s' % x )
|
||||
run_win32.append("--upx-exclude=%s" % x)
|
||||
# Lower case variants are required.
|
||||
run_win32.append('--upx-exclude=%s' % x.lower() )
|
||||
elif upx_enabled == 'n':
|
||||
run_win32.append('--noupx')
|
||||
run_win32.append("--upx-exclude=%s" % x.lower())
|
||||
elif upx_enabled == "n":
|
||||
run_win32.append("--noupx")
|
||||
for x in exclude_modules:
|
||||
run_win32.append('--exclude-module=%s' % x )
|
||||
if windowed == 'y':
|
||||
run_win32.append('--windowed')
|
||||
if onefile == 'y':
|
||||
run_win32.append('--onefile')
|
||||
elif onefile == 'n':
|
||||
run_win32.append("--exclude-module=%s" % x)
|
||||
if windowed == "y":
|
||||
run_win32.append("--windowed")
|
||||
if onefile == "y":
|
||||
run_win32.append("--onefile")
|
||||
elif onefile == "n":
|
||||
for x in add_data:
|
||||
run_win32.append('--add-data=%s' % x )
|
||||
run_win32.append("--add-data=%s" % x)
|
||||
|
||||
|
||||
if package_universal_crt == 'y':
|
||||
run_win32.append('--paths=\"%s\"' % crt_path)
|
||||
if package_universal_crt == "y":
|
||||
run_win32.append('--paths="%s"' % crt_path)
|
||||
if os.path.exists(crt_path):
|
||||
if is_64bit == False:
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\API-MS-Win-core-xstate-l2-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\ucrtbase.dll;.' % crt_path)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\API-MS-Win-core-xstate-l2-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append("--add-binary=%s\\ucrtbase.dll;." % crt_path)
|
||||
elif is_64bit == True:
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;.' % crt_path)
|
||||
run_win32.append('--add-binary=%s\\ucrtbase.dll;.' % crt_path)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-console-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-console-l1-2-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-datetime-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-debug-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-errorhandling-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-file-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-file-l1-2-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-file-l2-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-handle-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-heap-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-interlocked-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-libraryloader-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-localization-l1-2-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-memory-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-namedpipe-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-processenvironment-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-processthreads-l1-1-1.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-profile-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-rtlsupport-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-string-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-synch-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-synch-l1-2-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-sysinfo-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-timezone-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-core-util-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-conio-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-convert-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-environment-l1-1-0.dll;."
|
||||
% crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-filesystem-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-heap-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-locale-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-math-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-multibyte-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-private-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-process-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-runtime-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-stdio-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-string-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-time-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append(
|
||||
"--add-binary=%s\\api-ms-win-crt-utility-l1-1-0.dll;." % crt_path
|
||||
)
|
||||
run_win32.append("--add-binary=%s\\ucrtbase.dll;." % crt_path)
|
||||
print(run_win32)
|
||||
PyInstaller.__main__.run(run_win32)
|
||||
|
||||
if onefile == 'y':
|
||||
|
||||
if onefile == "y":
|
||||
# There's more proper ways to do this, but this doesn't require changing our paths
|
||||
for x in data_folders:
|
||||
print(x)
|
||||
shutil.copytree(x, os.path.join('dist', data_folders[x]),
|
||||
ignore=shutil.ignore_patterns('*.psd',
|
||||
'*.xcf*',
|
||||
'ebg2.png',
|
||||
'ebg1.png'))
|
||||
shutil.copytree(
|
||||
x,
|
||||
os.path.join("dist", data_folders[x]),
|
||||
ignore=shutil.ignore_patterns(
|
||||
"*.psd", "*.xcf*", "ebg2.png", "ebg1.png"
|
||||
),
|
||||
)
|
||||
for x in data_files:
|
||||
print(x)
|
||||
shutil.copy(x, os.path.join('dist', data_files[x]))
|
||||
|
||||
files = os.listdir('dist')
|
||||
shutil.copy(x, os.path.join("dist", data_files[x]))
|
||||
|
||||
files = os.listdir("dist")
|
||||
try:
|
||||
os.mkdir(os.path.join('dist', 'Pesterchum'))
|
||||
os.mkdir(os.path.join("dist", "Pesterchum"))
|
||||
except FileExistsError:
|
||||
pass
|
||||
for x in files:
|
||||
shutil.move(os.path.join('dist',x), os.path.join('dist', 'Pesterchum'))
|
||||
|
||||
#shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
|
||||
shutil.move(os.path.join("dist", x), os.path.join("dist", "Pesterchum"))
|
||||
|
||||
# shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
|
||||
# os.path.join('dist', 'Pesterchum', 'xref-Pesterchum.html'))
|
||||
#shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
|
||||
# shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
|
||||
# os.path.join('dist', 'Pesterchum', 'Analysis-00.toc'))
|
||||
|
||||
|
||||
#MacOS
|
||||
elif sys.platform == 'darwin' :
|
||||
run_darwin =[
|
||||
'pesterchum.py',
|
||||
'--name=Pesterchum',
|
||||
|
||||
# MacOS
|
||||
elif sys.platform == "darwin":
|
||||
run_darwin = [
|
||||
"pesterchum.py",
|
||||
"--name=Pesterchum",
|
||||
#'--windowed', # Hide console
|
||||
#'--noconfirm', # Overwrite output directory.
|
||||
'--icon=trayicon32.icns', # Icon
|
||||
'--onedir',
|
||||
'--clean', # Clear cache
|
||||
"--icon=trayicon32.icns", # Icon
|
||||
"--onedir",
|
||||
"--clean", # Clear cache
|
||||
#'--noupx'
|
||||
]
|
||||
|
||||
if upx_enabled == 'y':
|
||||
if upx_enabled == "y":
|
||||
if os.path.isdir(upx_dir):
|
||||
run_darwin.append('--upx-dir=%s' % upx_dir)
|
||||
run_darwin.append("--upx-dir=%s" % upx_dir)
|
||||
for x in upx_exclude:
|
||||
run_darwin.append('--upx-exclude=%s' % x )
|
||||
run_darwin.append("--upx-exclude=%s" % x)
|
||||
# Lower case variants are required.
|
||||
run_darwin.append('--upx-exclude=%s' % x.lower() )
|
||||
elif upx_enabled == 'n':
|
||||
run_darwin.append('--noupx')
|
||||
run_darwin.append("--upx-exclude=%s" % x.lower())
|
||||
elif upx_enabled == "n":
|
||||
run_darwin.append("--noupx")
|
||||
if os.path.isdir(upx_dir):
|
||||
run_darwin.append('--upx-dir=%s' % upx_dir)
|
||||
run_darwin.append("--upx-dir=%s" % upx_dir)
|
||||
for x in exclude_modules:
|
||||
run_darwin.append('--exclude-module=%s' % x )
|
||||
run_darwin.append("--exclude-module=%s" % x)
|
||||
for x in add_data:
|
||||
run_darwin.append('--add-data=%s' % x.replace(';',':') )
|
||||
if windowed == 'y':
|
||||
run_darwin.append('--windowed')
|
||||
run_darwin.append("--add-data=%s" % x.replace(";", ":"))
|
||||
if windowed == "y":
|
||||
run_darwin.append("--windowed")
|
||||
PyInstaller.__main__.run(run_darwin)
|
||||
|
||||
#Linux
|
||||
elif sys.platform == 'linux':
|
||||
run_linux =[
|
||||
'pesterchum.py',
|
||||
'--name=Pesterchum',
|
||||
|
||||
# Linux
|
||||
elif sys.platform == "linux":
|
||||
run_linux = [
|
||||
"pesterchum.py",
|
||||
"--name=Pesterchum",
|
||||
#'--windowed', # Hide console
|
||||
#'--noconfirm', # Overwrite output directory.
|
||||
'--icon=trayicon32.icns', # Icon
|
||||
'--clean', # Clear cache
|
||||
"--icon=trayicon32.icns", # Icon
|
||||
"--clean", # Clear cache
|
||||
]
|
||||
|
||||
if upx_enabled == 'y':
|
||||
if upx_enabled == "y":
|
||||
if os.path.isdir(upx_dir):
|
||||
run_linux.append('--upx-dir=%s' % upx_dir)
|
||||
run_linux.append("--upx-dir=%s" % upx_dir)
|
||||
for x in upx_exclude:
|
||||
run_linux.append('--upx-exclude=%s' % x )
|
||||
run_linux.append("--upx-exclude=%s" % x)
|
||||
# Lower case variants are required.
|
||||
run_linux.append('--upx-exclude=%s' % x.lower() )
|
||||
elif upx_enabled == 'n':
|
||||
run_linux.append('--noupx')
|
||||
run_linux.append("--upx-exclude=%s" % x.lower())
|
||||
elif upx_enabled == "n":
|
||||
run_linux.append("--noupx")
|
||||
for x in exclude_modules:
|
||||
run_linux.append('--exclude-module=%s' % x )
|
||||
if onefile == 'y':
|
||||
run_linux.append('--onefile')
|
||||
elif onefile == 'n':
|
||||
run_linux.append("--exclude-module=%s" % x)
|
||||
if onefile == "y":
|
||||
run_linux.append("--onefile")
|
||||
elif onefile == "n":
|
||||
for x in add_data:
|
||||
run_linux.append('--add-data=%s' % x.replace(';',':') )
|
||||
if windowed == 'y':
|
||||
run_linux.append('--windowed')
|
||||
|
||||
run_linux.append("--add-data=%s" % x.replace(";", ":"))
|
||||
if windowed == "y":
|
||||
run_linux.append("--windowed")
|
||||
|
||||
print(run_linux)
|
||||
PyInstaller.__main__.run(run_linux)
|
||||
|
||||
if onefile == 'y':
|
||||
|
||||
if onefile == "y":
|
||||
# There's more proper ways to do this, but this doesn't require changing our paths
|
||||
for x in data_folders:
|
||||
print(x)
|
||||
shutil.copytree(x, os.path.join('dist', data_folders[x]),
|
||||
ignore=shutil.ignore_patterns('*.psd',
|
||||
'*.xcf*',
|
||||
'ebg2.png',
|
||||
'ebg1.png'))
|
||||
shutil.copytree(
|
||||
x,
|
||||
os.path.join("dist", data_folders[x]),
|
||||
ignore=shutil.ignore_patterns(
|
||||
"*.psd", "*.xcf*", "ebg2.png", "ebg1.png"
|
||||
),
|
||||
)
|
||||
for x in data_files_linux:
|
||||
print(x)
|
||||
shutil.copy(x, os.path.join('dist', data_files_linux[x]))
|
||||
|
||||
files = os.listdir('dist')
|
||||
shutil.copy(x, os.path.join("dist", data_files_linux[x]))
|
||||
|
||||
files = os.listdir("dist")
|
||||
try:
|
||||
os.mkdir(os.path.join('dist', '.cache'))
|
||||
os.mkdir(os.path.join("dist", ".cache"))
|
||||
except FileExistsError as e:
|
||||
print(e)
|
||||
for x in files:
|
||||
try:
|
||||
shutil.move(os.path.join('dist',x), os.path.join('dist', '.cache', x))
|
||||
shutil.move(os.path.join("dist", x), os.path.join("dist", ".cache", x))
|
||||
except FileExistsError as e:
|
||||
print(e)
|
||||
shutil.move(os.path.join('dist', '.cache'), os.path.join('dist', 'Pesterchum'))
|
||||
#shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
|
||||
shutil.move(os.path.join("dist", ".cache"), os.path.join("dist", "Pesterchum"))
|
||||
# shutil.copy(os.path.join('build', 'Pesterchum', 'xref-Pesterchum.html'),
|
||||
# os.path.join('dist', 'Pesterchum', 'xref-Pesterchum.html'))
|
||||
#shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
|
||||
# shutil.copy(os.path.join('build', 'Pesterchum', 'Analysis-00.toc'),
|
||||
# os.path.join('dist', 'Pesterchum', 'Analysis-00.toc'))
|
||||
|
||||
|
||||
else:
|
||||
print("Unknown platform.")
|
||||
|
||||
run_generic =[
|
||||
'pesterchum.py',
|
||||
'--name=Pesterchum'
|
||||
'--clean', # Clear cache
|
||||
|
||||
run_generic = [
|
||||
"pesterchum.py",
|
||||
"--name=Pesterchum" "--clean", # Clear cache
|
||||
]
|
||||
|
||||
if upx_enabled == 'y':
|
||||
if upx_enabled == "y":
|
||||
if os.path.isdir(upx_dir):
|
||||
run_generic.append('--upx-dir=%s' % upx_dir)
|
||||
run_generic.append("--upx-dir=%s" % upx_dir)
|
||||
else:
|
||||
run_generic.append('--noupx')
|
||||
run_generic.append("--noupx")
|
||||
for x in upx_exclude:
|
||||
run_generic.append('--upx-exclude=%s' % x )
|
||||
run_generic.append("--upx-exclude=%s" % x)
|
||||
# Lower case variants are required.
|
||||
run_generic.append('--upx-exclude=%s' % x.lower() )
|
||||
run_generic.append("--upx-exclude=%s" % x.lower())
|
||||
for x in exclude_modules:
|
||||
run_generic.append('--exclude-module=%s' % x )
|
||||
run_generic.append("--exclude-module=%s" % x)
|
||||
for x in add_data:
|
||||
run_generic.append('--add-data=%s' % x.replace(';',':') )
|
||||
if windowed == 'y':
|
||||
run_win32.append('--windowed')
|
||||
|
||||
run_generic.append("--add-data=%s" % x.replace(";", ":"))
|
||||
if windowed == "y":
|
||||
run_win32.append("--windowed")
|
||||
|
||||
print(run_generic)
|
||||
|
||||
|
||||
PyInstaller.__main__.run(run_generic)
|
||||
|
|
22
pyquirks.py
22
pyquirks.py
|
@ -10,44 +10,45 @@ except ImportError:
|
|||
import ostools
|
||||
from quirks import ScriptQuirks
|
||||
|
||||
PchumLog = logging.getLogger('pchumLogger')
|
||||
PchumLog = logging.getLogger("pchumLogger")
|
||||
|
||||
|
||||
class PythonQuirks(ScriptQuirks):
|
||||
def loadModule(self, name, filename):
|
||||
# imp is deprecated since Python 3.4
|
||||
#return imp.load_source(name, filename)
|
||||
|
||||
# return imp.load_source(name, filename)
|
||||
|
||||
spec = importlib.util.spec_from_file_location(name, filename)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def getExtension(self):
|
||||
return '.py'
|
||||
return ".py"
|
||||
|
||||
def modHas(self, module, attr):
|
||||
if attr == 'commands':
|
||||
if attr == "commands":
|
||||
variables = vars(module)
|
||||
for name, obj in variables.items():
|
||||
if self.modHas(obj, 'command'):
|
||||
if self.modHas(obj, "command"):
|
||||
return True
|
||||
return hasattr(module, attr)
|
||||
|
||||
def register(self, module):
|
||||
variables = vars(module)
|
||||
for name, obj in variables.items():
|
||||
if self.modHas(obj, 'command'):
|
||||
if self.modHas(obj, "command"):
|
||||
try:
|
||||
if not isinstance(obj("test"), str):
|
||||
raise Exception
|
||||
except:
|
||||
#print("Quirk malformed: %s" % (obj.command))
|
||||
# print("Quirk malformed: %s" % (obj.command))
|
||||
PchumLog.error("Quirk malformed: %s" % (obj.command))
|
||||
|
||||
# Since this is executed before QApplication is constructed,
|
||||
# This prevented pesterchum from starting entirely when a quirk was malformed :/
|
||||
# (QWidget: Must construct a QApplication before a QWidget)
|
||||
|
||||
|
||||
if QtWidgets.QApplication.instance() != None:
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
msgbox.setWindowTitle("Error!")
|
||||
|
@ -55,4 +56,3 @@ class PythonQuirks(ScriptQuirks):
|
|||
msgbox.exec()
|
||||
else:
|
||||
self.quirks[obj.command] = obj
|
||||
|
||||
|
|
26
pytwmn.py
26
pytwmn.py
|
@ -1,12 +1,15 @@
|
|||
import os
|
||||
import socket
|
||||
|
||||
|
||||
class TwmnError(Exception):
|
||||
UNWN_ERR = -1
|
||||
NO_TWMND = -2
|
||||
NO_CONF = -3
|
||||
NO_CONF = -3
|
||||
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
|
||||
def __str__(self):
|
||||
if self.code == TwmnError.NO_TWMND:
|
||||
return "Unable to connect to twmnd"
|
||||
|
@ -19,10 +22,12 @@ class TwmnError(Exception):
|
|||
def confExists():
|
||||
try:
|
||||
from xdg import BaseDirectory
|
||||
return os.path.join(BaseDirectory.xdg_config_home,"twmn/twmn.conf")
|
||||
|
||||
return os.path.join(BaseDirectory.xdg_config_home, "twmn/twmn.conf")
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def init(host="127.0.0.1", port=None):
|
||||
if not port:
|
||||
port = 9797
|
||||
|
@ -32,8 +37,7 @@ def init(host="127.0.0.1", port=None):
|
|||
return False
|
||||
with open(fn) as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith("port=") and \
|
||||
line[5:-1].isdigit():
|
||||
if line.startswith("port=") and line[5:-1].isdigit():
|
||||
port = int(line[5:-1])
|
||||
break
|
||||
except IOError:
|
||||
|
@ -44,6 +48,7 @@ def init(host="127.0.0.1", port=None):
|
|||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect((host, port))
|
||||
|
||||
|
||||
class Notification(object):
|
||||
def __init__(self, title="", msg="", icon=""):
|
||||
self.title = str(title)
|
||||
|
@ -59,17 +64,22 @@ class Notification(object):
|
|||
def show(self):
|
||||
try:
|
||||
if self.time is None:
|
||||
s.send("<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!")
|
||||
|
|
48
quirks.py
48
quirks.py
|
@ -4,7 +4,8 @@ import logging
|
|||
import ostools
|
||||
|
||||
_datadir = ostools.getDataDir()
|
||||
PchumLog = logging.getLogger('pchumLogger')
|
||||
PchumLog = logging.getLogger("pchumLogger")
|
||||
|
||||
|
||||
class ScriptQuirks(object):
|
||||
def __init__(self):
|
||||
|
@ -13,7 +14,7 @@ class ScriptQuirks(object):
|
|||
self.quirks = {}
|
||||
self.last = {}
|
||||
self.scripts = []
|
||||
#self.load()
|
||||
# self.load()
|
||||
|
||||
def loadModule(self, name, filename):
|
||||
raise Exception
|
||||
|
@ -27,17 +28,17 @@ class ScriptQuirks(object):
|
|||
for script in self.scripts:
|
||||
PchumLog.info(script.getExtension())
|
||||
script.load()
|
||||
#print script.quirks
|
||||
# print script.quirks
|
||||
for q in script.quirks:
|
||||
self.quirks.update(script.quirks)
|
||||
for k in self.last:
|
||||
if k in self.quirks:
|
||||
if self.last[k] == self.quirks[k]:
|
||||
del self.quirks[k]
|
||||
#print self.quirks
|
||||
if hasattr(self, 'quirks'):
|
||||
# print self.quirks
|
||||
if hasattr(self, "quirks"):
|
||||
# See https://stackoverflow.com/questions/12843099/python-logging-typeerror-not-all-arguments-converted-during-string-formatting
|
||||
reg_quirks = ('Registered quirks:', '(), '.join(self.quirks) + "()")
|
||||
reg_quirks = ("Registered quirks:", "(), ".join(self.quirks) + "()")
|
||||
PchumLog.info(reg_quirks)
|
||||
else:
|
||||
PchumLog.warning("Couldn't find any script quirks")
|
||||
|
@ -50,18 +51,17 @@ class ScriptQuirks(object):
|
|||
self.quirks.clear()
|
||||
extension = self.getExtension()
|
||||
filenames = []
|
||||
if not os.path.exists(os.path.join(self.home, 'quirks')):
|
||||
os.makedirs(os.path.join(self.home, 'quirks'), exist_ok=True)
|
||||
for fn in os.listdir(os.path.join(self.home, 'quirks')):
|
||||
if fn.endswith(extension) and not fn.startswith('_'):
|
||||
filenames.append(os.path.join(self.home, 'quirks', fn))
|
||||
if hasattr(self, '_datadir'):
|
||||
if not os.path.exists(os.path.join(self._datadir, 'quirks')):
|
||||
os.makedirs(os.path.join(self._datadir, 'quirks'), exist_ok=True)
|
||||
for fn in os.listdir(os.path.join(self._datadir, 'quirks')):
|
||||
if fn.endswith(extension) and not fn.startswith('_'):
|
||||
filenames.append(os.path.join(self._datadir, 'quirks', fn))
|
||||
|
||||
if not os.path.exists(os.path.join(self.home, "quirks")):
|
||||
os.makedirs(os.path.join(self.home, "quirks"), exist_ok=True)
|
||||
for fn in os.listdir(os.path.join(self.home, "quirks")):
|
||||
if fn.endswith(extension) and not fn.startswith("_"):
|
||||
filenames.append(os.path.join(self.home, "quirks", fn))
|
||||
if hasattr(self, "_datadir"):
|
||||
if not os.path.exists(os.path.join(self._datadir, "quirks")):
|
||||
os.makedirs(os.path.join(self._datadir, "quirks"), exist_ok=True)
|
||||
for fn in os.listdir(os.path.join(self._datadir, "quirks")):
|
||||
if fn.endswith(extension) and not fn.startswith("_"):
|
||||
filenames.append(os.path.join(self._datadir, "quirks", fn))
|
||||
|
||||
modules = []
|
||||
for filename in filenames:
|
||||
|
@ -72,11 +72,13 @@ class ScriptQuirks(object):
|
|||
if module is None:
|
||||
continue
|
||||
except Exception as e:
|
||||
PchumLog.warning("Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e))
|
||||
PchumLog.warning(
|
||||
"Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e)
|
||||
)
|
||||
else:
|
||||
if self.modHas(module, 'setup'):
|
||||
if self.modHas(module, "setup"):
|
||||
module.setup()
|
||||
if self.modHas(module, 'commands'):
|
||||
if self.modHas(module, "commands"):
|
||||
self.register(module)
|
||||
modules.append(name)
|
||||
for k in self.last:
|
||||
|
@ -85,10 +87,10 @@ class ScriptQuirks(object):
|
|||
del self.quirks[k]
|
||||
|
||||
def funcre(self):
|
||||
if not hasattr(self, 'quirks'):
|
||||
if not hasattr(self, "quirks"):
|
||||
return r"\\[0-9]+"
|
||||
f = r"("
|
||||
for q in self.quirks:
|
||||
f = f + q+r"\(|"
|
||||
f = f + q + r"\(|"
|
||||
f = f + r"\)|\\[0-9]+)"
|
||||
return f
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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_]+)")
|
||||
|
|
21
randomer.py
21
randomer.py
|
@ -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 == "!":
|
||||
|
|
|
@ -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
209
setup.py
|
@ -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
237
toast.py
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue