This commit is contained in:
Stephen Dranger 2011-02-21 13:07:59 -06:00
parent 90e3a4155b
commit be18980fa5
18 changed files with 336 additions and 62 deletions

6
TODO
View file

@ -1,5 +1,7 @@
Bugs:
* idle doesnt seem to work?
* import quirks from 2.5!
* edit quirks?
* begin and end regexps should only be applied once!
* X and _ buttons move around all crazy like
Features:
@ -8,7 +10,6 @@ Features:
* shared buddy lists - changes to the buddy list should refresh it?
multiple clients share buddy list???
* chumList not scaling -- QListView + delegate?
* spell check?
* help button on quirks menu?
-- release beta
* hide offline chums
@ -22,4 +23,3 @@ Features:
* don't clear new message when clicking away from tab?
* Spy mode
* Animated
* put code into separate files

View file

@ -264,9 +264,10 @@ class PesterText(QtGui.QTextEdit):
idlethreshhold = 60
if (not hasattr(self, 'lastmsg')) or \
datetime.now() - self.lastmsg > timedelta(0,idlethreshhold):
verb = window.theme["convo/text/idle"]
idlemsg = me.idlemsg(systemColor, verb)
self.textArea.append(convertTags(idlemsg))
window.chatlog.log(self.title(), idlemsg)
parent.textArea.append(convertTags(idlemsg))
window.chatlog.log(parent.title(), idlemsg)
parent.messageSent.emit("PESTERCHUM:IDLE", parent.title())
self.lastmsg = datetime.now()
window.chatlog.log(chum.handle, lexmsg)

BIN
convo.pyc

Binary file not shown.

View file

@ -4,7 +4,7 @@ import re
import random
from generic import PesterIcon
from parsetools import timeDifference, convertTags
from parsetools import timeDifference, convertTags, lexMessage
from mispeller import mispeller
_upperre = re.compile(r"upper\(([\w\\]+)\)")
@ -116,7 +116,7 @@ class pesterQuirks(object):
suffix = [q for q in self.quirklist if q.type=='suffix']
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']
randomrep = [q for q in self.quirklist if q.type=='random']
spelling = [q for q in self.quirklist if q.type=='spelling']
newlist = []
@ -133,19 +133,27 @@ class pesterQuirks(object):
string = o
for s in spelling:
string = s.apply(string)
for r in random:
for r in randomrep:
string = r.apply(string, first=(i==0), last=lastStr)
for r in replace:
string = r.apply(string, first=(i==0), last=lastStr)
if i == 0:
for p in prefix:
string = p.apply(string)
if len(prefix) >= 1:
myprefix = random.choice(prefix)
string = myprefix.apply(string)
if lastStr:
for s in suffix:
string = s.apply(string)
if len(suffix) >= 1:
mysuffix = random.choice(suffix)
string = mysuffix.apply(string)
newlist.append(string)
return newlist
final = []
for n in newlist:
if type(n) in [str, unicode]:
final.extend(lexMessage(n))
else:
final.append(n)
return final
def __iter__(self):
for q in self.quirklist:

Binary file not shown.

26
irc.py
View file

@ -16,22 +16,29 @@ class PesterIRC(QtCore.QThread):
QtCore.QThread.__init__(self)
self.mainwindow = window
self.config = config
self.registeredIRC = False
self.stopIRC = None
def IRCConnect(self):
server = self.config.server()
port = self.config.port()
self.cli = IRCClient(PesterHandler, host=server, port=int(port), nick=self.mainwindow.profile().handle, real_name='pcc30', blocking=True, timeout=5)
self.cli = IRCClient(PesterHandler, host=server, port=int(port), nick=self.mainwindow.profile().handle, real_name='pcc30', blocking=True, timeout=15)
self.cli.command_handler.parent = self
self.cli.command_handler.mainwindow = self.mainwindow
self.conn = self.cli.connect()
self.stopIRC = None
self.registeredIRC = False
self.cli.connect()
self.conn = self.cli.conn()
def run(self):
self.IRCConnect()
try:
self.IRCConnect()
except socket.error, se:
self.stopIRC = se
return
while 1:
res = True
try:
logging.debug("updateIRC()")
res = self.updateIRC()
except socket.timeout, se:
print "timeout in thread %s" % (self)
logging.debug("timeout in thread %s" % (self))
self.cli.close()
self.stopIRC = se
return
@ -40,16 +47,18 @@ class PesterIRC(QtCore.QThread):
self.stopIRC = None
else:
self.stopIRC = se
logging.debug("socket error, exiting thread")
return
else:
if not res:
logging.debug("false Yield: %s, returning" % res)
return
def setConnected(self):
self.registeredIRC = True
self.connected.emit()
def setConnectionBroken(self):
print "setconnection broken"
logging.debug("setconnection broken")
self.reconnectIRC()
#self.brokenConnection = True
@QtCore.pyqtSlot()
@ -64,12 +73,13 @@ class PesterIRC(QtCore.QThread):
except socket.error, se:
raise se
except StopIteration:
self.conn = self.cli.conn()
return True
else:
return res
@QtCore.pyqtSlot()
def reconnectIRC(self):
print "reconnectIRC() from thread %s" % (self)
logging.debug("reconnectIRC() from thread %s" % (self))
self.cli.close()
@QtCore.pyqtSlot(PesterProfile)

