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

This commit is contained in:
Valentin Lorentz 2015-03-02 20:35:56 +00:00
commit 18bafc725f
18 changed files with 158 additions and 145 deletions

View File

@ -41,10 +41,6 @@ def configure(advanced):
from supybot.questions import expect, anything, something, yn from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Admin', True) conf.registerPlugin('Admin', True)
Admin = conf.registerPlugin('Admin') Admin = conf.registerPlugin('Admin')
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(Admin, 'someConfigVariableName',
# registry.Boolean(False, """Help for someConfigVariableName."""))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -231,34 +231,6 @@ class Admin(callbacks.Plugin):
irc.reply(irc.nick) irc.reply(irc.nick)
nick = wrap(nick, [additional('nick'), additional('something')]) nick = wrap(nick, [additional('nick'), additional('something')])
@internationalizeDocstring
def part(self, irc, msg, args, channel, reason):
"""[<channel>] [<reason>]
Tells the bot to part the list of channels you give it. <channel> is
only necessary if you want the bot to part a channel other than the
current channel. If <reason> is specified, use it as the part
message.
"""
if channel is None:
if irc.isChannel(msg.args[0]):
channel = msg.args[0]
else:
irc.error(Raise=True)
try:
network = conf.supybot.networks.get(irc.network)
network.channels().remove(channel)
except KeyError:
pass
if channel not in irc.state.channels:
irc.error(_('I\'m not in %s.') % channel, Raise=True)
irc.queueMsg(ircmsgs.part(channel, reason or msg.nick))
if msg.nick in irc.state.channels[channel].users:
irc.noReply()
else:
irc.replySuccess()
part = wrap(part, [optional('validChannel'), additional('text')])
class capability(callbacks.Commands): class capability(callbacks.Commands):
@internationalizeDocstring @internationalizeDocstring

View File

@ -94,27 +94,6 @@ class AdminTestCase(PluginTestCase):
self.assertEqual(m.args[0], '#foo') self.assertEqual(m.args[0], '#foo')
self.assertEqual(m.args[1], 'key') self.assertEqual(m.args[1], 'key')
def testPart(self):
def getAfterJoinMessages():
m = self.irc.takeMsg()
self.assertEqual(m.command, 'MODE')
m = self.irc.takeMsg()
self.assertEqual(m.command, 'MODE')
m = self.irc.takeMsg()
self.assertEqual(m.command, 'WHO')
self.assertError('part #foo')
self.assertRegexp('part #foo', 'not in')
self.irc.feedMsg(ircmsgs.join('#foo', prefix=self.prefix))
getAfterJoinMessages()
m = self.getMsg('part #foo')
self.assertEqual(m.command, 'PART')
self.irc.feedMsg(ircmsgs.join('#foo', prefix=self.prefix))
getAfterJoinMessages()
m = self.getMsg('part #foo reason')
self.assertEqual(m.command, 'PART')
self.assertEqual(m.args[0], '#foo')
self.assertEqual(m.args[1], 'reason')
def testNick(self): def testNick(self):
original = conf.supybot.nick() original = conf.supybot.nick()
try: try:

View File

@ -51,6 +51,15 @@ conf.registerChannelValue(Channel, 'nicksInPrivate',
registry.Boolean(True, _("""Determines whether the output of 'nicks' will registry.Boolean(True, _("""Determines whether the output of 'nicks' will
be sent in private. This prevents mass-highlights of a channel's users, be sent in private. This prevents mass-highlights of a channel's users,
accidental or on purpose."""))) accidental or on purpose.""")))
conf.registerChannelValue(Channel, 'rejoinDelay',
registry.NonNegativeInteger(0, _("""Determines how many seconds the bot will wait
before rejoining a channel if kicked and
supybot.plugins.Channel.alwaysRejoin is on.""")))
conf.registerChannelValue(Channel, 'partMsg',
registry.String('$version', _("""Determines what part message should be
used by default. If the part command is called without a part message,
this will be used. If this value is empty, then no part message will
be used (they are optional in the IRC protocol). The standard
substitutions ($version, $nick, etc.) are all handled appropriately.""")))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -30,6 +30,7 @@
import sys import sys
import fnmatch import fnmatch
import time
import supybot.conf as conf import supybot.conf as conf
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
@ -46,6 +47,7 @@ class Channel(callbacks.Plugin):
"""This plugin provides various commands for channel management, such """This plugin provides various commands for channel management, such
as setting modes and channel-wide bans/ignores/capabilities. This is as setting modes and channel-wide bans/ignores/capabilities. This is
a core Supybot plugin that should not be removed!""" a core Supybot plugin that should not be removed!"""
def __init__(self, irc): def __init__(self, irc):
self.__parent = super(Channel, self) self.__parent = super(Channel, self)
self.__parent.__init__(irc) self.__parent.__init__(irc)
@ -55,10 +57,18 @@ class Channel(callbacks.Plugin):
channel = msg.args[0] channel = msg.args[0]
if msg.args[1] == irc.nick: if msg.args[1] == irc.nick:
if self.registryValue('alwaysRejoin', channel): if self.registryValue('alwaysRejoin', channel):
self.log.info('Kicked from %s by %s. Rejoining.' % delay = self.registryValue('rejoinDelay', channel)
(channel, msg.prefix))
networkGroup = conf.supybot.networks.get(irc.network) networkGroup = conf.supybot.networks.get(irc.network)
irc.sendMsg(networkGroup.channels.join(channel)) if delay:
def f():
irc.sendMsg(networkGroup.channels.join(channel))
schedule.addEvent(f, time.time() + delay)
self.log.info('Kicked from %s by %s. Rejoining after %s '
'seconds.', channel, msg.prefix, delay)
else:
self.log.info('Kicked from %s by %s. Rejoining.',
channel, msg.prefix)
irc.sendMsg(networkGroup.channels.join(channel))
else: else:
self.log.info('Kicked from %s by %s. Not auto-rejoining.' % self.log.info('Kicked from %s by %s. Not auto-rejoining.' %
(channel, msg.prefix)) (channel, msg.prefix))
@ -258,17 +268,22 @@ class Channel(callbacks.Plugin):
any('nickInChannel')]) any('nickInChannel')])
@internationalizeDocstring @internationalizeDocstring
def cycle(self, irc, msg, args, channel): def cycle(self, irc, msg, args, channel, reason):
"""[<channel>] """[<channel>]
If you have the #channel,op capability, this will cause the bot to If you have the #channel,op capability, this will cause the bot to
"cycle", or PART and then JOIN the channel. <channel> is only necessary "cycle", or PART and then JOIN the channel. <channel> is only necessary
if the message isn't sent in the channel itself. if the message isn't sent in the channel itself. If <reason> is not
specified, the default part message specified in
supybot.plugins.Channel.partMsg will be used. No part message will be
used if neither a cycle reason nor a default part message is given.
""" """
self._sendMsg(irc, ircmsgs.part(channel, msg.nick)) reason = (reason or self.registryValue("partMsg", channel))
reason = ircutils.standardSubstitute(irc, msg, reason)
self._sendMsg(irc, ircmsgs.part(channel, reason))
networkGroup = conf.supybot.networks.get(irc.network) networkGroup = conf.supybot.networks.get(irc.network)
self._sendMsg(irc, networkGroup.channels.join(channel)) self._sendMsg(irc, networkGroup.channels.join(channel))
cycle = wrap(cycle, ['op']) cycle = wrap(cycle, ['op', additional('text')])
@internationalizeDocstring @internationalizeDocstring
def kick(self, irc, msg, args, channel, nicks, reason): def kick(self, irc, msg, args, channel, nicks, reason):
@ -941,6 +956,41 @@ class Channel(callbacks.Plugin):
self.alertOps(irc, channel, text, frm=msg.nick) self.alertOps(irc, channel, text, frm=msg.nick)
alert = wrap(alert, ['inChannel', 'text']) alert = wrap(alert, ['inChannel', 'text'])
@internationalizeDocstring
def part(self, irc, msg, args, channel, reason):
"""[<channel>] [<reason>]
Tells the bot to part the list of channels you give it. <channel> is
only necessary if you want the bot to part a channel other than the
current channel. If <reason> is specified, use it as the part
message. Otherwise, the default part message specified in
supybot.plugins.Channel.partMsg will be used. No part message will be
used if no default is configured.
"""
if channel is None:
if irc.isChannel(msg.args[0]):
channel = msg.args[0]
else:
irc.error(Raise=True)
capability = ircdb.makeChannelCapability(channel, 'op')
hostmask = irc.state.nickToHostmask(msg.nick)
if not ircdb.checkCapability(hostmask, capability):
irc.errorNoCapability(capability, Raise=True)
try:
network = conf.supybot.networks.get(irc.network)
network.channels().remove(channel)
except KeyError:
pass
if channel not in irc.state.channels:
irc.error(_('I\'m not in %s.') % channel, Raise=True)
reason = (reason or self.registryValue("partMsg", channel))
reason = ircutils.standardSubstitute(irc, msg, reason)
irc.queueMsg(ircmsgs.part(channel, reason))
if msg.nick in irc.state.channels[channel].users:
irc.noReply()
else:
irc.replySuccess()
part = wrap(part, [optional('validChannel'), additional('text')])
Class = Channel Class = Channel

View File

@ -251,6 +251,27 @@ class ChannelTestCase(ChannelPluginTestCase):
def testNicks(self): def testNicks(self):
self.assertResponse('channel nicks', 'bar, foo, and test') self.assertResponse('channel nicks', 'bar, foo, and test')
self.assertResponse('channel nicks --count', '3') self.assertResponse('channel nicks --count', '3')
def testPart(self):
def getAfterJoinMessages():
m = self.irc.takeMsg()
self.assertEqual(m.command, 'MODE')
m = self.irc.takeMsg()
self.assertEqual(m.command, 'MODE')
m = self.irc.takeMsg()
self.assertEqual(m.command, 'WHO')
self.assertError('part #foo')
self.assertRegexp('part #foo', 'not in')
self.irc.feedMsg(ircmsgs.join('#foo', prefix=self.prefix))
getAfterJoinMessages()
m = self.getMsg('part #foo')
self.assertEqual(m.command, 'PART')
self.irc.feedMsg(ircmsgs.join('#foo', prefix=self.prefix))
getAfterJoinMessages()
m = self.getMsg('part #foo reason')
self.assertEqual(m.command, 'PART')
self.assertEqual(m.args[0], '#foo')
self.assertEqual(m.args[1], 'reason')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -111,7 +111,7 @@ class Connection:
capstr, msgid = re.search('<(.*)> (<.*>)$', string).groups() capstr, msgid = re.search('<(.*)> (<.*>)$', string).groups()
self.capabilities = capstr.split('.') self.capabilities = capstr.split('.')
self.messageid = msgid self.messageid = msgid
def getcapabilities(self): def getcapabilities(self):
"""Returns a list of the capabilities advertised by the server.""" """Returns a list of the capabilities advertised by the server."""
return self.capabilities return self.capabilities
@ -126,7 +126,7 @@ class Connection:
network traffic!""" network traffic!"""
if hasattr(self, 'dbdescs'): if hasattr(self, 'dbdescs'):
return self.dbdescs return self.dbdescs
self.sendcommand("SHOW DB") self.sendcommand("SHOW DB")
self.dbdescs = self.get100dict() self.dbdescs = self.get100dict()
return self.dbdescs return self.dbdescs
@ -165,7 +165,7 @@ class Connection:
def sendcommand(self, command): def sendcommand(self, command):
"""Takes a command, without a newline character, and sends it to """Takes a command, without a newline character, and sends it to
the server.""" the server."""
self.wfile.write(command.encode('ascii') + b"\n") self.wfile.write(command.encode('utf-8') + b"\n")
def define(self, database, word): def define(self, database, word):
"""Returns a list of Definition objects for each matching """Returns a list of Definition objects for each matching
@ -182,7 +182,7 @@ class Connection:
if database != '*' and database != '!' and \ if database != '*' and database != '!' and \
not database in self.getdbdescs(): not database in self.getdbdescs():
raise Exception("Invalid database '%s' specified" % database) raise Exception("Invalid database '%s' specified" % database)
self.sendcommand("DEFINE " + enquote(database) + " " + enquote(word)) self.sendcommand("DEFINE " + enquote(database) + " " + enquote(word))
code = self.getresultcode()[0] code = self.getresultcode()[0]
@ -249,11 +249,11 @@ class Database:
a database name.""" a database name."""
self.conn = dictconn self.conn = dictconn
self.name = dbname self.name = dbname
def getname(self): def getname(self):
"""Returns the short name for this database.""" """Returns the short name for this database."""
return self.name return self.name
def getdescription(self): def getdescription(self):
if hasattr(self, 'description'): if hasattr(self, 'description'):
return self.description return self.description
@ -264,7 +264,7 @@ class Database:
else: else:
self.description = self.conn.getdbdescs()[self.getname()] self.description = self.conn.getdbdescs()[self.getname()]
return self.description return self.description
def getinfo(self): def getinfo(self):
"""Returns a string of info describing this database.""" """Returns a string of info describing this database."""
if hasattr(self, 'info'): if hasattr(self, 'info'):

View File

@ -243,8 +243,7 @@ class Network(callbacks.Plugin):
L.append(format(_('is on %L'), normal)) L.append(format(_('is on %L'), normal))
else: else:
if command == 'whois': if command == 'whois':
L = [_('isn\'t on any non-secret channels or is using a ' L = [_('isn\'t on any publicly visible channels')]
'channel-list hiding umode.')]
else: else:
L = [] L = []
channels = format('%L', L) channels = format('%L', L)

View File

@ -46,10 +46,11 @@ conf.registerGlobalValue(Owner, 'public',
registry.Boolean(True, """Determines whether this plugin is publicly registry.Boolean(True, """Determines whether this plugin is publicly
visible.""")) visible."""))
conf.registerGlobalValue(Owner, 'quitMsg', conf.registerGlobalValue(Owner, 'quitMsg',
registry.String('', """Determines what quit message will be used by default. registry.String('$version', """Determines what quit message will be used by default.
If the quit command is called without a quit message, this will be used. If If the quit command is called without a quit message, this will be used. If
this value is empty, the nick of the person giving the quit command will be this value is empty, the nick of the person giving the quit command will be
used.""")) used. The standard substitutions ($version, $nick, etc.) are all handled
appropriately."""))
conf.registerGroup(conf.supybot.commands, 'renames', orderAlphabetically=True) conf.registerGroup(conf.supybot.commands, 'renames', orderAlphabetically=True)

View File

@ -344,9 +344,11 @@ class Owner(callbacks.Plugin):
Exits the bot with the QUIT message <text>. If <text> is not given, Exits the bot with the QUIT message <text>. If <text> is not given,
the default quit message (supybot.plugins.Owner.quitMsg) will be used. the default quit message (supybot.plugins.Owner.quitMsg) will be used.
If there is no default quitMsg set, your nick will be used. If there is no default quitMsg set, your nick will be used. The standard
substitutions ($version, $nick, etc.) are all handled appropriately.
""" """
text = text or self.registryValue('quitMsg') or msg.nick text = text or self.registryValue('quitMsg') or msg.nick
text = ircutils.standardSubstitute(irc, msg, text)
irc.noReply() irc.noReply()
m = ircmsgs.quit(text) m = ircmsgs.quit(text)
world.upkeep() world.upkeep()

View File

@ -61,8 +61,11 @@ class QuoteGrabsRecord(dbi.Record):
def __str__(self): def __str__(self):
grabber = plugins.getUserName(self.grabber) grabber = plugins.getUserName(self.grabber)
return format(_('%s (Said by: %s; grabbed by %s at %t)'), if self.at:
self.text, self.hostmask, grabber, self.at) return format(_('%s (Said by: %s; grabbed by %s at %t)'),
self.text, self.hostmask, grabber, self.at)
else:
return format('%s', self.text)
class SqliteQuoteGrabsDB(object): class SqliteQuoteGrabsDB(object):
def __init__(self, filename): def __init__(self, filename):
@ -105,7 +108,7 @@ class SqliteQuoteGrabsDB(object):
db.commit() db.commit()
return db return db
def get(self, channel, id): def get(self, channel, id, quoteonly = 0):
db = self._getDb(channel) db = self._getDb(channel)
cursor = db.cursor() cursor = db.cursor()
cursor.execute("""SELECT id, nick, quote, hostmask, added_at, added_by cursor.execute("""SELECT id, nick, quote, hostmask, added_at, added_by
@ -114,8 +117,11 @@ class SqliteQuoteGrabsDB(object):
if len(results) == 0: if len(results) == 0:
raise dbi.NoRecordError raise dbi.NoRecordError
(id, by, quote, hostmask, at, grabber) = results[0] (id, by, quote, hostmask, at, grabber) = results[0]
return QuoteGrabsRecord(id, by=by, text=quote, hostmask=hostmask, if quoteonly is 0:
at=int(at), grabber=grabber) return QuoteGrabsRecord(id, by=by, text=quote, hostmask=hostmask,
at=int(at), grabber=grabber)
else:
return QuoteGrabsRecord(id, text=quote)
def random(self, channel, nick): def random(self, channel, nick):
db = self._getDb(channel) db = self._getDb(channel)
@ -359,6 +365,20 @@ class QuoteGrabs(callbacks.Plugin):
'grabbed quotes in the database?')) 'grabbed quotes in the database?'))
random = wrap(random, ['channeldb', additional('nick')]) random = wrap(random, ['channeldb', additional('nick')])
@internationalizeDocstring
def say(self, irc, msg, args, channel, id):
"""[<channel>] <id>
Return the quotegrab with the given <id>. <channel> is only necessary
if the message isn't sent in the channel itself.
"""
try:
irc.reply(self.db.get(channel, id, 1))
except dbi.NoRecordError:
irc.error(_('No quotegrab for id %s') % utils.str.quoted(id),
Raise=True)
say = wrap(say, ['channeldb', 'id'])
@internationalizeDocstring @internationalizeDocstring
def get(self, irc, msg, args, channel, id): def get(self, irc, msg, args, channel, id):
"""[<channel>] <id> """[<channel>] <id>

View File

@ -225,6 +225,17 @@ class Seen(callbacks.Plugin):
except KeyError: except KeyError:
irc.reply(format(_('I have not seen %s.'), name)) irc.reply(format(_('I have not seen %s.'), name))
def _checkChannelPresence(self, irc, channel, target, you):
if channel not in irc.state.channels:
irc.error(_("I'm not in %s." % channel), Raise=True)
if target not in irc.state.channels[channel].users:
if you:
msg = format(_('You must be in %s to use this command.'), channel)
else:
msg = format(_('%s must be in %s to use this command.'),
target, channel)
irc.error(msg, Raise=True)
@internationalizeDocstring @internationalizeDocstring
def seen(self, irc, msg, args, channel, name): def seen(self, irc, msg, args, channel, name):
"""[<channel>] <nick> """[<channel>] <nick>
@ -236,9 +247,7 @@ class Seen(callbacks.Plugin):
if name and ircutils.strEqual(name, irc.nick): if name and ircutils.strEqual(name, irc.nick):
irc.reply(_("You've found me!")) irc.reply(_("You've found me!"))
return return
if msg.nick not in irc.state.channels[channel].users: self._checkChannelPresence(irc, channel, msg.nick, True)
irc.error(format('You must be in %s to use this command.', channel))
return
self._seen(irc, channel, name) self._seen(irc, channel, name)
seen = wrap(seen, ['channel', 'something']) seen = wrap(seen, ['channel', 'something'])
@ -256,9 +265,7 @@ class Seen(callbacks.Plugin):
if name and ircutils.strEqual(name, irc.nick): if name and ircutils.strEqual(name, irc.nick):
irc.reply(_("You've found me!")) irc.reply(_("You've found me!"))
return return
if msg.nick not in irc.state.channels[channel].users: self._checkChannelPresence(irc, channel, msg.nick, True)
irc.error(format('You must be in %s to use this command.', channel))
return
if name and optlist: if name and optlist:
raise callbacks.ArgumentError raise callbacks.ArgumentError
elif name: elif name:
@ -295,9 +302,7 @@ class Seen(callbacks.Plugin):
Returns the last thing said in <channel>. <channel> is only necessary Returns the last thing said in <channel>. <channel> is only necessary
if the message isn't sent in the channel itself. if the message isn't sent in the channel itself.
""" """
if msg.nick not in irc.state.channels[channel].users: self._checkChannelPresence(irc, channel, msg.nick, True)
irc.error(format('You must be in %s to use this command.', channel))
return
self._last(irc, channel) self._last(irc, channel)
last = wrap(last, ['channel']) last = wrap(last, ['channel'])
@ -327,9 +332,7 @@ class Seen(callbacks.Plugin):
<channel> is only necessary if the message isn't sent in the channel <channel> is only necessary if the message isn't sent in the channel
itself. itself.
""" """
if msg.nick not in irc.state.channels[channel].users: self._checkChannelPresence(irc, channel, msg.nick, True)
irc.error(format('You must be in %s to use this command.', channel))
return
self._user(irc, channel, user) self._user(irc, channel, user)
user = wrap(user, ['channel', 'otherUser']) user = wrap(user, ['channel', 'otherUser'])
@ -343,13 +346,10 @@ class Seen(callbacks.Plugin):
""" """
if nick is None: if nick is None:
nick = msg.nick nick = msg.nick
if channel not in irc.state.channels: you = True
irc.error(_('I am not in %s.') % channel) else:
return you = False
if nick not in irc.state.channels[channel].users: self._checkChannelPresence(irc, channel, nick, you)
irc.error(format(_('%s must be in %s to use this command.'),
('You' if nick == msg.nick else nick), channel))
return
if nick is None: if nick is None:
nick = msg.nick nick = msg.nick
end = None # By default, up until the most recent message. end = None # By default, up until the most recent message.

View File

@ -42,8 +42,8 @@ def configure(advanced):
conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True)
class ShrinkService(registry.OnlySomeStrings): class ShrinkService(registry.OnlySomeStrings):
"""Valid values include 'ln', 'tiny', 'goo', 'ur1', and 'x0'.""" """Valid values include 'tiny', 'goo', 'ur1', and 'x0'."""
validStrings = ('ln', 'tiny', 'goo', 'ur1', 'x0') validStrings = ('tiny', 'goo', 'ur1', 'x0')
class ShrinkCycle(registry.SpaceSeparatedListOfStrings): class ShrinkCycle(registry.SpaceSeparatedListOfStrings):
"""Valid values include 'ln', 'tiny', 'goo', 'ur1', and 'x0'.""" """Valid values include 'ln', 'tiny', 'goo', 'ur1', and 'x0'."""
@ -71,7 +71,7 @@ conf.registerChannelValue(ShrinkUrl, 'shrinkSnarfer',
shrink snarfer is enabled. This snarfer will watch for URLs in the shrink snarfer is enabled. This snarfer will watch for URLs in the
channel, and if they're sufficiently long (as determined by channel, and if they're sufficiently long (as determined by
supybot.plugins.ShrinkUrl.minimumLength) it will post a supybot.plugins.ShrinkUrl.minimumLength) it will post a
smaller URL from either ln-s.net or tinyurl.com, as denoted in smaller URL from tinyurl.com, as denoted in
supybot.plugins.ShrinkUrl.default."""))) supybot.plugins.ShrinkUrl.default.""")))
conf.registerChannelValue(ShrinkUrl.shrinkSnarfer, 'showDomain', conf.registerChannelValue(ShrinkUrl.shrinkSnarfer, 'showDomain',
registry.Boolean(True, _("""Determines whether the snarfer will show the registry.Boolean(True, _("""Determines whether the snarfer will show the

View File

@ -171,37 +171,6 @@ class ShrinkUrl(callbacks.PluginRegexp):
shrinkSnarfer = urlSnarfer(shrinkSnarfer) shrinkSnarfer = urlSnarfer(shrinkSnarfer)
shrinkSnarfer.__doc__ = utils.web._httpUrlRe shrinkSnarfer.__doc__ = utils.web._httpUrlRe
@retry
def _getLnUrl(self, url):
url = utils.web.urlquote(url)
try:
return self.db.get('ln', url)
except KeyError:
text = utils.web.getUrl('http://ln-s.net/home/api.jsp?url=' + url)
text = text.decode()
(code, text) = text.split(None, 1)
text = text.strip()
if code == '200':
self.db.set('ln', url, text)
return text
else:
raise ShrinkError(text)
@internationalizeDocstring
def ln(self, irc, msg, args, url):
"""<url>
Returns an ln-s.net version of <url>.
"""
try:
lnurl = self._getLnUrl(url)
m = irc.reply(lnurl)
if m is not None:
m.tag('shrunken')
except ShrinkError as e:
irc.error(str(e))
ln = thread(wrap(ln, ['httpUrl']))
@retry @retry
def _getTinyUrl(self, url): def _getTinyUrl(self, url):
try: try:

View File

@ -39,8 +39,6 @@ class ShrinkUrlTestCase(ChannelPluginTestCase):
'term=all+your+base+are+belong+to+us' 'term=all+your+base+are+belong+to+us'
tests = {'tiny': [(sfUrl, r'http://tinyurl.com/b7wyvfz'), tests = {'tiny': [(sfUrl, r'http://tinyurl.com/b7wyvfz'),
(udUrl, r'http://tinyurl.com/u479')], (udUrl, r'http://tinyurl.com/u479')],
'ln': [(sfUrl, r'http://ln-s.net/\+PE-'),
(udUrl, r'http://ln-s.net/2\$K')],
'goo': [(sfUrl, r'http://goo.gl/3c59N'), 'goo': [(sfUrl, r'http://goo.gl/3c59N'),
(udUrl, r'http://goo.gl/ocTga')], (udUrl, r'http://goo.gl/ocTga')],
'ur1': [(sfUrl, r'http://ur1.ca/9xl25'), 'ur1': [(sfUrl, r'http://ur1.ca/9xl25'),
@ -62,16 +60,14 @@ class ShrinkUrlTestCase(ChannelPluginTestCase):
origsnarfer = snarfer() origsnarfer = snarfer()
try: try:
self.assertNotError( self.assertNotError(
'config plugins.ShrinkUrl.serviceRotation ln x0') 'config plugins.ShrinkUrl.serviceRotation goo x0')
self.assertError( self.assertError(
'config plugins.ShrinkUrl.serviceRotation ln x1') 'config plugins.ShrinkUrl.serviceRotation goo x1')
snarfer.setValue(True) snarfer.setValue(True)
self.assertSnarfRegexp(self.udUrl, r'.*%s.* \(at' % self.assertSnarfRegexp(self.udUrl, r'.*%s.* \(at' %
self.tests['ln'][1][1]) self.tests['goo'][1][1])
self.assertSnarfRegexp(self.udUrl, r'.*%s.* \(at' % self.assertSnarfRegexp(self.udUrl, r'.*%s.* \(at' %
self.tests['x0'][1][1]) self.tests['x0'][1][1])
self.assertSnarfRegexp(self.udUrl, r'.*%s.* \(at' %
self.tests['ln'][1][1])
finally: finally:
cycle.setValue(origcycle) cycle.setValue(origcycle)
snarfer.setValue(origsnarfer) snarfer.setValue(origsnarfer)
@ -93,9 +89,6 @@ class ShrinkUrlTestCase(ChannelPluginTestCase):
def testTinysnarf(self): def testTinysnarf(self):
self._snarf('tiny') self._snarf('tiny')
def testLnsnarf(self):
self._snarf('ln')
def testGoosnarf(self): def testGoosnarf(self):
self._snarf('goo') self._snarf('goo')

View File

@ -69,6 +69,7 @@ import supybot.i18n as i18n
import supybot.utils as utils import supybot.utils as utils
import supybot.registry as registry import supybot.registry as registry
import supybot.questions as questions import supybot.questions as questions
import supybot.ircutils as ircutils
from supybot.version import version from supybot.version import version
@ -104,6 +105,7 @@ def main():
for irc in world.ircs: for irc in world.ircs:
quitmsg = conf.supybot.plugins.Owner.quitMsg() or \ quitmsg = conf.supybot.plugins.Owner.quitMsg() or \
'Ctrl-C at console.' 'Ctrl-C at console.'
quitmsg = ircutils.standardSubstitute(irc, None, quitmsg)
irc.queueMsg(ircmsgs.quit(quitmsg)) irc.queueMsg(ircmsgs.quit(quitmsg))
irc.die() irc.die()
except SystemExit as e: except SystemExit as e:

View File

@ -470,8 +470,7 @@ registerChannelValue(supybot.reply, 'withNotice',
registerGlobalValue(supybot.reply, 'withNoticeWhenPrivate', registerGlobalValue(supybot.reply, 'withNoticeWhenPrivate',
registry.Boolean(True, _("""Determines whether the bot will reply with a registry.Boolean(True, _("""Determines whether the bot will reply with a
notice when it is sending a private message, in order not to open a /query notice when it is sending a private message, in order not to open a /query
window in clients. This can be overridden by individual users via the user window in clients.""")))
configuration variable reply.withNoticeWhenPrivate.""")))
registerChannelValue(supybot.reply, 'withNickPrefix', registerChannelValue(supybot.reply, 'withNickPrefix',
registry.Boolean(True, _("""Determines whether the bot will always prefix registry.Boolean(True, _("""Determines whether the bot will always prefix

View File

@ -49,7 +49,7 @@ from cStringIO import StringIO as sio
from . import utils from . import utils
from . import minisix from . import minisix
from .version import version
def debug(s, *args): def debug(s, *args):
"""Prints a debug string. Most likely replaced by our logging debug.""" """Prints a debug string. Most likely replaced by our logging debug."""
@ -690,6 +690,7 @@ def standardSubstitute(irc, msg, text, env=None):
'm': localtime[4], 'min': localtime[4], 'minute': localtime[4], 'm': localtime[4], 'min': localtime[4], 'minute': localtime[4],
's': localtime[5], 'sec': localtime[5], 'second': localtime[5], 's': localtime[5], 'sec': localtime[5], 'second': localtime[5],
'tz': time.strftime('%Z', localtime), 'tz': time.strftime('%Z', localtime),
'version': 'Supybot %s' % version,
}) })
if irc: if irc:
vars.update({ vars.update({