Merge pull request #130 from kiooeht/master

merge in kiooeht
This commit is contained in:
illuminatedwax 2014-10-15 02:10:05 -05:00
commit f2a482b241
15 changed files with 441 additions and 114 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
*.swp
*~
logs/* logs/*
build/* build/*
profiles/* profiles/*

View file

@ -22,6 +22,10 @@ CHANGELOG
* FTP and Magnet links - oakwhiz * FTP and Magnet links - oakwhiz
* Userlist search - oakwhiz * Userlist search - oakwhiz
* Chanserv in menus - Cerxi [binaryCabalist] * Chanserv in menus - Cerxi [binaryCabalist]
* Lua quirks
* Multi-select memo chooser - [alGore]
* Auto-identify with NickServ - Kiooeht [evacipatedBox]
* Auto-join memos - Kiooeht [evacipatedBox]
* Bug fixes * Bug fixes
* Don't require pygame (it's kind of optional, you just don't get sound) - Kiooeht [evacipatedBox] * Don't require pygame (it's kind of optional, you just don't get sound) - Kiooeht [evacipatedBox]
* Allow add chum dialog to open after adding an existing chum - Kiooeht [evacipatedBox] * Allow add chum dialog to open after adding an existing chum - Kiooeht [evacipatedBox]
@ -32,6 +36,9 @@ CHANGELOG
* Move hidden chums when deleting group - Kiooeht [evacipatedBox] * Move hidden chums when deleting group - Kiooeht [evacipatedBox]
* Don't allow rename groups with parenthesis - Kiooeht [evacipatedBox] * Don't allow rename groups with parenthesis - Kiooeht [evacipatedBox]
* Wrap long error messages - Kiooeht [evacipatedBox] * Wrap long error messages - Kiooeht [evacipatedBox]
* chdir into quirks folder for Lua quirks - [alGore]
* Toast notifications don't require sound to be on - Kiooeht [evacipatedBox]
* Don't close Pesterchum if a window is closed while main window minimized - Kiooeht [evacipatedBox]
### 3.41.3 ### 3.41.3

View file

@ -35,8 +35,6 @@ Bugs
* Closing a timeclone doesn't actually cease for everyone else * Closing a timeclone doesn't actually cease for everyone else
* Kill Zalgo * Kill Zalgo
* Random invisible, tiny links to last link at end of every message * Random invisible, tiny links to last link at end of every message
* Clicking link to invite-only memo crashes
* Close when banned from memo when main PC window minimized
* Chums not appearing on chumroll when initals are the same? (bS) * Chums not appearing on chumroll when initals are the same? (bS)
* Recognize IRC 471, 473, 474 and 475 * Recognize IRC 471, 473, 474 and 475
* memo links aren't case sensitive * memo links aren't case sensitive

3
irc.py
View file

@ -176,6 +176,9 @@ class PesterIRC(QtCore.QThread):
except socket.error: except socket.error:
self.setConnectionBroken() self.setConnectionBroken()
self.mainwindow.closeConversations(True) self.mainwindow.closeConversations(True)
self.mainwindow.doAutoIdentify()
self.mainwindow.autoJoinDone = False
self.mainwindow.doAutoJoins()
self.updateMood() self.updateMood()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def updateMood(self): def updateMood(self):

61
luaquirks.py Normal file
View file

@ -0,0 +1,61 @@
import os, sys, re, ostools
try:
import lua
except ImportError:
lua = None
from quirks import ScriptQuirks
from PyQt4 import QtGui, QtCore
class LuaQuirks(ScriptQuirks):
def loadModule(self, name, filename):
if lua is None:
return None
lua.globals().package.loaded[name] = None
CurrentDir = os.getcwd()
os.chdir('quirks')
try:
return lua.require(name)
except Error as e:
print e
return None
finally:
os.chdir(CurrentDir)
def getExtension(self):
return '.lua'
def modHas(self, module, attr):
return module[attr] is not None
def register(self, module):
class Wrapper(object):
def __init__(self, module, name):
self.module = module
self.name = name
def __call__(self, text):
CurrentDir = os.getcwd()
os.chdir('quirks')
try:
return self.module.commands[self.name](lua.globals().tostring(text))
except:
return None
finally:
os.chdir(CurrentDir)
for name in module.commands:
CommandWrapper = Wrapper(module,name)
try:
if not isinstance(CommandWrapper("test"), basestring):
raise Exception
except:
print "Quirk malformed: %s" % (name)
msgbox = QtGui.QMessageBox()
msgbox.setWindowTitle("Error!")
msgbox.setText("Quirk malformed: %s" % (name))
msgbox.exec_()
else:
self.quirks[name] = CommandWrapper

View file

@ -323,7 +323,7 @@ class PesterQuirkTypes(QtGui.QDialog):
self.funclist2.setStyleSheet("color: #000000; background-color: #FFFFFF;") self.funclist2.setStyleSheet("color: #000000; background-color: #FFFFFF;")
from parsetools import quirkloader from parsetools import quirkloader
funcs = [q+")" for q in quirkloader.quirks.keys()] funcs = [q+"()" for q in quirkloader.quirks.keys()]
funcs.sort() funcs.sort()
self.funclist.addItems(funcs) self.funclist.addItems(funcs)
self.funclist2.addItems(funcs) self.funclist2.addItems(funcs)
@ -561,7 +561,7 @@ class PesterQuirkTypes(QtGui.QDialog):
def reloadQuirkFuncSlot(self): def reloadQuirkFuncSlot(self):
from parsetools import reloadQuirkFunctions, quirkloader from parsetools import reloadQuirkFunctions, quirkloader
reloadQuirkFunctions() reloadQuirkFunctions()
funcs = [q+")" for q in quirkloader.quirks.keys()] funcs = [q+"()" for q in quirkloader.quirks.keys()]
funcs.sort() funcs.sort()
self.funclist.clear() self.funclist.clear()
self.funclist.addItems(funcs) self.funclist.addItems(funcs)
@ -1001,7 +1001,7 @@ class PesterOptions(QtGui.QDialog):
self.tabs = QtGui.QButtonGroup(self) self.tabs = QtGui.QButtonGroup(self)
self.connect(self.tabs, QtCore.SIGNAL('buttonClicked(int)'), self.connect(self.tabs, QtCore.SIGNAL('buttonClicked(int)'),
self, QtCore.SLOT('changePage(int)')) self, QtCore.SLOT('changePage(int)'))
tabNames = ["Chum List", "Conversations", "Interface", "Sound", "Notifications", "Logging", "Idle/Updates", "Theme"] tabNames = ["Chum List", "Conversations", "Interface", "Sound", "Notifications", "Logging", "Idle/Updates", "Theme", "Connection"]
if parent.advanced: tabNames.append("Advanced") if parent.advanced: tabNames.append("Advanced")
for t in tabNames: for t in tabNames:
button = QtGui.QPushButton(t) button = QtGui.QPushButton(t)
@ -1020,6 +1020,24 @@ class PesterOptions(QtGui.QDialog):
font.setPointSize(8) font.setPointSize(8)
bandwidthLabel.setFont(font) bandwidthLabel.setFont(font)
self.autonickserv = QtGui.QCheckBox("Auto-Identify with NickServ", self)
self.autonickserv.setChecked(parent.userprofile.getAutoIdentify())
self.connect(self.autonickserv, QtCore.SIGNAL('stateChanged(int)'),
self, QtCore.SLOT('autoNickServChange(int)'))
self.nickservpass = QtGui.QLineEdit(self)
self.nickservpass.setPlaceholderText("NickServ Password")
self.nickservpass.setEchoMode(QtGui.QLineEdit.PasswordEchoOnEdit)
self.nickservpass.setText(parent.userprofile.getNickServPass())
self.autojoinlist = QtGui.QListWidget(self)
self.autojoinlist.addItems(parent.userprofile.getAutoJoins())
self.addAutoJoinBtn = QtGui.QPushButton("Add", self)
self.connect(self.addAutoJoinBtn, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('addAutoJoin()'))
self.delAutoJoinBtn = QtGui.QPushButton("Remove", self)
self.connect(self.delAutoJoinBtn, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('delAutoJoin()'))
self.tabcheck = QtGui.QCheckBox("Tabbed Conversations", self) self.tabcheck = QtGui.QCheckBox("Tabbed Conversations", self)
if self.config.tabs(): if self.config.tabs():
self.tabcheck.setChecked(True) self.tabcheck.setChecked(True)
@ -1259,8 +1277,6 @@ class PesterOptions(QtGui.QDialog):
layout_chumlist.addWidget(self.showemptycheck) layout_chumlist.addWidget(self.showemptycheck)
layout_chumlist.addWidget(self.showonlinenumbers) layout_chumlist.addWidget(self.showonlinenumbers)
layout_chumlist.addLayout(layout_3) layout_chumlist.addLayout(layout_3)
layout_chumlist.addWidget(self.bandwidthcheck)
layout_chumlist.addWidget(bandwidthLabel)
self.pages.addWidget(widget) self.pages.addWidget(widget)
# Conversations # Conversations
@ -1365,6 +1381,25 @@ class PesterOptions(QtGui.QDialog):
layout_theme.addWidget(self.ghostchum) layout_theme.addWidget(self.ghostchum)
self.pages.addWidget(widget) self.pages.addWidget(widget)
# Connection
widget = QtGui.QWidget()
layout_connect = QtGui.QVBoxLayout(widget)
layout_connect.setAlignment(QtCore.Qt.AlignTop)
layout_connect.addWidget(self.bandwidthcheck)
layout_connect.addWidget(bandwidthLabel)
layout_connect.addWidget(self.autonickserv)
layout_indent = QtGui.QVBoxLayout()
layout_indent.addWidget(self.nickservpass)
layout_indent.setContentsMargins(22,0,0,0)
layout_connect.addLayout(layout_indent)
layout_connect.addWidget(QtGui.QLabel("Auto-Join Memos:"))
layout_connect.addWidget(self.autojoinlist)
layout_8 = QtGui.QHBoxLayout()
layout_8.addWidget(self.addAutoJoinBtn)
layout_8.addWidget(self.delAutoJoinBtn)
layout_connect.addLayout(layout_8)
self.pages.addWidget(widget)
# Advanced # Advanced
if parent.advanced: if parent.advanced:
widget = QtGui.QWidget() widget = QtGui.QWidget()
@ -1411,6 +1446,32 @@ class PesterOptions(QtGui.QDialog):
self.notifyNewConvoCheck.setEnabled(True) self.notifyNewConvoCheck.setEnabled(True)
self.notifyMentionsCheck.setEnabled(True) self.notifyMentionsCheck.setEnabled(True)
@QtCore.pyqtSlot(int)
def autoNickServChange(self, state):
self.nickservpass.setEnabled(state != 0)
@QtCore.pyqtSlot()
def addAutoJoin(self, mitem=None):
d = {"label": "Memo:", "inputname": "value" }
if mitem is not None:
d["value"] = str(mitem.text())
pdict = MultiTextDialog("ENTER MEMO", self, d).getText()
if pdict is None:
return
pdict["value"] = "#" + pdict["value"]
if mitem is None:
items = self.autojoinlist.findItems(pdict["value"], QtCore.Qt.MatchFixedString)
if len(items) == 0:
self.autojoinlist.addItem(pdict["value"])
else:
mitem.setText(pdict["value"])
@QtCore.pyqtSlot()
def delAutoJoin(self):
i = self.autojoinlist.currentRow()
if i >= 0:
self.autojoinlist.takeItem(i)
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
def soundChange(self, state): def soundChange(self, state):
if state == 0: if state == 0:
@ -1588,6 +1649,7 @@ class PesterMemoList(QtGui.QDialog):
self.label = QtGui.QLabel("MEMOS") self.label = QtGui.QLabel("MEMOS")
self.channelarea = RightClickTree(self) self.channelarea = RightClickTree(self)
self.channelarea.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.channelarea.setStyleSheet(self.theme["main/chums/style"]) self.channelarea.setStyleSheet(self.theme["main/chums/style"])
self.channelarea.optionsMenu = QtGui.QMenu(self) self.channelarea.optionsMenu = QtGui.QMenu(self)
self.channelarea.setColumnCount(2) self.channelarea.setColumnCount(2)
@ -1599,7 +1661,7 @@ class PesterMemoList(QtGui.QDialog):
self.channelarea.sortByColumn(0, QtCore.Qt.AscendingOrder) self.channelarea.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.connect(self.channelarea, self.connect(self.channelarea,
QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *, int)'), QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *, int)'),
self, QtCore.SLOT('joinActivatedMemo()')) self, QtCore.SLOT('AcceptSelection()'))
self.orjoinlabel = QtGui.QLabel("OR MAKE A NEW MEMO:") self.orjoinlabel = QtGui.QLabel("OR MAKE A NEW MEMO:")
self.newmemo = QtGui.QLineEdit(channel, self) self.newmemo = QtGui.QLineEdit(channel, self)
@ -1616,7 +1678,7 @@ class PesterMemoList(QtGui.QDialog):
self.join = QtGui.QPushButton("JOIN", self) self.join = QtGui.QPushButton("JOIN", self)
self.join.setDefault(True) self.join.setDefault(True)
self.connect(self.join, QtCore.SIGNAL('clicked()'), self.connect(self.join, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('checkEmpty()')) self, QtCore.SLOT('AcceptIfSelectionMade()'))
layout_ok = QtGui.QHBoxLayout() layout_ok = QtGui.QHBoxLayout()
layout_ok.addWidget(self.cancel) layout_ok.addWidget(self.cancel)
layout_ok.addWidget(self.join) layout_ok.addWidget(self.join)
@ -1644,8 +1706,12 @@ class PesterMemoList(QtGui.QDialog):
def newmemoname(self): def newmemoname(self):
return self.newmemo.text() return self.newmemo.text()
def selectedmemo(self):
return self.channelarea.currentItem() def SelectedMemos(self):
return self.channelarea.selectedItems()
def HasSelection(self):
return len(self.SelectedMemos()) > 0 or self.newmemoname()
def updateChannels(self, channels): def updateChannels(self, channels):
for c in channels: for c in channels:
@ -1663,13 +1729,12 @@ class PesterMemoList(QtGui.QDialog):
item.setIcon(QtGui.QIcon(theme["memos/memoicon"])) item.setIcon(QtGui.QIcon(theme["memos/memoicon"]))
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def checkEmpty(self): def AcceptIfSelectionMade(self):
newmemo = self.newmemoname() if self.HasSelection():
selectedmemo = self.selectedmemo() self.AcceptSelection()
if newmemo or selectedmemo:
self.accept()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def joinActivatedMemo(self): def AcceptSelection(self):
self.accept() self.accept()

19
nickservmsgs.py Normal file
View file

@ -0,0 +1,19 @@
# 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."
}
def translate(msg):
if msg in messages:
if messages[msg] == "":
return msg
return messages[msg]
return None

View file

@ -6,7 +6,9 @@ from datetime import timedelta
from PyQt4 import QtGui from PyQt4 import QtGui
from generic import mysteryTime from generic import mysteryTime
from quirks import ScriptQuirks
from pyquirks import PythonQuirks from pyquirks import PythonQuirks
from luaquirks import LuaQuirks
_ctag_begin = re.compile(r'(?i)<c=(.*?)>') _ctag_begin = re.compile(r'(?i)<c=(.*?)>')
_gtag_begin = re.compile(r'(?i)<g[a-f]>') _gtag_begin = re.compile(r'(?i)<g[a-f]>')
@ -23,12 +25,16 @@ _format_begin = re.compile(r'(?i)<([ibu])>')
_format_end = re.compile(r'(?i)</([ibu])>') _format_end = re.compile(r'(?i)</([ibu])>')
_honk = re.compile(r"(?i)\bhonk\b") _honk = re.compile(r"(?i)\bhonk\b")
quirkloader = PythonQuirks() quirkloader = ScriptQuirks()
quirkloader.add(PythonQuirks())
quirkloader.add(LuaQuirks())
quirkloader.loadAll()
print quirkloader.funcre()
_functionre = re.compile(r"%s" % quirkloader.funcre()) _functionre = re.compile(r"%s" % quirkloader.funcre())
_groupre = re.compile(r"\\([0-9]+)") _groupre = re.compile(r"\\([0-9]+)")
def reloadQuirkFunctions(): def reloadQuirkFunctions():
quirkloader.load() quirkloader.loadAll()
global _functionre global _functionre
_functionre = re.compile(r"%s" % quirkloader.funcre()) _functionre = re.compile(r"%s" % quirkloader.funcre())
@ -395,8 +401,8 @@ def parseRegexpFunctions(to):
backr = _groupre.search(mo.group()) backr = _groupre.search(mo.group())
if backr is not None: if backr is not None:
current.append(backreference(backr.group(1))) current.append(backreference(backr.group(1)))
elif mo.group() in functiondict.keys(): elif mo.group()[:-1] in functiondict.keys():
p = parseLeaf(functiondict[mo.group()], current) p = parseLeaf(functiondict[mo.group()[:-1]], current)
current.append(p) current.append(p)
current = p current = p
elif mo.group() == ")": elif mo.group() == ")":

View file

@ -40,7 +40,7 @@ else:
minor = int(vnum[vnum.find(".")+1:]) minor = int(vnum[vnum.find(".")+1:])
if not ((major > 4) or (major == 4 and minor >= 6)): if not ((major > 4) or (major == 4 and minor >= 6)):
print "ERROR: Pesterchum requires Qt version >= 4.6" print "ERROR: Pesterchum requires Qt version >= 4.6"
print "You currently have version " + vnum + ". Please ungrade Qt" print "You currently have version " + vnum + ". Please upgrade Qt"
exit() exit()
import ostools import ostools
@ -89,7 +89,8 @@ from memos import PesterMemo, MemoTabWindow, TimeTracker
from irc import PesterIRC from irc import PesterIRC
from logviewer import PesterLogUserSelect, PesterLogViewer from logviewer import PesterLogUserSelect, PesterLogViewer
from bugreport import BugReporter from bugreport import BugReporter
from randomer import RandomHandler from randomer import RandomHandler, RANDNICK
import nickservmsgs
# Rawr, fuck you OSX leopard # Rawr, fuck you OSX leopard
if not ostools.isOSXLeopard(): if not ostools.isOSXLeopard():
@ -103,6 +104,9 @@ canon_handles = ["apocalypseArisen", "arsenicCatnip", "arachnidsGrip", "adiosTor
"caligulasAquarium", "cuttlefishCuller", "carcinoGeneticist", "centaursTesticle", \ "caligulasAquarium", "cuttlefishCuller", "carcinoGeneticist", "centaursTesticle", \
"grimAuxiliatrix", "gallowsCalibrator", "gardenGnostic", "ectoBiologist", \ "grimAuxiliatrix", "gallowsCalibrator", "gardenGnostic", "ectoBiologist", \
"twinArmageddons", "terminallyCapricious", "turntechGodhead", "tentacleTherapist"] "twinArmageddons", "terminallyCapricious", "turntechGodhead", "tentacleTherapist"]
CUSTOMBOTS = ["CALSPRITE", RANDNICK.upper()]
BOTNAMES = ["NICKSERV", "CHANSERV", "MEMOSERV", "OPERSERV", "HELPSERV"]
BOTNAMES.extend(CUSTOMBOTS)
class waitingMessageHolder(object): class waitingMessageHolder(object):
@ -982,10 +986,12 @@ class TrollSlumWindow(QtGui.QFrame):
unblockChumSignal = QtCore.pyqtSignal(QtCore.QString) unblockChumSignal = QtCore.pyqtSignal(QtCore.QString)
class PesterWindow(MovingWindow): class PesterWindow(MovingWindow):
def __init__(self, options, parent=None): def __init__(self, options, parent=None, app=None):
MovingWindow.__init__(self, parent, MovingWindow.__init__(self, parent,
(QtCore.Qt.CustomizeWindowHint | (QtCore.Qt.CustomizeWindowHint |
QtCore.Qt.FramelessWindowHint)) QtCore.Qt.FramelessWindowHint))
self.autoJoinDone = False
self.app = app
self.convos = CaseInsensitiveDict() self.convos = CaseInsensitiveDict()
self.memos = CaseInsensitiveDict() self.memos = CaseInsensitiveDict()
self.tabconvo = None self.tabconvo = None
@ -1056,7 +1062,7 @@ class PesterWindow(MovingWindow):
exitaction = QtGui.QAction(self.theme["main/menus/client/exit"], self) exitaction = QtGui.QAction(self.theme["main/menus/client/exit"], self)
self.exitaction = exitaction self.exitaction = exitaction
self.connect(exitaction, QtCore.SIGNAL('triggered()'), self.connect(exitaction, QtCore.SIGNAL('triggered()'),
self, QtCore.SLOT('close()')) self.app, QtCore.SLOT('quit()'))
userlistaction = QtGui.QAction(self.theme["main/menus/client/userlist"], self) userlistaction = QtGui.QAction(self.theme["main/menus/client/userlist"], self)
self.userlistaction = userlistaction self.userlistaction = userlistaction
self.connect(userlistaction, QtCore.SIGNAL('triggered()'), self.connect(userlistaction, QtCore.SIGNAL('triggered()'),
@ -1305,6 +1311,7 @@ class PesterWindow(MovingWindow):
t.show() t.show()
elif not self.config.notifyOptions() & self.config.NEWCONVO: elif not self.config.notifyOptions() & self.config.NEWCONVO:
if msg[:11] != "PESTERCHUM:": if msg[:11] != "PESTERCHUM:":
if handle.upper() not in BOTNAMES:
t = self.tm.Toast("From: %s" % handle, re.sub("</?c(=.*?)?>", "", msg)) t = self.tm.Toast("From: %s" % handle, re.sub("</?c(=.*?)?>", "", msg))
t.show() t.show()
else: else:
@ -1355,17 +1362,23 @@ class PesterWindow(MovingWindow):
systemColor = QtGui.QColor(self.theme["memos/systemMsgColor"]) systemColor = QtGui.QColor(self.theme["memos/systemMsgColor"])
msg = "<c=%s>%s</c>" % (systemColor.name(), msg) msg = "<c=%s>%s</c>" % (systemColor.name(), msg)
memo.addMessage(msg, handle) memo.addMessage(msg, handle)
if self.config.soundOn(): mentioned = False
if self.config.memoSound():
if self.config.nameSound():
m = convertTags(msg, "text") m = convertTags(msg, "text")
if m.find(":") <= 3: if m.find(":") <= 3:
m = m[m.find(":"):] m = m[m.find(":"):]
for search in self.userprofile.getMentions(): for search in self.userprofile.getMentions():
if re.search(search, m): if re.search(search, m):
mentioned = True
break
if mentioned:
if self.config.notifyOptions() & self.config.INITIALS: if self.config.notifyOptions() & self.config.INITIALS:
t = self.tm.Toast(chan, re.sub("</?c(=.*?)?>", "", msg)) t = self.tm.Toast(chan, re.sub("</?c(=.*?)?>", "", msg))
t.show() t.show()
if self.config.soundOn():
if self.config.memoSound():
if self.config.nameSound():
if mentioned:
self.namesound.play() self.namesound.play()
return return
if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I): if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I):
@ -1413,18 +1426,12 @@ class PesterWindow(MovingWindow):
self.connect(convoWindow, QtCore.SIGNAL('windowClosed(QString)'), self.connect(convoWindow, QtCore.SIGNAL('windowClosed(QString)'),
self, QtCore.SLOT('closeConvo(QString)')) self, QtCore.SLOT('closeConvo(QString)'))
self.convos[chum.handle] = convoWindow self.convos[chum.handle] = convoWindow
if unicode(chum.handle).upper() == "CHANSERV" or \ if unicode(chum.handle).upper() in BOTNAMES:
unicode(chum.handle).upper() == "NICKSERV" or \
unicode(chum.handle).upper() == "MEMOSERV" or \
unicode(chum.handle).upper() == "OPERSERV" or \
unicode(chum.handle).upper() == "HELPSERV":
convoWindow.toggleQuirks(True) convoWindow.toggleQuirks(True)
convoWindow.quirksOff.setChecked(True) convoWindow.quirksOff.setChecked(True)
if unicode(chum.handle).upper() in CUSTOMBOTS:
self.newConvoStarted.emit(QtCore.QString(chum.handle), initiated)
else: else:
if unicode(chum.handle).upper() == "CALSPRITE" or \
unicode(chum.handle).upper() == "RANDOMENCOUNTER":
convoWindow.toggleQuirks(True)
convoWindow.quirksOff.setChecked(True)
self.newConvoStarted.emit(QtCore.QString(chum.handle), initiated) self.newConvoStarted.emit(QtCore.QString(chum.handle), initiated)
convoWindow.show() convoWindow.show()
@ -1706,11 +1713,25 @@ class PesterWindow(MovingWindow):
else: else:
self.waitingMessages.answerMessage() self.waitingMessages.answerMessage()
def doAutoIdentify(self):
if self.userprofile.getAutoIdentify():
self.sendMessage.emit("identify " + self.userprofile.getNickServPass(), "NickServ")
def doAutoJoins(self):
if not self.autoJoinDone:
self.autoJoinDone = True
for memo in self.userprofile.getAutoJoins():
self.newMemo(memo, "i")
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def connected(self): def connected(self):
if self.loadingscreen: if self.loadingscreen:
self.loadingscreen.done(QtGui.QDialog.Accepted) self.loadingscreen.done(QtGui.QDialog.Accepted)
self.loadingscreen = None self.loadingscreen = None
self.doAutoIdentify()
self.doAutoJoins()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def blockSelectedChum(self): def blockSelectedChum(self):
curChumListing = self.chumList.currentItem() curChumListing = self.chumList.currentItem()
@ -1803,6 +1824,11 @@ class PesterWindow(MovingWindow):
self.randhandler.incoming(msg) self.randhandler.incoming(msg)
elif self.convos.has_key(h): elif self.convos.has_key(h):
self.newMessage(h, m) self.newMessage(h, m)
elif h.upper() == "NICKSERV" and "PESTERCHUM:" not in m:
m = nickservmsgs.translate(m)
if m:
t = self.tm.Toast("NickServ:", m)
t.show()
@QtCore.pyqtSlot(QtCore.QString, QtCore.QString) @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
def deliverInvite(self, handle, channel): def deliverInvite(self, handle, channel):
msgbox = QtGui.QMessageBox() msgbox = QtGui.QMessageBox()
@ -2058,18 +2084,21 @@ class PesterWindow(MovingWindow):
self.memochooser.show() self.memochooser.show()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def joinSelectedMemo(self): def joinSelectedMemo(self):
newmemo = self.memochooser.newmemoname()
selectedmemo = self.memochooser.selectedmemo()
time = unicode(self.memochooser.timeinput.text()) time = unicode(self.memochooser.timeinput.text())
secret = self.memochooser.secretChannel.isChecked() secret = self.memochooser.secretChannel.isChecked()
invite = self.memochooser.inviteChannel.isChecked() invite = self.memochooser.inviteChannel.isChecked()
if newmemo:
if self.memochooser.newmemoname():
newmemo = self.memochooser.newmemoname()
channel = "#"+unicode(newmemo).replace(" ", "_") channel = "#"+unicode(newmemo).replace(" ", "_")
channel = re.sub(r"[^A-Za-z0-9#_]", "", channel) channel = re.sub(r"[^A-Za-z0-9#_]", "", channel)
self.newMemo(channel, time, secret=secret, invite=invite) self.newMemo(channel, time, secret=secret, invite=invite)
elif selectedmemo:
channel = "#"+unicode(selectedmemo.target) for SelectedMemo in self.memochooser.SelectedMemos():
channel = "#"+unicode(SelectedMemo.target)
self.newMemo(channel, time) self.newMemo(channel, time)
self.memochooser = None self.memochooser = None
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def memoChooserClose(self): def memoChooserClose(self):
@ -2443,6 +2472,16 @@ class PesterWindow(MovingWindow):
self.leftChannel.emit("#pesterchum") self.leftChannel.emit("#pesterchum")
else: else:
self.joinChannel.emit("#pesterchum") self.joinChannel.emit("#pesterchum")
# nickserv
autoidentify = self.optionmenu.autonickserv.isChecked()
nickservpass = self.optionmenu.nickservpass.text()
self.userprofile.setAutoIdentify(autoidentify)
self.userprofile.setNickServPass(str(nickservpass))
# auto join memos
autojoins = []
for i in range(self.optionmenu.autojoinlist.count()):
autojoins.append(str(self.optionmenu.autojoinlist.item(i).text()))
self.userprofile.setAutoJoins(autojoins)
# advanced # advanced
## user mode ## user mode
if self.advanced: if self.advanced:
@ -2463,7 +2502,7 @@ class PesterWindow(MovingWindow):
self, QtCore.SLOT('closeToTray()')); self, QtCore.SLOT('closeToTray()'));
elif old == 2: # quit elif old == 2: # quit
self.disconnect(button, QtCore.SIGNAL('clicked()'), self.disconnect(button, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('close()')); self.app, QtCore.SLOT('quit()'));
if setting == 0: # minimize to taskbar if setting == 0: # minimize to taskbar
self.connect(button, QtCore.SIGNAL('clicked()'), self.connect(button, QtCore.SIGNAL('clicked()'),
@ -2473,7 +2512,7 @@ class PesterWindow(MovingWindow):
self, QtCore.SLOT('closeToTray()')); self, QtCore.SLOT('closeToTray()'));
elif setting == 2: # quit elif setting == 2: # quit
self.connect(button, QtCore.SIGNAL('clicked()'), self.connect(button, QtCore.SIGNAL('clicked()'),
self, QtCore.SLOT('close()')); self.app, QtCore.SLOT('quit()'));
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def themeSelectOverride(self): def themeSelectOverride(self):
@ -2618,6 +2657,8 @@ class PesterWindow(MovingWindow):
@QtCore.pyqtSlot(QtCore.QString) @QtCore.pyqtSlot(QtCore.QString)
def myHandleChanged(self, handle): def myHandleChanged(self, handle):
if self.profile().handle == handle: if self.profile().handle == handle:
self.doAutoIdentify()
self.doAutoJoins()
return return
else: else:
self.nickCollision(self.profile().handle, handle) self.nickCollision(self.profile().handle, handle)
@ -2695,6 +2736,7 @@ class MainProgram(QtCore.QObject):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
self.app = QtGui.QApplication(sys.argv) self.app = QtGui.QApplication(sys.argv)
self.app.setApplicationName("Pesterchum 3.14") self.app.setApplicationName("Pesterchum 3.14")
self.app.setQuitOnLastWindowClosed(False)
options = self.oppts(sys.argv[1:]) options = self.oppts(sys.argv[1:])
@ -2707,7 +2749,7 @@ class MainProgram(QtCore.QObject):
print "Warning: No sound! %s" % (e) print "Warning: No sound! %s" % (e)
else: else:
print "Warning: No sound!" print "Warning: No sound!"
self.widget = PesterWindow(options) self.widget = PesterWindow(options, app=self.app)
self.widget.show() self.widget.show()
self.trayicon = PesterTray(PesterIcon(self.widget.theme["main/icon"]), self.widget, self.app) self.trayicon = PesterTray(PesterIcon(self.widget.theme["main/icon"]), self.widget, self.app)
@ -2729,7 +2771,7 @@ class MainProgram(QtCore.QObject):
self.widget, QtCore.SLOT('showMinimized()')) self.widget, QtCore.SLOT('showMinimized()'))
exitAction = QtGui.QAction("EXIT", self) exitAction = QtGui.QAction("EXIT", self)
self.trayicon.connect(exitAction, QtCore.SIGNAL('triggered()'), self.trayicon.connect(exitAction, QtCore.SIGNAL('triggered()'),
self.widget, QtCore.SLOT('close()')) self.app, QtCore.SLOT('quit()'))
self.traymenu.addAction(miniAction) self.traymenu.addAction(miniAction)
self.traymenu.addAction(exitAction) self.traymenu.addAction(exitAction)
@ -2942,7 +2984,7 @@ Click this message to never see this again.")
widget.loadingscreen = LoadingScreen(widget) widget.loadingscreen = LoadingScreen(widget)
widget.loadingscreen.loadinglabel.setText(msg) widget.loadingscreen.loadinglabel.setText(msg)
self.connect(widget.loadingscreen, QtCore.SIGNAL('rejected()'), self.connect(widget.loadingscreen, QtCore.SIGNAL('rejected()'),
widget, QtCore.SLOT('close()')) widget.app, QtCore.SLOT('quit()'))
self.connect(self.widget.loadingscreen, QtCore.SIGNAL('tryAgain()'), self.connect(self.widget.loadingscreen, QtCore.SIGNAL('tryAgain()'),
self, QtCore.SLOT('tryAgain()')) self, QtCore.SLOT('tryAgain()'))
if hasattr(self, 'irc') and self.irc.registeredIRC: if hasattr(self, 'irc') and self.irc.registeredIRC:

View file

@ -370,6 +370,7 @@ class userProfile(object):
self.mentions = [r"\b(%s)\b" % ("|".join(initials))] self.mentions = [r"\b(%s)\b" % ("|".join(initials))]
else: else:
self.mentions = [] self.mentions = []
self.autojoins = []
else: else:
fp = open("%s/%s.js" % (self.profiledir, user)) fp = open("%s/%s.js" % (self.profiledir, user))
self.userprofile = json.load(fp) self.userprofile = json.load(fp)
@ -394,6 +395,20 @@ class userProfile(object):
else: else:
self.userprofile["mentions"] = [] self.userprofile["mentions"] = []
self.mentions = self.userprofile["mentions"] self.mentions = self.userprofile["mentions"]
if "autojoins" not in self.userprofile:
self.userprofile["autojoins"] = []
self.autojoins = self.userprofile["autojoins"]
try:
with open(_datadir+"passwd.js") as fp:
self.passwd = json.load(fp)
except Exception, e:
self.passwd = {}
self.autoidentify = False
self.nickservpass = ""
if self.chat.handle in self.passwd:
self.autoidentify = self.passwd[self.chat.handle]["auto"]
self.nickservpass = self.passwd[self.chat.handle]["pw"]
def setMood(self, mood): def setMood(self, mood):
self.chat.mood = mood self.chat.mood = mood
@ -435,6 +450,28 @@ class userProfile(object):
self.save() self.save()
def getTheme(self): def getTheme(self):
return self.theme return self.theme
def getAutoIdentify(self):
return self.autoidentify
def setAutoIdentify(self, b):
self.autoidentify = b
if self.chat.handle not in self.passwd:
self.passwd[self.chat.handle] = {}
self.passwd[self.chat.handle]["auto"] = b
self.saveNickServPass()
def getNickServPass(self):
return self.nickservpass
def setNickServPass(self, pw):
self.nickservpass = pw
if self.chat.handle not in self.passwd:
self.passwd[self.chat.handle] = {}
self.passwd[self.chat.handle]["pw"] = pw
self.saveNickServPass()
def getAutoJoins(self):
return self.autojoins
def setAutoJoins(self, autojoins):
self.autojoins = autojoins
self.userprofile["autojoins"] = self.autojoins
self.save()
def save(self): def save(self):
handle = self.chat.handle handle = self.chat.handle
if handle[0:12] == "pesterClient": if handle[0:12] == "pesterClient":
@ -447,6 +484,17 @@ class userProfile(object):
fp = open("%s/%s.js" % (self.profiledir, handle), 'w') fp = open("%s/%s.js" % (self.profiledir, handle), 'w')
fp.write(jsonoutput) fp.write(jsonoutput)
fp.close() fp.close()
def saveNickServPass(self):
# remove profiles with no passwords
for h,t in self.passwd.items():
if "auto" not in t or "pw" not in t or t["pw"] == "":
del self.passwd[h]
try:
jsonoutput = json.dumps(self.passwd, indent=4)
except ValueError, e:
raise e
with open(_datadir+"passwd.js", 'w') as fp:
fp.write(jsonoutput)
@staticmethod @staticmethod
def newUserProfile(chatprofile): def newUserProfile(chatprofile):
if os.path.exists("%s/%s.js" % (_datadir+"profiles", chatprofile.handle)): if os.path.exists("%s/%s.js" % (_datadir+"profiles", chatprofile.handle)):

View file

@ -1,57 +1,26 @@
import os, sys, imp, re, ostools import os, sys, imp, re, ostools
from quirks import ScriptQuirks
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
class PythonQuirks(object): class PythonQuirks(ScriptQuirks):
def __init__(self): def loadModule(self, name, filename):
self._datadir = ostools.getDataDir() return imp.load_source(name, filename)
self.home = os.getcwd()
self.quirks = {}
self.last = {}
self.load()
def load(self): def getExtension(self):
self.last = self.quirks.copy() return '.py'
self.quirks.clear()
filenames = []
if not os.path.exists(os.path.join(self.home, 'quirks')):
os.mkdir(os.path.join(self.home, 'quirks'))
for fn in os.listdir(os.path.join(self.home, 'quirks')):
if fn.endswith('.py') and not fn.startswith('_'):
filenames.append(os.path.join(self.home, 'quirks', fn))
if self._datadir:
if not os.path.exists(os.path.join(self._datadir, 'quirks')):
os.mkdir(os.path.join(self._datadir, 'quirks'))
for fn in os.listdir(os.path.join(self._datadir, 'quirks')):
if fn.endswith('.py') and not fn.startswith('_'):
filenames.append(os.path.join(self._datadir, 'quirks', fn))
modules = [] def modHas(self, module, attr):
for filename in filenames: if attr == 'commands':
name = os.path.basename(filename)[:-3] variables = vars(module)
try: module = imp.load_source(name, filename)
except Exception, e:
print "Error loading %s: %s (in pyquirks.py)" % (name, e)
msgbox = QtGui.QMessageBox()
msgbox.setWindowTitle("Error!")
msgbox.setText("Error loading %s: %s (in pyquirks.py)" % (name, e))
msgbox.exec_()
else:
if hasattr(module, 'setup'):
module.setup()
self.register(vars(module))
modules.append(name)
for k in self.last:
if k in self.quirks:
if self.last[k] == self.quirks[k]:
del self.quirks[k]
if self.quirks:
print 'Registered quirks:', '), '.join(self.quirks) + ")"
else:print "Warning: Couldn't find any python quirks"
def register(self, variables):
for name, obj in variables.iteritems(): for name, obj in variables.iteritems():
if hasattr(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.iteritems():
if self.modHas(obj, 'command'):
try: try:
if not isinstance(obj("test"), basestring): if not isinstance(obj("test"), basestring):
raise Exception raise Exception
@ -62,13 +31,5 @@ class PythonQuirks(object):
msgbox.setText("Quirk malformed: %s" % (obj.command)) msgbox.setText("Quirk malformed: %s" % (obj.command))
msgbox.exec_() msgbox.exec_()
else: else:
self.quirks[obj.command+"("] = obj self.quirks[obj.command] = obj
def funcre(self):
if not self.quirks:
return r"\\[0-9]+"
f = r"("
for q in self.quirks:
f = f + q[:-1]+r"\(|"
f = f + r"\)|\\[0-9]+)"
return f

91
quirks.py Normal file
View file

@ -0,0 +1,91 @@
import os, sys, re, ostools
from PyQt4 import QtGui, QtCore
class ScriptQuirks(object):
def __init__(self):
self._datadir = ostools.getDataDir()
self.home = os.getcwd()
self.quirks = {}
self.last = {}
self.scripts = []
#self.load()
def loadModule(self, name, filename):
raise Exception
def modHas(self, module, attr):
return False
def loadAll(self):
self.last = self.quirks.copy()
self.quirks.clear()
for script in self.scripts:
print script.getExtension()
script.load()
#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 self.quirks:
print 'Registered quirks:', '(), '.join(self.quirks) + "()"
else:
print "Warning: Couldn't find any script quirks"
def add(self, script):
self.scripts.append(script)
def load(self):
self.last = self.quirks.copy()
self.quirks.clear()
extension = self.getExtension()
filenames = []
if not os.path.exists(os.path.join(self.home, 'quirks')):
os.mkdir(os.path.join(self.home, 'quirks'))
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 self._datadir:
if not os.path.exists(os.path.join(self._datadir, 'quirks')):
os.mkdir(os.path.join(self._datadir, 'quirks'))
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:
extension_length = len(self.getExtension())
name = os.path.basename(filename)[:-extension_length]
try:
module = self.loadModule(name, filename)
if module is None:
continue
except Exception, e:
print "Error loading %s: %s (in quirks.py)" % (os.path.basename(name), e)
msgbox = QtGui.QMessageBox()
msgbox.setWindowTitle("Error!")
msgbox.setText("Error loading %s: %s (in quirks.py)" % (os.path.basename(filename), e))
msgbox.exec_()
else:
if self.modHas(module, 'setup'):
module.setup()
if self.modHas(module, 'commands'):
self.register(module)
modules.append(name)
for k in self.last:
if k in self.quirks:
if self.last[k] == self.quirks[k]:
del self.quirks[k]
def funcre(self):
if not self.quirks:
return r"\\[0-9]+"
f = r"("
for q in self.quirks:
f = f + q+r"\(|"
f = f + r"\)|\\[0-9]+)"
return f

18
quirks/example.lua Normal file
View file

@ -0,0 +1,18 @@
module(..., package.seeall)
commands = {}
local function upper(text)
return string.upper(text)
end
commands.luaupper = upper
local function lower(text)
return string.lower(text)
end
commands.lualower = lower
local function utf8reverse(text)
return text:gsub("([\194-\244][\128-\191]+)", string.reverse):reverse()
end
commands.luareverse = utf8reverse

View file

@ -1,9 +1,11 @@
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
RANDNICK = "randomEncounter"
class RandomHandler(QtCore.QObject): class RandomHandler(QtCore.QObject):
def __init__(self, parent): def __init__(self, parent):
QtCore.QObject.__init__(self, parent) QtCore.QObject.__init__(self, parent)
self.randNick = "randomEncounter" self.randNick = RANDNICK
self.mainwindow = parent self.mainwindow = parent
self.queue = [] self.queue = []
self.running = False self.running = False

View file

@ -74,7 +74,11 @@ class ToastMachine(object):
t = None t = None
for (k,v) in self.machine.types.iteritems(): for (k,v) in self.machine.types.iteritems():
if self.machine.type == k: if self.machine.type == k:
try:
args = inspect.getargspec(v.__init__).args args = inspect.getargspec(v.__init__).args
except:
args = []
extras = {} extras = {}
if 'parent' in args: if 'parent' in args:
extras['parent'] = self.machine.parent extras['parent'] = self.machine.parent