first feature freeze

This commit is contained in:
Stephen Dranger 2011-02-13 19:32:02 -06:00
parent 2cde4e6ec7
commit 201b9b3886
20 changed files with 215 additions and 18 deletions

10
TODO
View file

@ -1,8 +1,4 @@
Features:
* More complex quirks: spelling, by-sound
* IRC message limit!!!
* on ban, redirect to memo chooser
* ? time option???
* help menu -- about and forum
* Tray doesn't disappear on windows after close
-- release alpha
@ -12,7 +8,9 @@ Features:
* spell check?
* help button on quirks menu?
-- release beta
* flashing??
* hide offline chums
* chum list groups
* More complex quirks: by-sound
* change profile only once we have confirmation from server
* log viewer
* pick your own icon
@ -22,5 +20,3 @@ Features:
* Spy mode
* Animated
* put code into separate files
* hide offline chums
* chum list groups

View file

@ -117,13 +117,16 @@ class PesterTabWindow(QtGui.QFrame):
return True
else:
return False
def activateChat(self):
if platform.system() == "Windows":
self.activateWindow()
def notifyNewMessage(self, handle):
i = self.tabIndices[handle]
self.tabs.setTabTextColor(i, QtGui.QColor(self.mainwindow.theme["%s/tabs/newmsgcolor" % (self.type)]))
convo = self.convos[handle]
def func():
convo.showChat()
self.activateChat()
self.mainwindow.waitingMessages.addMessage(handle, func)
# set system tray
def clearNewMessage(self, handle):
@ -480,6 +483,11 @@ class PesterConvo(QtGui.QFrame):
if self.parent():
self.parent().showChat(self.title())
self.raiseChat()
def activateChat(self):
if self.parent():
self.parent().activateChat()
if platform.system() == "Windows":
self.activateWindow()
def contextMenuEvent(self, event):
if event.reason() == QtGui.QContextMenuEvent.Mouse:
self.optionsMenu.popup(event.globalPos())

BIN
convo.pyc

Binary file not shown.

View file

@ -5,6 +5,9 @@ import random
from generic import PesterIcon
from parsetools import timeDifference, convertTags
from mispeller import mispeller
_upperre = re.compile(r"upper\(([\w\\]+)\)")
class Mood(object):
moods = ["chummy", "rancorous", "offline", "pleasant", "distraught",
@ -32,6 +35,7 @@ class Mood(object):
return PesterIcon(theme["main/chums/moods/chummy/icon"])
return PesterIcon(f)
class pesterQuirk(object):
def __init__(self, quirk):
if type(quirk) != dict:
@ -51,7 +55,13 @@ class pesterQuirk(object):
return string
if not last and len(fr) > 0 and fr[len(fr)-1] == "$":
return string
return re.sub(fr, self.quirk["to"], string)
def regexprep(mo):
to = self.quirk["to"]
def upperrep(m):
return mo.expand(m.group(1)).upper()
to = _upperre.sub(upperrep, to)
return mo.expand(to)
return re.sub(fr, regexprep, string)
elif self.type == "random":
fr = self.quirk["from"]
if not first and len(fr) > 0 and fr[0] == "^":
@ -62,6 +72,17 @@ class pesterQuirk(object):
choice = random.choice(self.quirk["randomlist"])
return mo.expand(choice)
return re.sub(self.quirk["from"], randomrep, string)
elif self.type == "spelling":
percentage = self.quirk["percentage"]/100.0
words = string.split(" ")
newl = []
for w in words:
p = random.random()
if p < percentage:
newl.append(mispeller(w))
else:
newl.append(w)
return " ".join(newl)
def __str__(self):
if self.type == "prefix":
@ -74,6 +95,8 @@ class pesterQuirk(object):
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"], [str(r) for r in self.quirk["randomlist"]])
elif self.type == "spelling":
return "MISPELLER: %d%%" % (self.quirk["percentage"])
class pesterQuirks(object):
def __init__(self, quirklist):
@ -91,6 +114,7 @@ class pesterQuirks(object):
replace = [q for q in self.quirklist if
q.type=='replace' or q.type=='regexp']
random = [q for q in self.quirklist if q.type=='random']
spelling = [q for q in self.quirklist if q.type=='spelling']
newlist = []
for (i, o) in enumerate(lexed):
@ -104,6 +128,8 @@ class pesterQuirks(object):
continue
lastStr = (i == len(lexed)-1)
string = o
for s in spelling:
string = s.apply(string)
for r in random:
string = r.apply(string, first=(i==0), last=lastStr)
for r in replace:

Binary file not shown.

View file

@ -1,4 +1,13 @@
from PyQt4 import QtGui, QtCore
from datetime import timedelta
class mysteryTime(timedelta):
def __sub__(self, other):
return self
def __eq__(self, other):
return (type(other) is mysteryTime)
def __neq__(self, other):
return (type(other) is not mysteryTime)
class PesterList(list):
def __init__(self, l):

Binary file not shown.

18
irc.py
View file

@ -32,8 +32,24 @@ class PesterIRC(QtCore.QObject):
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
def sendMessage(self, text, handle):
h = unicode(handle)
textl = [unicode(text)]
def splittext(l):
if len(l[0]) > 400:
space = l[0].rfind(" ", 0,400)
if space == -1:
space = 400
a = l[0][0:space]
b = l[0][space:]
if len(b) > 0:
return [a] + splittext([b])
else:
return [a]
else:
return l
textl = splittext(textl)
try:
helpers.msg(self.cli, h, text)
for t in textl:
helpers.msg(self.cli, h, t)
except socket.error:
self.setConnectionBroken()
@QtCore.pyqtSlot(QtCore.QString, bool)

BIN
irc.pyc

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -5,12 +5,15 @@ from PyQt4 import QtGui, QtCore
from datetime import time, timedelta, datetime
from dataobjs import PesterProfile, Mood, PesterHistory
from generic import PesterIcon, RightClickList
from generic import PesterIcon, RightClickList, mysteryTime
from convo import PesterConvo, PesterInput, PesterText, PesterTabWindow
from parsetools import convertTags, addTimeInitial, timeProtocol, \
lexMessage, colorBegin, colorEnd, mecmd
def delta2txt(d, format="pc"):
if type(d) is mysteryTime:
return "?"
if format == "pc":
sign = "+" if d >= timedelta(0) else "-"
else:
@ -34,6 +37,8 @@ def delta2txt(d, format="pc"):
def txt2delta(txt):
sign = 1
if txt[0] == '?':
return mysteryTime()
if txt[0] == '+':
txt = txt[1:]
elif txt[0] == '-':
@ -51,7 +56,11 @@ def txt2delta(txt):
return sign*timed
def pcfGrammar(td):
if td > timedelta(0):
if type(td) is mysteryTime:
when = "???"
temporal = "???"
pcf = "?"
elif td > timedelta(0):
when = "FROM NOW"
temporal = "FUTURE"
pcf = "F"
@ -109,14 +118,14 @@ class TimeTracker(list):
self.current = self.index(timed)
def addRecord(self, timed):
(temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
if pcf == "C":
if pcf == "C" or pcf == "?":
return
if timed in self.timerecord[pcf]:
return
self.timerecord[pcf].append(timed)
def getRecord(self, timed):
(temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
if pcf == "C":
if pcf == "C" or pcf == "?":
return 0
if len(self.timerecord[pcf]) > 1:
return self.timerecord[pcf].index(timed)+1
@ -170,6 +179,10 @@ class TimeInput(QtGui.QLineEdit):
def setSlider(self):
value = unicode(self.text())
timed = txt2delta(value)
if type(timed) is mysteryTime:
self.timeslider.setValue(0)
self.setText("?")
return
sign = 1 if timed >= timedelta(0) else -1
abstimed = abs(txt2delta(value))
index = 50
@ -254,7 +267,7 @@ class MemoText(PesterText):
if chum is not me:
if parent.times.has_key(chum.handle):
time = parent.times[chum.handle]
if not time.getTime():
if time.getTime() is None:
# MY WAY OR THE HIGHWAY
time.addTime(timedelta(0))
else:

BIN
memos.pyc

Binary file not shown.

View file

@ -38,6 +38,45 @@ class PesterQuirkList(QtGui.QListWidget):
if i >= 0:
self.takeItem(i)
class MispellQuirkDialog(QtGui.QDialog):
def __init__(self, parent):
QtGui.QDialog.__init__(self, parent)
self.setWindowTitle("MISPELLER")
layout_1 = QtGui.QHBoxLayout()
zero = QtGui.QLabel("1%", self)
hund = QtGui.QLabel("100%", self)
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
self.slider.setMinimum(1)
self.slider.setMaximum(100)
self.slider.setValue(50)
layout_1.addWidget(zero)
layout_1.addWidget(self.slider)
layout_1.addWidget(hund)
self.ok = QtGui.QPushButton("OK", self)
self.ok.setDefault(True)
self.connect(self.ok, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('accept()'))
self.cancel = QtGui.QPushButton("CANCEL", self)
self.connect(self.cancel, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('reject()'))
layout_ok = QtGui.QHBoxLayout()
layout_ok.addWidget(self.cancel)
layout_ok.addWidget(self.ok)
layout_0 = QtGui.QVBoxLayout()
layout_0.addLayout(layout_1)
layout_0.addLayout(layout_ok)
self.setLayout(layout_0)
def getPercentage(self):
r = self.exec_()
if r == QtGui.QDialog.Accepted:
retval = {"percentage": self.slider.value()}
return retval
else:
return None
class RandomQuirkDialog(MultiTextDialog):
def __init__(self, parent):
QtGui.QDialog.__init__(self, parent)
@ -138,6 +177,10 @@ class PesterChooseQuirks(QtGui.QDialog):
self.connect(self.addRandomReplaceButton, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('addRandomDialog()'))
self.addMispellingButton = QtGui.QPushButton("MISPELLER", self)
self.connect(self.addMispellingButton, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('addSpellDialog()'))
layout_1 = QtGui.QHBoxLayout()
layout_1.addWidget(self.addPrefixButton)
layout_1.addWidget(self.addSuffixButton)
@ -145,6 +188,7 @@ class PesterChooseQuirks(QtGui.QDialog):
layout_2 = QtGui.QHBoxLayout()
layout_2.addWidget(self.addRegexpReplaceButton)
layout_2.addWidget(self.addRandomReplaceButton)
layout_2.addWidget(self.addMispellingButton)
self.removeSelectedButton = QtGui.QPushButton("REMOVE", self)
self.connect(self.removeSelectedButton, QtCore.SIGNAL('clicked()'),
@ -240,6 +284,16 @@ class PesterChooseQuirks(QtGui.QDialog):
item = PesterQuirkItem(quirk, self.quirkList)
self.quirkList.addItem(item)
#self.quirkList.sortItems()
@QtCore.pyqtSlot()
def addSpellDialog(self):
vdict = MispellQuirkDialog(self).getPercentage()
if vdict is None:
return
vdict["type"] = "spelling"
quirk = pesterQuirk(vdict)
item = PesterQuirkItem(quirk, self.quirkList)
self.quirkList.addItem(item)
#self.quirkList.sortItems()
class PesterChooseTheme(QtGui.QDialog):
def __init__(self, config, theme, parent):

BIN
menus.pyc

Binary file not shown.

60
mispeller.py Normal file
View file

@ -0,0 +1,60 @@
import random
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"}
def mispeller(word):
if len(word) <= 6:
num = 1
else:
num = random.choice([1,2])
wordseq = range(0, len(word))
random.shuffle(wordseq)
letters = wordseq[0:num]
def mistype(string, i):
l = string[i]
if not kbdict.has_key(l):
return string
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:]
return string
def transpose(string, i):
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:]
return string
def randomreplace(string, i):
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:]
return string
func = random.choice([mistype, transpose, randomletter, randomreplace,
soundalike])
for i in letters:
word = func(word, i)
return word

BIN
mispeller.pyc Normal file

Binary file not shown.

View file

@ -3,6 +3,8 @@ from copy import copy
from datetime import timedelta
from PyQt4 import QtGui
from generic import mysteryTime
_ctag_begin = re.compile(r'(?i)<c=(.*?)>')
_gtag_begin = re.compile(r'(?i)<g[a-f]>')
_ctag_end = re.compile(r'(?i)</c>')
@ -183,6 +185,8 @@ def addTimeInitial(string, grammar):
def timeProtocol(cmd):
dir = cmd[0]
if dir == "?":
return mysteryTime(0)
cmd = cmd[1:]
cmd = re.sub("[^0-9:]", "", cmd)
try:
@ -195,6 +199,8 @@ def timeProtocol(cmd):
return timed
def timeDifference(td):
if type(td) is mysteryTime:
return "??:?? FROM ????"
if td < timedelta(0):
when = "AGO"
else:

Binary file not shown.

View file

@ -1 +1 @@
{"tabs": true, "soundon": true, "chums": ["unknownTraveler", "tentacleTherapist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium", "terminallyCapricious", "illuminatedWax", "aquaMarinist", "elegantDiversion", "moirailBunp", "uroborosUnbound", "androidTechnician", "midnightSparrow", "apocalypseArisen", "anguillaNuntia", "oilslickOrchid", "confusedTransient", "pretentiousFantasia", "aquaticMarinist", "lyricalKeraunoscopic", "counterRealist", "ectoBiologist", "percipientPedestrian", "asceticClinician"], "defaultprofile": "ghostDunk", "block": []}
{"tabs": true, "soundon": true, "chums": ["unknownTraveler", "tentacleTherapist", "vaginalEngineer", "mechanicalSpectacle", "carcinoGeneticist", "schlagzeugGator", "gamblingGenocider", "gardenGnostic", "superGhost", "centaursTesticle", "arachnidsGrip", "grimAuxiliatrix", "remoteBloodbath", "nitroZealist", "greenZephyr", "arsenicCatnip", "adiosToreador", "cuttlefishCuller", "rageInducer", "gallowsCalibrator", "caligulasAquarium", "terminallyCapricious", "illuminatedWax", "aquaMarinist", "elegantDiversion", "moirailBunp", "uroborosUnbound", "androidTechnician", "midnightSparrow", "apocalypseArisen", "anguillaNuntia", "oilslickOrchid", "confusedTransient", "pretentiousFantasia", "aquaticMarinist", "lyricalKeraunoscopic", "counterRealist", "ectoBiologist", "percipientPedestrian", "asceticClinician", "doctectiveMiracles", "noSense"], "defaultprofile": "ghostDunk", "block": []}

View file

@ -825,6 +825,7 @@ class PesterWindow(MovingWindow):
self.closeConversations()
if hasattr(self, 'trollslum') and self.trollslum:
self.trollslum.close()
self.closeSignal.emit()
event.accept()
def newMessage(self, handle, msg):
if handle in self.config.getBlocklist():
@ -1625,6 +1626,7 @@ class PesterWindow(MovingWindow):
joinChannel = QtCore.pyqtSignal(QtCore.QString)
leftChannel = QtCore.pyqtSignal(QtCore.QString)
setChannelMode = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
closeSignal = QtCore.pyqtSignal()
class IRCThread(QtCore.QThread):
def __init__(self, ircobj):
@ -1652,6 +1654,9 @@ class PesterTray(QtGui.QSystemTrayIcon):
self.setIcon(PesterIcon(self.mainwindow.theme["main/icon"]))
else:
self.setIcon(PesterIcon(self.mainwindow.theme["main/newmsgicon"]))
@QtCore.pyqtSlot()
def mainWindowClosed(self):
self.hide()
class MainProgram(QtCore.QObject):
def __init__(self):
@ -1698,6 +1703,10 @@ class MainProgram(QtCore.QObject):
QtCore.SIGNAL('closeToTraySignal()'),
self.trayicon,
QtCore.SLOT('show()'))
self.trayicon.connect(self.widget,
QtCore.SIGNAL('closeSignal()'),
self.trayicon,
QtCore.SLOT('mainWindowClosed()'))
self.irc = PesterIRC(self.widget)
self.connectWidgets(self.irc, self.widget)
@ -1829,7 +1838,7 @@ class MainProgram(QtCore.QObject):
status = self.widget.loadingscreen.exec_()
if status == QtGui.QDialog.Rejected:
sys.exit(0)
os._exit(self.app.exec_())
sys.exit(self.app.exec_())
pesterchum = MainProgram()
pesterchum.run()