BIN
irc.pyc

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -652,11 +652,16 @@ class LoadingScreen(QtGui.QDialog):
self.layout.addLayout(layout_1)
self.setLayout(self.layout)
def hideReconnect(self):
self.ok.hide()
def showReconnect(self):
self.ok.show()
tryAgain = QtCore.pyqtSignal()
class AboutPesterchum(QtGui.QMessageBox):
def __init__(self, parent=None):
QtGui.QMessageBox.__init__(self, parent)
self.setText("P3ST3RCHUM V. 3.14 alpha 4")
self.setText("P3ST3RCHUM V. 3.14 alpha 6")
self.setInformativeText("Programming by illuminatedwax (ghostDunk), art by Grimlive (aquaMarinist)")
self.mainwindow = parent

BIN
menus.pyc

Binary file not shown.

View file

@ -93,7 +93,7 @@ class IRCClient:
self.__dict__.update(kwargs)
self.command_handler = cmd_handler(self)
self._end = 0
self._end = False
def send(self, *args, **kwargs):
""" send a message to the connected server. all arguments are joined
@ -139,29 +139,23 @@ class IRCClient:
def connect(self):
""" initiates the connection to the server set in self.host:self.port
and returns a generator object.
>>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
>>> g = cli.connect()
>>> while 1:
... g.next()
"""
#logfile = open('irctest.log', 'a')
try:
logging.info('connecting to %s:%s' % (self.host, self.port))
self.socket.connect(("%s" % self.host, self.port))
if not self.blocking:
self.socket.setblocking(0)
if self.timeout:
self.socket.settimeout(self.timeout)
helpers.nick(self, self.nick)
helpers.user(self, self.nick, self.real_name)
logging.info('connecting to %s:%s' % (self.host, self.port))
self.socket.connect(("%s" % self.host, self.port))
if not self.blocking:
self.socket.setblocking(0)
if self.timeout:
self.socket.settimeout(self.timeout)
helpers.nick(self, self.nick)
helpers.user(self, self.nick, self.real_name)
if self.connect_cb:
self.connect_cb(self)
if self.connect_cb:
self.connect_cb(self)
def conn(self):
"""returns a generator object. """
try:
buffer = bytes()
while not self._end:
try:
@ -169,12 +163,12 @@ class IRCClient:
except socket.timeout, e:
if self._end:
break
print "timeout in client.py"
logging.debug("timeout in client.py")
raise e
except socket.error, e:
if self._end:
break
print "error %s" % e
logging.debug("error %s" % e)
try: # a little dance of compatibility to get the errno
errno = e.errno
except AttributeError:
@ -202,14 +196,19 @@ class IRCClient:
yield True
except socket.timeout, se:
logging.debug("passing timeout")
raise se
except socket.error, se:
print "problem: %s" % (se)
logging.debug("problem: %s" % (se))
if self.socket:
logging.info('error: closing socket')
self.socket.close()
raise se
except Exception, e:
logging.debug("other exception: %s" % e)
raise e
else:
logging.debug("ending while, end is %s" % self._end)
if self.socket:
logging.info('finished: closing socket')
self.socket.close()

Binary file not shown.

View file

@ -151,6 +151,8 @@ def lexMessage(string):
if 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, unicode]:
balanced.append("")
return balanced

Binary file not shown.

View file

@ -1 +1 @@
{"tabs": false, "soundon": true, "server": "irc.tymoon.eu", "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", "obliviousCrafter", "ircMonster", "twinArmageddons", "cannabisHero"], "defaultprofile": "ghostDunk", "block": []}
{"tabs": true, "soundon": true, "server": "irc.tymoon.eu", "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", "obliviousCrafter", "ircMonster", "twinArmageddons", "cannabisHero", "jetRocket"], "defaultprofile": "ghostDunk", "block": []}

View file

@ -1138,8 +1138,10 @@ class PesterWindow(MovingWindow):
@QtCore.pyqtSlot()
def connected(self):
print "CONNECTED!"
print self.loadingscreen
if self.loadingscreen:
self.loadingscreen.accept()
self.loadingscreen.done(QtGui.QDialog.Accepted)
self.loadingscreen = None
@QtCore.pyqtSlot()
def blockSelectedChum(self):
@ -1352,6 +1354,8 @@ class PesterWindow(MovingWindow):
@QtCore.pyqtSlot()
def importExternalConfig(self):
f = QtGui.QFileDialog.getOpenFileName(self)
if f == "":
return
fp = open(f, 'r')
for l in fp.xreadlines():
# import chumlist
@ -1802,24 +1806,42 @@ class MainProgram(QtCore.QObject):
def showLoading(self, widget, msg="CONN3CT1NG"):
self.widget.show()
self.widget.activateWindow()
widget.loadingscreen = LoadingScreen(widget)
widget.loadingscreen.loadinglabel.setText(msg)
self.connect(widget.loadingscreen, QtCore.SIGNAL('rejected()'),
widget, QtCore.SLOT('close()'))
self.connect(self.widget.loadingscreen, QtCore.SIGNAL('tryAgain()'),
self, QtCore.SLOT('tryAgain()'))
status = widget.loadingscreen.exec_()
if status == QtGui.QDialog.Rejected:
sys.exit(0)
if hasattr(self.widget, 'loadingscreen') and widget.loadingscreen:
widget.loadingscreen.loadinglabel.setText(msg)
if self.reconnectok:
widget.loadingscreen.showReconnect()
else:
widget.loadingscreen.hideReconnect()
else:
widget.loadingscreen = LoadingScreen(widget)
widget.loadingscreen.loadinglabel.setText(msg)
self.connect(widget.loadingscreen, QtCore.SIGNAL('rejected()'),
widget, QtCore.SLOT('close()'))
self.connect(self.widget.loadingscreen, QtCore.SIGNAL('tryAgain()'),
self, QtCore.SLOT('tryAgain()'))
if hasattr(self, 'irc') and self.irc.registeredIRC:
return
if self.reconnectok:
widget.loadingscreen.showReconnect()
else:
widget.loadingscreen.hideReconnect()
status = widget.loadingscreen.exec_()
print "exited with status %d" % status
if status == QtGui.QDialog.Rejected:
sys.exit(0)
else:
return True
@QtCore.pyqtSlot()
def connected(self):
self.attempts = 0
@QtCore.pyqtSlot()
def tryAgain(self):
if not self.reconnectok:
return
if self.widget.loadingscreen:
self.widget.loadingscreen.accept()
self.widget.loadingscreen.done(QtGui.QDialog.Accepted)
self.widget.loadingscreen = None
self.attempts += 1
if hasattr(self, 'irc') and self.irc:
print "tryagain: reconnectIRC()"
@ -1827,17 +1849,18 @@ class MainProgram(QtCore.QObject):
print "finishing"
self.irc.quit()
else:
print "tryagain: restartIRC"
print "tryagain: restartIRC()"
self.restartIRC()
@QtCore.pyqtSlot()
def restartIRC(self):
if hasattr(self, 'irc') and self.irc:
print "deleting IRC"
self.disconnectWidgets(self.irc, self.widget)
stop = self.irc.stopIRC
del self.irc
else:
stop = None
if not stop:
if stop is None:
self.irc = PesterIRC(self.widget.config, self.widget)
self.connectWidgets(self.irc, self.widget)
self.irc.start()
@ -1847,12 +1870,16 @@ class MainProgram(QtCore.QObject):
msg = "R3CONN3CT1NG %d" % (self.attempts)
else:
msg = "CONN3CT1NG"
print "loadingscreen: auto reconnect"
self.reconnectok = False
self.showLoading(self.widget, msg)
else:
self.reconnectok = True
self.showLoading(self.widget, "F41L3D: %s" % stop)
def run(self):
self.irc.start()
self.reconnectok = False
self.showLoading(self.widget)
sys.exit(self.app.exec_())

View file

@ -1 +1 @@
{"color": "#ff00ff", "theme": "pesterchum2.5", "quirks": [], "handle": "ghostDunk"}
{"color": "#ff00ff", "theme": "pesterchum", "quirks": [], "handle": "ghostDunk"}

View file

@ -98,6 +98,228 @@ CG: #FRUITYRUMPUSASSHOLEFACTORY
and it will appear as a link that you can click, which will open the
memo chooser window.
QUIRKS
------
There are six kinds of quirks! I'll teach you how to use them all!
(In this section, I will use quotes ("") around things so it's clearer
to see exactly what to type! Don't include these quotes when using
these examples!
Prefix/Suffix: This will put text before or after everything you
say. So for example, we can use prefixes to emulate part of Nepeta or
Equius' quirks:
PREFIX: ":33 < "
You type: "*ac twitches her friendly whiskers at ct*"
Result:
AC: :33 < *ac twitches her friendly whiskers at ct*
PREFIX: "D --> "
You type: "Hi"
Result:
CT: D --> Hi
Suffixes work the same way, but at the end of the message:
SUFFIX: "!!!"
You type: hey there
Result:
GD: hey there!!!
Remember that it doesn't automatically add a space! You'll need to add
it in (see CT and AC examples again!)
Simple Replace:
This will simply take a set of characters and replace them with other
characters. Let's add a quirk to our Nepeta:
Replace: "ee" With: "33"
You type: "*ac saunters from her dark cave a little bit sleepy from
the recent kill*"
Result:
AC: :33 < *ac saunters from her dark cave a little bit sl33py from the
recent kill*
Let's add two to Equius:
Replace: "loo" With: "100"
Replace: "x" With "%"
You type: "look"
Result:
CT: D --> 100k
You type: "What are you expecting to accomplish with this"
Result:
CT: D --> What are you e%pecting to accomplish with this
Aradia:
Replace: "o" With: "0"
You type: "and the reward would be within our reach"
Result:
AA: and the reward w0uld be within 0ur reach
Notice that it is CASE SENSITIVE. So in the above case, if you typed
"ABSCOND", it would not replace the "O".
Sollux:
Replace: "i" With: "ii"
Replace: "s" With: "2"
Eridan:
Replace: "v" With: "vv"
Replace: "w" With: "ww"
Feferi:
Replace: "h" with: ")("
Replace: "H" with: ")("
Replace: "E" with: "-E"
Regexp Replace:
This is a more complex kind of replacement. Regexp stands for "regular
expression", a kind of programming language (yes, it is a language)
used to find and replace text. PC 3.14 also includes a function to
handle capitalization (upper()). If you want to learn it on your own,
I suggest you start with the Python tutorial
(http://docs.python.org/howto/regex.html) since PC 3.14 uses Python's
regexps. Check out V2.5's tutorial too, as that is a pretty good start
as well.
Let's start with Karkat. Regexps are just like your every day find and
replace: they search for a string that matches what you want to
replace, and replaces it with... the replacement.
Regexp: "(.)" Replace with: "upper(\1)"
Three concepts here. Let's look at the regexp. "(.)" has two things
going on. The first is that ".". In regexp speak, "." is the wildcard:
it will match *any* character -- and just one.
The parentheses tell the regexp to *save* what's inside them so you
can put it back when you replace. That's what the "\1" is for -- it
means, "put the match inside parentheses #1 here". You can have any
number of parentheses.
"upper()" is a function special to PC 3.14 -- it will uppercase
anything inside the parentheses. So in this case, upper will uppercase
"\1" -- which, as you recall is what we found inside the
parentheses. Which was *every* character. So to sum up, it replaces
every character with an uppercase version of that character. WHICH
MAKES YOU TALK LIKE THIS.
Let's look at Terezi next.
Regexp: "[aA]" Replace with: "4"
Regexp: "[iI]" Replace with: "1"
Regexp: "[eE]" Replace with: "3"
Regexp: "(.)" Replace with: "upper(\1)"
We already know what the last line does. But what's up with those
brackets? What's their deal? Basically, in regular expressions,
brackets indicate a list of matching characters. So, basically any
single character within the brackets will be matched. In this case,
either "a" or "A" will be matched and replaced with "4," and likewise,
"i" and "I" will be replaced with "1", and "e" and "E" will be
replaced with "3."
You should also know that "^" is a special character in brackets. If
placed immediately after the opening bracket (like "[^"), then the
brackets instead match every character *except* the ones in the
brackets. So, for example, if you wanted to have a quirk where you
capitalized all your letters *except* o, you'd do this:
Regexp: "([^o])" Replace with: "upper(\1)"
You type: "hello there"
Result:
GD: HELLo THERE
You can also specify a *range* of characters inside the brackets, by
using the "-" character. [a-z] will match any lowercase letter. You
can combine them, too: [a-z0-9] will match any digit and lowercase letter.
There are also different shortcuts for character types:
\d matches any digit; same as [0-9]
\D matches any non-digit; same as [^0-9]
\s matches any spaces
\S matches any non-space
\w matches any alphanumeric character; same as [a-zA-Z0-9_]
\W matches any non-alphanumeric character; same as [^a-zA-Z0-9_]
You can include this inside brackets, too.
There's also a special character, \b. What \b does is make sure that
you are at the beginning or end of a word. So with that knowledge,
let's try Kanaya:
Regexp: \b(\w) Replace with: upper(\1)
You type: "i suggest you come to terms with it"
Result:
GA: I Suggest You Come To Terms With It
Another feature of regular expressions is the ability to match
*repeated* characters. There are three repeat characters: the "*", the
"+", "?", and "{m,n}". They work by playing them after the character,
or character type you want to match. (So, you could say "\s+" or ".*")
The "*" character matches ZERO or more of that character. So, for
example, "f*" would match "f" and "ff" -- and any other character!
That's right, every character counts as matching it zero times. Yeah,
it's weird. I suggest you use...
The "+" character matches ONE or more of that character. So, if we
wanted to have a character that wanted to elongate their s's so that
they used four 's's every time, like sssso, but didn't want to have
eight s's when using words with double s's, like pass, we'd do this:
Regexp: "s+" Replace with: "ssss"
You type: "you shall not pass"
Result:
UU: you sssshall not passss
As for the other two, I can't really think of any useful quirks to be
made with them. But to let you know, "?" matches either 0 or 1 of that
character, so "trolls?" would match "troll" and "trolls". "{m,n}"
matches between m and n characters. (If you leave out 'n', any number
of characters more than m will be matched.) So "s{2,4}" will match
"ss", "sss", and "ssss" and that's it.
Now with repeating expressions, we can do something like make EVERY
other WORD capitalized:
Regexp: "(\w+) (\w+)" Replace with: "upper(\1) \2"
You type: "this is pretty annoying i bet"
Result:
GD: THIS is PRETTY annoying I bet
The \1 matches the first word -- which has been matched because the
word is alphanumeric characters, repeated once or more -- and \2
matches the second word.
Another operator to use is the "|", which will match more than one set
of characters. For example, "black|red" will match "black" or
"red". If you want to match something in the middle of words, you have
to use parentheses: "(black|red) romance" will match "black romance"
and "red romance".
Finally, there are the "^" and "$" characters. Yes, we already did the
"^" character, but this is OUTSIDE of brackets, not INSIDE. "^"
matches the beginning of a message, and "$" matches the end of it. You
can use this to make more sophisticated prefix and suffix
behaviors. For example, if we have a quirk that adds "..." to the end
of all our messages, we can set it up so it doesn't do that if we put
punctuation [?!.] at the end. So:
Regexp: "([^?!.])$" Replace with: "\1..."
This will match the end of any message as long as it doesn't have
"?", "!", or "." at the end. Then it will replace it with whatever the
last character of the sentence was (remember we're replacing it, so we
have to put it back!) and add "..." at the end.
Careful with the beginning and ending replaces -- if you use more than
one, you may not get what you expect because they will ALL be applied,
one after the other! This is a bug in my opinion, that I plan to fix!
Random replace:
SMILIES
-------
Here's a list of smilies: