dsfa
This commit is contained in:
parent
90e3a4155b
commit
be18980fa5
18 changed files with 336 additions and 62 deletions
6
TODO
6
TODO
|
@ -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
|
||||
|
|
5
convo.py
5
convo.py
|
@ -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
BIN
convo.pyc
Binary file not shown.
24
dataobjs.py
24
dataobjs.py
|
@ -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:
|
||||
|
|
BIN
dataobjs.pyc
BIN
dataobjs.pyc
Binary file not shown.
26
irc.py
26
irc.py
|
@ -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
BIN
irc.pyc
Binary file not shown.
File diff suppressed because one or more lines are too long
7
menus.py
7
menus.py
|
@ -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
BIN
menus.pyc
Binary file not shown.
|
@ -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()
|
||||
|
|
BIN
oyoyo/client.pyc
BIN
oyoyo/client.pyc
Binary file not shown.
|
@ -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
|
||||
|
|
BIN
parsetools.pyc
BIN
parsetools.pyc
Binary file not shown.
|
@ -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": []}
|
|
@ -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_())
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"color": "#ff00ff", "theme": "pesterchum2.5", "quirks": [], "handle": "ghostDunk"}
|
||||
{"color": "#ff00ff", "theme": "pesterchum", "quirks": [], "handle": "ghostDunk"}
|
222
readme.txt
222
readme.txt
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue