mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 02:49:27 +01:00
Updated to use commands.wrap.
This commit is contained in:
parent
1a6b4aaa95
commit
c6e5d22f6b
@ -53,8 +53,8 @@ from itertools import imap
|
||||
|
||||
import supybot.conf as conf
|
||||
import supybot.utils as utils
|
||||
from supybot.commands import *
|
||||
import supybot.webutils as webutils
|
||||
import supybot.privmsgs as privmsgs
|
||||
import supybot.registry as registry
|
||||
import supybot.callbacks as callbacks
|
||||
|
||||
@ -76,34 +76,27 @@ class Http(callbacks.Privmsg):
|
||||
except webutils.WebError, e:
|
||||
irc.error(str(e))
|
||||
|
||||
def headers(self, irc, msg, args):
|
||||
def headers(self, irc, msg, args, url):
|
||||
"""<url>
|
||||
|
||||
Returns the HTTP headers of <url>. Only HTTP urls are valid, of
|
||||
course.
|
||||
"""
|
||||
url = privmsgs.getArgs(args)
|
||||
if not url.startswith('http://'):
|
||||
irc.error('Only HTTP urls are valid.')
|
||||
return
|
||||
fd = webutils.getUrlFd(url)
|
||||
try:
|
||||
s = ', '.join(['%s: %s' % (k, v) for (k, v) in fd.headers.items()])
|
||||
irc.reply(s)
|
||||
finally:
|
||||
fd.close()
|
||||
headers = wrap(headers, ['httpUrl'])
|
||||
|
||||
_doctypeRe = re.compile(r'(<!DOCTYPE[^>]+>)', re.M)
|
||||
def doctype(self, irc, msg, args):
|
||||
def doctype(self, irc, msg, args, url):
|
||||
"""<url>
|
||||
|
||||
Returns the DOCTYPE string of <url>. Only HTTP urls are valid, of
|
||||
course.
|
||||
"""
|
||||
url = privmsgs.getArgs(args)
|
||||
if not url.startswith('http://'):
|
||||
irc.error('Only HTTP urls are valid.')
|
||||
return
|
||||
size = conf.supybot.protocols.http.peekSize()
|
||||
s = webutils.getUrl(url, size=size)
|
||||
m = self._doctypeRe.search(s)
|
||||
@ -112,17 +105,14 @@ class Http(callbacks.Privmsg):
|
||||
irc.reply(s)
|
||||
else:
|
||||
irc.reply('That URL has no specified doctype.')
|
||||
doctype = wrap(doctype, ['httpUrl'])
|
||||
|
||||
def size(self, irc, msg, args):
|
||||
def size(self, irc, msg, args, url):
|
||||
"""<url>
|
||||
|
||||
Returns the Content-Length header of <url>. Only HTTP urls are valid,
|
||||
of course.
|
||||
"""
|
||||
url = privmsgs.getArgs(args)
|
||||
if not url.startswith('http://'):
|
||||
irc.error('Only HTTP urls are valid.')
|
||||
return
|
||||
fd = webutils.getUrlFd(url)
|
||||
try:
|
||||
try:
|
||||
@ -138,15 +128,13 @@ class Http(callbacks.Privmsg):
|
||||
'but it\'s longer than %s bytes.' % (url, size))
|
||||
finally:
|
||||
fd.close()
|
||||
size = wrap(size, ['httpUrl'])
|
||||
|
||||
def title(self, irc, msg, args):
|
||||
def title(self, irc, msg, args, url):
|
||||
"""<url>
|
||||
|
||||
Returns the HTML <title>...</title> of a URL.
|
||||
"""
|
||||
url = privmsgs.getArgs(args)
|
||||
if '://' not in url:
|
||||
url = 'http://%s' % url
|
||||
size = conf.supybot.protocols.http.peekSize()
|
||||
text = webutils.getUrl(url, size=size)
|
||||
m = self._titleRe.search(text)
|
||||
@ -155,13 +143,13 @@ class Http(callbacks.Privmsg):
|
||||
else:
|
||||
irc.reply('That URL appears to have no HTML title '
|
||||
'within the first %s bytes.' % size)
|
||||
title = wrap(title, ['httpUrl'])
|
||||
|
||||
def freshmeat(self, irc, msg, args):
|
||||
def freshmeat(self, irc, msg, args, project):
|
||||
"""<project name>
|
||||
|
||||
Returns Freshmeat data about a given project.
|
||||
"""
|
||||
project = privmsgs.getArgs(args)
|
||||
project = ''.join(project.split())
|
||||
url = 'http://www.freshmeat.net/projects-xml/%s' % project
|
||||
try:
|
||||
@ -183,14 +171,14 @@ class Http(callbacks.Privmsg):
|
||||
(project, lastupdated, vitality, popularity, version))
|
||||
except FreshmeatException, e:
|
||||
irc.error(str(e))
|
||||
freshmeat = wrap(freshmeat, ['something'])
|
||||
|
||||
def stockquote(self, irc, msg, args):
|
||||
def stockquote(self, irc, msg, args, symbol):
|
||||
"""<company symbol>
|
||||
|
||||
Gets the information about the current price and change from the
|
||||
previous day of a given company (represented by a stock symbol).
|
||||
"""
|
||||
symbol = privmsgs.getArgs(args)
|
||||
if ' ' in symbol:
|
||||
irc.error('Only one stockquote can be looked up at a time.')
|
||||
return
|
||||
@ -205,15 +193,15 @@ class Http(callbacks.Privmsg):
|
||||
else:
|
||||
m = 'I couldn\'t find a listing for %s' % symbol
|
||||
irc.error(m)
|
||||
stockquote = wrap(stockquote, ['something'])
|
||||
|
||||
_cyborgRe = re.compile(r'<p class="mediumheader">(.*?)</p>', re.I)
|
||||
def cyborg(self, irc, msg, args):
|
||||
def cyborg(self, irc, msg, args, name):
|
||||
"""[<name>]
|
||||
|
||||
Returns a cyborg acronym for <name> from <http://www.cyborgname.com/>.
|
||||
If <name> is not specified, uses that of the user.
|
||||
"""
|
||||
name = privmsgs.getArgs(args, required=0, optional=1)
|
||||
if not name:
|
||||
name = msg.nick
|
||||
name = urllib.quote(name)
|
||||
@ -226,15 +214,15 @@ class Http(callbacks.Privmsg):
|
||||
irc.reply(s)
|
||||
else:
|
||||
irc.errorPossibleBug('No cyborg name returned.')
|
||||
cyborg = wrap(cyborg, [additional('somethingWithoutSpaces')])
|
||||
|
||||
_acronymre = re.compile(r'valign="middle" width="7\d%" bgcolor="[^"]+">'
|
||||
r'(?:<b>)?([^<]+)')
|
||||
def acronym(self, irc, msg, args):
|
||||
def acronym(self, irc, msg, args, acronym):
|
||||
"""<acronym>
|
||||
|
||||
Displays acronym matches from acronymfinder.com
|
||||
"""
|
||||
acronym = privmsgs.getArgs(args)
|
||||
url = 'http://www.acronymfinder.com/' \
|
||||
'af-query.asp?String=exact&Acronym=%s' % urllib.quote(acronym)
|
||||
html = webutils.getUrl(url)
|
||||
@ -253,16 +241,16 @@ class Http(callbacks.Privmsg):
|
||||
else:
|
||||
s = ', or '.join(defs)
|
||||
irc.reply('%s could be %s' % (acronym, s))
|
||||
acronym = wrap(acronym, ['text'])
|
||||
|
||||
_netcraftre = re.compile(r'td align="left">\s+<a[^>]+>(.*?)<a href',
|
||||
re.S | re.I)
|
||||
def netcraft(self, irc, msg, args):
|
||||
def netcraft(self, irc, msg, args, hostname):
|
||||
"""<hostname|ip>
|
||||
|
||||
Returns Netcraft.com's determination of what operating system and
|
||||
webserver is running on the host given.
|
||||
"""
|
||||
hostname = privmsgs.getArgs(args)
|
||||
url = 'http://uptime.netcraft.com/up/graph/?host=%s' % hostname
|
||||
html = webutils.getUrl(url)
|
||||
m = self._netcraftre.search(html)
|
||||
@ -275,6 +263,7 @@ class Http(callbacks.Privmsg):
|
||||
irc.reply('No results found for %s.' % hostname)
|
||||
else:
|
||||
irc.error('The format of page the was odd.')
|
||||
netcraft = wrap(netcraft, ['text'])
|
||||
|
||||
def kernel(self, irc, msg, args):
|
||||
"""takes no arguments
|
||||
@ -299,16 +288,16 @@ class Http(callbacks.Privmsg):
|
||||
irc.reply('The latest stable kernel is %s; '
|
||||
'the latest snapshot of the stable kernel is %s; '
|
||||
'the latest beta kernel is %s.' % (stable, snapshot, mm))
|
||||
kernel = wrap(kernel)
|
||||
|
||||
_pgpkeyre = re.compile(r'pub\s+\d{4}\w/<a href="([^"]+)">'
|
||||
r'([^<]+)</a>[^>]+>([^<]+)</a>')
|
||||
def pgpkey(self, irc, msg, args):
|
||||
def pgpkey(self, irc, msg, args, search):
|
||||
"""<search words>
|
||||
|
||||
Returns the results of querying pgp.mit.edu for keys that match
|
||||
the <search words>.
|
||||
"""
|
||||
search = privmsgs.getArgs(args)
|
||||
urlClean = search.replace(' ', '+')
|
||||
host = 'http://pgp.mit.edu:11371'
|
||||
url = '%s/pks/lookup?op=index&search=%s' % (host, urlClean)
|
||||
@ -326,19 +315,20 @@ class Http(callbacks.Privmsg):
|
||||
irc.reply(s)
|
||||
finally:
|
||||
fd.close()
|
||||
pgpkey = wrap(pgpkey, ['text'])
|
||||
|
||||
_filextre = re.compile(
|
||||
r'<strong>Extension:</strong>.*?<tr>.*?</tr>\s+<tr>\s+<td colspan='
|
||||
r'"2">(?:<a href[^>]+>([^<]+)</a>\s+|([^<]+))</td>\s+<td>'
|
||||
r'(?:<a href[^>]+>([^<]+)</a>|<img src="images/spacer.gif"(.))',
|
||||
re.I|re.S)
|
||||
def extension(self, irc, msg, args):
|
||||
def extension(self, irc, msg, args, ext):
|
||||
"""<ext>
|
||||
|
||||
Returns the results of querying filext.com for file extensions that
|
||||
match <ext>.
|
||||
"""
|
||||
ext = privmsgs.getArgs(args)
|
||||
# XXX This probably ought to be handled in a converter from commands.py
|
||||
invalid = '|<>\^=?/[]";,*'
|
||||
for c in invalid:
|
||||
if c in ext:
|
||||
@ -367,16 +357,16 @@ class Http(callbacks.Privmsg):
|
||||
irc.reply(utils.commaAndify(res))
|
||||
else:
|
||||
irc.error('No matching file extensions were found.')
|
||||
extension = wrap(extension, ['text'])
|
||||
|
||||
_zipinfore = re.compile(r'Latitude<BR>\(([^)]+)\)</th><th>Longitude<BR>'
|
||||
r'\(([^)]+)\).*?<tr>(.*?)</tr>', re.I)
|
||||
_zipstatre = re.compile(r'(Only about \d+,\d{3} of.*?in use.)')
|
||||
def zipinfo(self, irc, msg, args):
|
||||
def zipinfo(self, irc, msg, args, zipcode):
|
||||
"""<zip code>
|
||||
|
||||
Returns a plethora of information for the given <zip code>.
|
||||
"""
|
||||
zipcode = privmsgs.getArgs(args)
|
||||
try:
|
||||
int(zipcode)
|
||||
except ValueError:
|
||||
@ -416,6 +406,7 @@ class Http(callbacks.Privmsg):
|
||||
'Longitude: %s (%s)' % (info[-1], longdir),
|
||||
]
|
||||
irc.reply('; '.join(resp))
|
||||
zipinfo = wrap(zipinfo, ['text'])
|
||||
|
||||
|
||||
Class = Http
|
||||
|
@ -48,10 +48,10 @@ import supybot.registry as registry
|
||||
import supybot.conf as conf
|
||||
import supybot.ircdb as ircdb
|
||||
import supybot.utils as utils
|
||||
from supybot.commands import *
|
||||
import supybot.ircmsgs as ircmsgs
|
||||
import supybot.plugins as plugins
|
||||
import supybot.ircutils as ircutils
|
||||
import supybot.privmsgs as privmsgs
|
||||
import supybot.callbacks as callbacks
|
||||
|
||||
try:
|
||||
@ -154,33 +154,32 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
|
||||
s = 'jots down a new quote for %s' % msg.nick
|
||||
irc.reply(s, action=True, prefixName=False)
|
||||
|
||||
def grab(self, irc, msg, args):
|
||||
def grab(self, irc, msg, args, channel, nick):
|
||||
"""[<channel>] <nick>
|
||||
|
||||
Grabs a quote from <channel> by <nick> for the quotegrabs table.
|
||||
<channel> is only necessary if the message isn't sent in the channel
|
||||
itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
nick = privmsgs.getArgs(args)
|
||||
if ircutils.strEqual(nick, msg.nick):
|
||||
irc.error('You can\'t quote grab yourself.')
|
||||
return
|
||||
for m in reversed(irc.state.history):
|
||||
# XXX Note that the channel isn't used here. Also note that
|
||||
# channel might be None since we use channeldb
|
||||
if m.command == 'PRIVMSG' and ircutils.strEqual(m.nick, nick):
|
||||
self._grab(irc, m, msg.prefix)
|
||||
irc.replySuccess()
|
||||
return
|
||||
irc.error('I couldn\'t find a proper message to grab.')
|
||||
grab = wrap(grab, ['channeldb', 'nick'])
|
||||
|
||||
def quote(self, irc, msg, args):
|
||||
def quote(self, irc, msg, args, channel, nick):
|
||||
"""[<channel>] <nick>
|
||||
|
||||
Returns <nick>'s latest quote grab in <channel>. <channel> is only
|
||||
necessary if the message isn't sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
nick = privmsgs.getArgs(args)
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
cursor.execute("""SELECT quote FROM quotegrabs
|
||||
@ -191,16 +190,17 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
|
||||
else:
|
||||
text = cursor.fetchone()[0]
|
||||
irc.reply(text)
|
||||
quote = wrap(quote, ['channeldb', 'nick'])
|
||||
|
||||
def list(self, irc, msg, args):
|
||||
"""<nick>
|
||||
def list(self, irc, msg, args, channel, nick):
|
||||
"""[<channel>] <nick>
|
||||
|
||||
Returns a list of shortened quotes that have been grabbed for <nick>
|
||||
as well as the id of each quote. These ids can be used to get the
|
||||
full quote.
|
||||
full quote. <channel> is only necessary if the message isn't sent in
|
||||
the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
nick = privmsgs.getArgs(args)
|
||||
# XXX This doesn't seem to be channel-specific in practice.
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
cursor.execute("""SELECT id, quote FROM quotegrabs
|
||||
@ -216,15 +216,17 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
|
||||
item_str = utils.ellipsisify('#%s: %s' % (id, quote), 50)
|
||||
l.append(item_str)
|
||||
irc.reply(utils.commaAndify(l))
|
||||
list = wrap(list, ['channeldb', 'nick'])
|
||||
|
||||
def randomquote(self, irc, msg, args):
|
||||
"""[<nick>]
|
||||
# XXX Could we rename this "random", to fit in more with the rest of
|
||||
# our plugins?
|
||||
def randomquote(self, irc, msg, args, channel, nick):
|
||||
"""[<channel>] [<nick>]
|
||||
|
||||
Returns a randomly grabbed quote, optionally choosing only from those
|
||||
quotes grabbed for <nick>.
|
||||
quotes grabbed for <nick>. <channel> is only necessary if the message
|
||||
isn't sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
nick = privmsgs.getArgs(args, required=0, optional=1)
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
if nick:
|
||||
@ -243,19 +245,14 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
|
||||
return
|
||||
quote = cursor.fetchone()[0]
|
||||
irc.reply(quote)
|
||||
randomquote = wrap(randomquote, ['channeldb', additional('nick')])
|
||||
|
||||
def get(self, irc, msg, args):
|
||||
"""<id>
|
||||
def get(self, irc, msg, args, channel, id):
|
||||
"""[<channel>] <id>
|
||||
|
||||
Return the quotegrab with the given <id>.
|
||||
Return the quotegrab with the given <id>. <channel> is only necessary
|
||||
if the message isn't sent in the channel itself.
|
||||
"""
|
||||
id = privmsgs.getArgs(args)
|
||||
try:
|
||||
id = int(id)
|
||||
except ValueError:
|
||||
irc.error('%r does not appear to be a valid quotegrab id' % id)
|
||||
return
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
cursor.execute("""SELECT quote, hostmask, added_at, added_by
|
||||
@ -272,6 +269,7 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
|
||||
grabber = grabber_mask
|
||||
irc.reply('%s (Said by: %s; grabbed by %s at %s)' % \
|
||||
(quote, hostmask, grabber, time_str))
|
||||
get = wrap(get, ['channeldb', 'id'])
|
||||
|
||||
|
||||
Class = QuoteGrabs
|
||||
|
@ -40,7 +40,7 @@ __contributors__ = {}
|
||||
import supybot.plugins as plugins
|
||||
|
||||
import time
|
||||
TIME = time
|
||||
TIME = time # For later use.
|
||||
|
||||
import dateutil.parser
|
||||
|
||||
@ -117,7 +117,7 @@ class Time(callbacks.Privmsg):
|
||||
seconds += i
|
||||
irc.reply(str(seconds))
|
||||
|
||||
def at(self, irc, msg, args):
|
||||
def at(self, irc, msg, args, s):
|
||||
"""<time string>
|
||||
|
||||
Returns the number of seconds since epoch <time string> is.
|
||||
@ -125,20 +125,19 @@ class Time(callbacks.Privmsg):
|
||||
and see if it will work.
|
||||
"""
|
||||
now = int(time.time())
|
||||
s = privmsgs.getArgs(args)
|
||||
new = parse(s)
|
||||
if new != now:
|
||||
irc.reply(new)
|
||||
else:
|
||||
irc.error('That\'s right now!')
|
||||
at = wrap(at, ['text'])
|
||||
|
||||
def until(self, irc, msg, args):
|
||||
def until(self, irc, msg, args, s):
|
||||
"""<time string>
|
||||
|
||||
Returns the number of seconds until <time string>.
|
||||
"""
|
||||
now = int(time.time())
|
||||
s = privmsgs.getArgs(args)
|
||||
new = parse(s)
|
||||
if new != now:
|
||||
if new - now < 0:
|
||||
@ -146,6 +145,7 @@ class Time(callbacks.Privmsg):
|
||||
irc.reply(new-now)
|
||||
else:
|
||||
irc.error('That\'s right now!')
|
||||
until = wrap(until, ['text'])
|
||||
|
||||
def ctime(self, irc, msg, args, seconds):
|
||||
"""[<seconds since epoch>]
|
||||
|
@ -45,8 +45,8 @@ import string
|
||||
|
||||
import supybot.conf as conf
|
||||
import supybot.utils as utils
|
||||
from supybot.commands import *
|
||||
import supybot.ircutils as ircutils
|
||||
import supybot.privmsgs as privmsgs
|
||||
import supybot.registry as registry
|
||||
import supybot.callbacks as callbacks
|
||||
|
||||
@ -143,13 +143,12 @@ class Words(callbacks.Privmsg):
|
||||
'sorted order) to be at %s. Contact the owner of this '
|
||||
'bot to remedy this situation.' %
|
||||
self.registryValue('file'))
|
||||
def crossword(self, irc, msg, args):
|
||||
def crossword(self, irc, msg, args, word):
|
||||
"""<word>
|
||||
|
||||
Gives the possible crossword completions for <word>; use underscores
|
||||
('_') to denote blank spaces.
|
||||
"""
|
||||
word = privmsgs.getArgs(args).lower()
|
||||
word = re.escape(word)
|
||||
word = word.replace('\\_', '_') # Stupid re.escape escapes underscores!
|
||||
word = word.replace('_', '.')
|
||||
@ -197,32 +196,31 @@ class Words(callbacks.Privmsg):
|
||||
def endGame(self, channel):
|
||||
del self.games[channel]
|
||||
|
||||
def letters(self, irc, msg, args):
|
||||
def letters(self, irc, msg, args, channel):
|
||||
"""[<channel>]
|
||||
|
||||
Returns the unused letters that can be guessed in the hangman game
|
||||
in <channel>. <channel> is only necessary if the message isn't sent in
|
||||
the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
if channel in self.games:
|
||||
game = self.games[channel]
|
||||
if game is not None:
|
||||
self._hangmanReply(irc, channel, ' '.join(game.unused))
|
||||
return
|
||||
irc.error('There is currently no hangman game in %s.' % channel)
|
||||
letters = wrap(letters, ['channel'])
|
||||
|
||||
def _hangmanReply(self, irc, channel, s):
|
||||
s = self.registryValue('hangman.prefix', channel=channel) + s
|
||||
irc.reply(s, prefixName=False)
|
||||
|
||||
def hangman(self, irc, msg, args):
|
||||
def hangman(self, irc, msg, args, channel):
|
||||
"""[<channel>]
|
||||
|
||||
Creates a new game of hangman in <channel>. <channel> is only
|
||||
necessary if the message isn't sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
# Fill our dictionary of games
|
||||
if channel not in self.games:
|
||||
self.games[channel] = None
|
||||
@ -259,14 +257,14 @@ class Words(callbacks.Privmsg):
|
||||
irc.reply('Sorry, there is already a game going on. '
|
||||
'%s left before the game times out.' %
|
||||
utils.timeElapsed(game.timeout - secondsElapsed))
|
||||
hangman = wrap(hangman, ['channel'])
|
||||
|
||||
def guess(self, irc, msg, args):
|
||||
def guess(self, irc, msg, args, channel, letter):
|
||||
"""[<channel>] <letter|word>
|
||||
|
||||
Try to guess a single letter or the whole word. If you try to guess
|
||||
the whole word and you are wrong, you automatically lose.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
try:
|
||||
game = self.games[channel]
|
||||
if game is None:
|
||||
@ -274,7 +272,6 @@ class Words(callbacks.Privmsg):
|
||||
except KeyError:
|
||||
irc.error('There is no hangman game going on right now.')
|
||||
return
|
||||
letter = privmsgs.getArgs(args)
|
||||
game.timeGuess = time.time()
|
||||
# User input a valid letter that hasn't been already tried
|
||||
if letter in game.unused:
|
||||
@ -326,6 +323,7 @@ class Words(callbacks.Privmsg):
|
||||
self._hangmanReply(irc, channel,
|
||||
'You lose! The word was %r.' % game.hidden)
|
||||
self.endGame(channel)
|
||||
guess = wrap(guess, ['channel', 'somethingWithoutSpaces'])
|
||||
###
|
||||
# END HANGMAN
|
||||
###
|
||||
|
@ -65,8 +65,6 @@ class HttpTest(PluginTestCase):
|
||||
self.failIf(m.args[1].count('Error') > 1)
|
||||
|
||||
def testTitle(self):
|
||||
self.assertResponse('title slashdot.org',
|
||||
'Slashdot: News for nerds, stuff that matters')
|
||||
self.assertResponse('title http://www.slashdot.org/',
|
||||
'Slashdot: News for nerds, stuff that matters')
|
||||
self.assertNotRegexp('title '
|
||||
|
Loading…
Reference in New Issue
Block a user