Merge branch 'testing' of git://github.com/ProgVal/Limnoria into testing

This commit is contained in:
nyuszika7h 2014-04-10 14:50:11 +02:00
commit 4664d5c293
14 changed files with 148 additions and 32 deletions

1
.gitignore vendored
View File

@ -23,3 +23,4 @@ supybot.egg-info/
test-conf/ test-conf/
test-data/ test-data/
test-logs/ test-logs/
src/version.py

1
.mailmap Normal file
View File

@ -0,0 +1 @@
James McCoy <jamessan@users.sourceforge.net>

80
INSTALL Normal file
View File

@ -0,0 +1,80 @@
Common
First things first: Supybot *requires* at least Python 2.6 and
setuptools. There ain't no getting around it. You can get Python from
http://www.python.org/ and setuptools from
https://pypi.python.org/pypi/setuptools.
Recommended Software
PySQLite -- Version 1.x
Twisted -- Version 1.2.0 or greater
For more information and help on how to use Supybot, checkout
the documents under docs/ (especially GETTING_STARTED and
CONFIGURATION).
So what do you do? That depends on which operating system you're
running. We've split this document up to address the different
methods, so find the section for your operating system and continue
from there.
UNIX/Linux/BSD
If you're installing Python using your distributor's packages, you may
need a python-dev package installed, too. If you don't have a
'/usr/lib/python2.x/distutils' directory or
'/usr/lib/python2.x/config/Makefile' (assuming '/usr/lib/python2.x' is
where your Python libs are installed), then you will need a python-dev
package.
After you extract Supybot and cd into the supybot directory just
created, you'll want to run (as root) 'python setup.py install'. This
will install Supybot globally. If you need to install locally for
whatever reason, see the notes at the end of this section. You'll then
have several new programs installed where Python scripts are normally
installed on your system ('/usr/bin' or '/usr/local/bin' are common on
UNIX systems). The two that might be of particular interest to you, the
new user, are 'supybot' and 'supybot-wizard'. The former, 'supybot', is
the script to run an actual bot; the latter, 'supybot-wizard', is an
in-depth wizard that provides a nice user interface for creating a
registry file for your bot.
Local Install
You can install Supybot in a local directory by using the '--user'
option when running 'setup.py'. E.g., 'python setup.py install
--user' to install into your home directory. You'll now have
a $HOME/.local/bin directory containing Supybot programs ('supybot',
'supybot-wizard', etc.) and a $HOME/.local/lib directory containing the
Supybot libraries.
Windows
**Note**: If you are using an IPV6 connection, you will not be able
to run Supybot under Windows (unless Python has fixed things). Current
versions of Python for Windows are *not* built with IPV6 support. This
isn't expected to be fixed until Python 2.4, at the earliest.
Now that you have Python installed, open up a command prompt. The
easiest way to do this is to open the run dialog (Programs -> run) and
type "cmd" (for Windows 2000/XP/2003) or "command" (for Windows 9x). In
order to reduce the amount of typing you need to do, I suggest adding
Python's directory to your path. If you installed Python using the
default settings, you would then do the following in the command prompt
(otherwise change the path to match your settings)::
set PATH=C:\Python2x\;%PATH%
You should now be able to type 'python' to start the Python
interpreter. Exit by pressing CTRL-Z and then Return. Now that that's
setup, you'll want to cd into the directory that was created when you
unzipped Supybot; I'll assume you unzipped it to 'C:\Supybot' for these
instructions. From 'C:\Supybot', run 'python setup.py install'. This
will install Supybot under 'C:\Python2x\'. You will now have several new
programs installed in 'C:\Python2x\Scripts\'. The two that might be of
particular interest to you, the new user, are 'supybot' and 'supybot-wizard'.
The former, 'supybot', is the script to run an actual bot; the latter,
'supybot-wizard', is an in-depth wizard that provides a nice user interface for
creating a registry file for your bot.

View File

@ -220,7 +220,7 @@ class ChannelLogger(callbacks.Plugin):
def doNick(self, irc, msg): def doNick(self, irc, msg):
oldNick = msg.nick oldNick = msg.nick
newNick = msg.args[0] newNick = msg.args[0]
for (channel, c) in irc.state.channels.iteritems(): for (channel, c) in irc.state.channels.items():
if newNick in c.users: if newNick in c.users:
self.doLog(irc, channel, self.doLog(irc, channel,
'*** %s is now known as %s\n', oldNick, newNick) '*** %s is now known as %s\n', oldNick, newNick)
@ -278,7 +278,9 @@ class ChannelLogger(callbacks.Plugin):
reason = "" reason = ""
if not isinstance(irc, irclib.Irc): if not isinstance(irc, irclib.Irc):
irc = irc.getRealIrc() irc = irc.getRealIrc()
for (channel, chan) in self.lastStates[irc].channels.iteritems(): if irc not in self.lastStates:
return
for (channel, chan) in self.lastStates[irc].channels.items():
if(self.registryValue('showJoinParts', channel)): if(self.registryValue('showJoinParts', channel)):
if msg.nick in chan.users: if msg.nick in chan.users:
self.doLog(irc, channel, self.doLog(irc, channel,

View File

@ -239,7 +239,19 @@ class Config(callbacks.Plugin):
s = group.help() s = group.help()
if s: if s:
if hasattr(group, 'value') and not group._private: if hasattr(group, 'value') and not group._private:
s += _(' (Current value: %s)') % group channel = msg.args[0]
if irc.isChannel(channel) and \
channel in group._children:
globvalue = str(group)
chanvalue = str(group.get(channel))
if chanvalue != globvalue:
s += _(' (Current global value: %s; '
'current channel value: %s)') % \
(globvalue, chanvalue)
else:
s += _(' (Current value: %s)') % group
else:
s += _(' (Current value: %s)') % group
irc.reply(s) irc.reply(s)
else: else:
irc.reply(_('That configuration group exists, but seems to ' irc.reply(_('That configuration group exists, but seems to '

View File

@ -301,6 +301,10 @@ repositories = {
'GLolol', 'GLolol',
'SupyPlugins', 'SupyPlugins',
), ),
'Iota': GithubRepository(
'IotaSpencer',
'supyplugins',
),
} }
class PluginDownloader(callbacks.Plugin): class PluginDownloader(callbacks.Plugin):

View File

@ -97,7 +97,7 @@ conf.registerGlobalValue(RSS, 'defaultNumberOfHeadlines',
registry.PositiveInteger(1, _("""Indicates how many headlines an rss feed registry.PositiveInteger(1, _("""Indicates how many headlines an rss feed
will output by default, if no number is provided."""))) will output by default, if no number is provided.""")))
conf.registerChannelValue(RSS, 'initialAnnounceHeadlines', conf.registerChannelValue(RSS, 'initialAnnounceHeadlines',
registry.PositiveInteger(5, _("""Indicates how many headlines an rss feed registry.Integer(5, _("""Indicates how many headlines an rss feed
will output when it is first added to announce for a channel."""))) will output when it is first added to announce for a channel.""")))
conf.registerChannelValue(RSS, 'keywordWhitelist', conf.registerChannelValue(RSS, 'keywordWhitelist',
registry.SpaceSeparatedSetOfStrings([], _("""Space separated list of registry.SpaceSeparatedSetOfStrings([], _("""Space separated list of

View File

@ -1,6 +1,6 @@
### ###
# Copyright (c) 2002-2004, Jeremiah Fincher # Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2010-2011, James McCoy # Copyright (c) 2010-2011, 2013, James McCoy
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without

View File

@ -1,5 +1,6 @@
### ###
# Copyright (c) 2002-2004, Jeremiah Fincher # Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2013, James McCoy
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -63,12 +64,24 @@ class ChannelDBTestCase(ChannelPluginTestCase):
with conf.supybot.plugins.seen.showLastMessage.context(False): with conf.supybot.plugins.seen.showLastMessage.context(False):
self.assertRegexp('seen any %s' % self.nick, self.assertRegexp('seen any %s' % self.nick,
'^%s was last seen[^:]*' % self.nick) '^%s was last seen[^:]*' % self.nick)
self.assertNotError('config plugins.Seen.minimumNonWildcard 0')
orig = conf.supybot.protocols.irc.strictRfc()
try:
for state in (True, False):
conf.supybot.protocols.irc.strictRfc.setValue(state)
for wildcard in self.wildcardTest:
self.assertRegexp('seen any %s' % wildcard,
'^%s was last seen' % self.nick)
self.assertRegexp('seen any bar*', '^I haven\'t seen anyone matching')
finally:
conf.supybot.protocols.irc.strictRfc.setValue(orig)
def testSeen(self): def testSeen(self):
self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick, self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick,
prefix=self.prefix)) prefix=self.prefix))
self.assertNotError('seen last') self.assertNotError('seen last')
self.assertNotError('list') self.assertNotError('list')
self.assertNotError('config plugins.Seen.minimumNonWildcard 2')
self.assertError('seen *') self.assertError('seen *')
self.assertNotError('seen %s' % self.nick) self.assertNotError('seen %s' % self.nick)
m = self.assertNotError('seen %s' % self.nick.upper()) m = self.assertNotError('seen %s' % self.nick.upper())
@ -76,10 +89,16 @@ class ChannelDBTestCase(ChannelPluginTestCase):
self.assertRegexp('seen user %s' % self.nick, self.assertRegexp('seen user %s' % self.nick,
'^%s was last seen' % self.nick) '^%s was last seen' % self.nick)
self.assertNotError('config plugins.Seen.minimumNonWildcard 0') self.assertNotError('config plugins.Seen.minimumNonWildcard 0')
for wildcard in self.wildcardTest: orig = conf.supybot.protocols.irc.strictRfc()
self.assertRegexp('seen %s' % wildcard, try:
'^%s was last seen' % self.nick) for state in (True, False):
self.assertRegexp('seen bar*', '^I haven\'t seen anyone matching') conf.supybot.protocols.irc.strictRfc.setValue(state)
for wildcard in self.wildcardTest:
self.assertRegexp('seen %s' % wildcard,
'^%s was last seen' % self.nick)
self.assertRegexp('seen bar*', '^I haven\'t seen anyone matching')
finally:
conf.supybot.protocols.irc.strictRfc.setValue(orig)
def testSeenNoUser(self): def testSeenNoUser(self):
self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick, self.irc.feedMsg(ircmsgs.join(self.channel, self.irc.nick,

View File

@ -114,7 +114,6 @@ def catch_web_errors(f):
class Web(callbacks.PluginRegexp): class Web(callbacks.PluginRegexp):
"""Add the help for "@help Web" here.""" """Add the help for "@help Web" here."""
threaded = True
regexps = ['titleSnarfer'] regexps = ['titleSnarfer']
@fetch_sandbox @fetch_sandbox

View File

@ -212,7 +212,8 @@ setup(
url='https://github.com/ProgVal/Limnoria', url='https://github.com/ProgVal/Limnoria',
author_email='progval+limnoria@progval.net', author_email='progval+limnoria@progval.net',
download_url='http://builds.progval.net/limnoria/', download_url='http://builds.progval.net/limnoria/',
description='A modified version of Supybot (an IRC bot)', description='A modified version of Supybot (an IRC bot and framework)',
platforms=['linux', 'linux2', 'win32', 'cygwin', 'darwin'],
long_description=normalizeWhitespace("""A robust, full-featured Python IRC long_description=normalizeWhitespace("""A robust, full-featured Python IRC
bot with a clean and flexible plugin API. Equipped with a complete ACL bot with a clean and flexible plugin API. Equipped with a complete ACL
system for specifying user permissions with as much as per-command system for specifying user permissions with as much as per-command

View File

@ -336,16 +336,14 @@ def getNetworkIrc(irc, msg, args, state, errorIfNoMatch=False):
state.args.append(irc) state.args.append(irc)
def getHaveVoice(irc, msg, args, state, action=_('do that')): def getHaveVoice(irc, msg, args, state, action=_('do that')):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
if state.channel not in irc.state.channels: if state.channel not in irc.state.channels:
state.error(_('I\'m not even in %s.') % state.channel, Raise=True) state.error(_('I\'m not even in %s.') % state.channel, Raise=True)
if not irc.state.channels[state.channel].isVoice(irc.nick): if not irc.state.channels[state.channel].isVoice(irc.nick):
state.error(_('I need to be voiced to %s.') % action, Raise=True) state.error(_('I need to be voiced to %s.') % action, Raise=True)
def getHaveVoicePlus(irc, msg, args, state, action=_('do that')): def getHaveVoicePlus(irc, msg, args, state, action=_('do that')):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
if state.channel not in irc.state.channels: if state.channel not in irc.state.channels:
state.error(_('I\'m not even in %s.') % state.channel, Raise=True) state.error(_('I\'m not even in %s.') % state.channel, Raise=True)
if not irc.state.channels[state.channel].isVoicePlus(irc.nick): if not irc.state.channels[state.channel].isVoicePlus(irc.nick):
@ -354,16 +352,14 @@ def getHaveVoicePlus(irc, msg, args, state, action=_('do that')):
Raise=True) Raise=True)
def getHaveHalfop(irc, msg, args, state, action=_('do that')): def getHaveHalfop(irc, msg, args, state, action=_('do that')):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
if state.channel not in irc.state.channels: if state.channel not in irc.state.channels:
state.error(_('I\'m not even in %s.') % state.channel, Raise=True) state.error(_('I\'m not even in %s.') % state.channel, Raise=True)
if not irc.state.channels[state.channel].isHalfop(irc.nick): if not irc.state.channels[state.channel].isHalfop(irc.nick):
state.error(_('I need to be halfopped to %s.') % action, Raise=True) state.error(_('I need to be halfopped to %s.') % action, Raise=True)
def getHaveHalfopPlus(irc, msg, args, state, action=_('do that')): def getHaveHalfopPlus(irc, msg, args, state, action=_('do that')):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
if state.channel not in irc.state.channels: if state.channel not in irc.state.channels:
state.error(_('I\'m not even in %s.') % state.channel, Raise=True) state.error(_('I\'m not even in %s.') % state.channel, Raise=True)
if not irc.state.channels[state.channel].isHalfopPlus(irc.nick): if not irc.state.channels[state.channel].isHalfopPlus(irc.nick):
@ -372,8 +368,7 @@ def getHaveHalfopPlus(irc, msg, args, state, action=_('do that')):
Raise=True) Raise=True)
def getHaveOp(irc, msg, args, state, action=_('do that')): def getHaveOp(irc, msg, args, state, action=_('do that')):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
if state.channel not in irc.state.channels: if state.channel not in irc.state.channels:
state.error(_('I\'m not even in %s.') % state.channel, Raise=True) state.error(_('I\'m not even in %s.') % state.channel, Raise=True)
if not irc.state.channels[state.channel].isOp(irc.nick): if not irc.state.channels[state.channel].isOp(irc.nick):
@ -400,8 +395,7 @@ def getHostmask(irc, msg, args, state):
def getBanmask(irc, msg, args, state): def getBanmask(irc, msg, args, state):
getHostmask(irc, msg, args, state) getHostmask(irc, msg, args, state)
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
channel = state.channel channel = state.channel
banmaskstyle = conf.supybot.protocols.irc.banmask banmaskstyle = conf.supybot.protocols.irc.banmask
state.args[-1] = banmaskstyle.makeBanmask(state.args[-1]) state.args[-1] = banmaskstyle.makeBanmask(state.args[-1])
@ -477,6 +471,8 @@ def getSeenNick(irc, msg, args, state, errmsg=None):
state.error(errmsg, Raise=True) state.error(errmsg, Raise=True)
def getChannel(irc, msg, args, state): def getChannel(irc, msg, args, state):
if state.channel:
return
if args and irc.isChannel(args[0]): if args and irc.isChannel(args[0]):
channel = args.pop(0) channel = args.pop(0)
elif irc.isChannel(msg.args[0]): elif irc.isChannel(msg.args[0]):
@ -508,8 +504,7 @@ def getChannelDb(irc, msg, args, state, **kwargs):
state.args.append(channel) state.args.append(channel)
def inChannel(irc, msg, args, state): def inChannel(irc, msg, args, state):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
if state.channel not in irc.state.channels: if state.channel not in irc.state.channels:
state.error(_('I\'m not in %s.') % state.channel, Raise=True) state.error(_('I\'m not in %s.') % state.channel, Raise=True)
@ -564,8 +559,7 @@ def getChannelOrGlobal(irc, msg, args, state):
state.args.append(channel) state.args.append(channel)
def checkChannelCapability(irc, msg, args, state, cap): def checkChannelCapability(irc, msg, args, state, cap):
if not state.channel: getChannel(irc, msg, args, state)
getChannel(irc, msg, args, state)
cap = ircdb.canonicalCapability(cap) cap = ircdb.canonicalCapability(cap)
cap = ircdb.makeChannelCapability(state.channel, cap) cap = ircdb.makeChannelCapability(state.channel, cap)
if not ircdb.checkCapability(msg.prefix, cap): if not ircdb.checkCapability(msg.prefix, cap):

View File

@ -361,15 +361,15 @@ class Favicon(SupyHTTPServerCallback):
file_path = conf.supybot.servers.http.favicon() file_path = conf.supybot.servers.http.favicon()
found = False found = False
if file_path: if file_path:
response = None
try: try:
icon = open(file_path, 'r') icon = open(file_path, 'rb')
found = True response = icon.read()
except IOError: except IOError:
pass pass
finally: finally:
icon.close() icon.close()
if found: if response is not None:
response = icon.read()
filename = file_path.rsplit(os.sep, 1)[1] filename = file_path.rsplit(os.sep, 1)[1]
if '.' in filename: if '.' in filename:
ext = filename.rsplit('.', 1)[1] ext = filename.rsplit('.', 1)[1]

View File

@ -209,7 +209,10 @@ class ValidLogLevel(registry.String):
def set(self, s): def set(self, s):
s = s.upper() s = s.upper()
try: try:
level = logging._levelNames[s] try:
level = logging._levelNames[s]
except AttributeError:
level = logging._nameToLevel[s]
except KeyError: except KeyError:
try: try:
level = int(s) level = int(s)