Removed spaces at the end of lines.

This commit is contained in:
Jeremy Fincher 2003-08-20 16:26:23 +00:00
parent 9dc1221045
commit ca646716b1
52 changed files with 5398 additions and 5379 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,275 +1,275 @@
"""Python wrapper for Amazon web APIs """Python wrapper for Amazon web APIs
This module allows you to access Amazon's web APIs, This module allows you to access Amazon's web APIs,
to do things like search Amazon and get the results programmatically. to do things like search Amazon and get the results programmatically.
Described here: Described here:
http://www.amazon.com/webservices http://www.amazon.com/webservices
You need a Amazon-provided license key to use these services. You need a Amazon-provided license key to use these services.
Follow the link above to get one. These functions will look in Follow the link above to get one. These functions will look in
several places (in this order) for the license key: several places (in this order) for the license key:
- the "license_key" argument of each function - the "license_key" argument of each function
- the module-level LICENSE_KEY variable (call setLicense once to set it) - the module-level LICENSE_KEY variable (call setLicense once to set it)
- an environment variable called AMAZON_LICENSE_KEY - an environment variable called AMAZON_LICENSE_KEY
- a file called ".amazonkey" in the current directory - a file called ".amazonkey" in the current directory
- a file called "amazonkey.txt" in the current directory - a file called "amazonkey.txt" in the current directory
- a file called ".amazonkey" in your home directory - a file called ".amazonkey" in your home directory
- a file called "amazonkey.txt" in your home directory - a file called "amazonkey.txt" in your home directory
- a file called ".amazonkey" in the same directory as amazon.py - a file called ".amazonkey" in the same directory as amazon.py
- a file called "amazonkey.txt" in the same directory as amazon.py - a file called "amazonkey.txt" in the same directory as amazon.py
Sample usage: Sample usage:
>>> import amazon >>> import amazon
>>> amazon.setLicense('...') # must get your own key! >>> amazon.setLicense('...') # must get your own key!
>>> pythonBooks = amazon.searchByKeyword('Python') >>> pythonBooks = amazon.searchByKeyword('Python')
>>> pythonBooks[0].ProductName >>> pythonBooks[0].ProductName
u'Learning Python (Help for Programmers)' u'Learning Python (Help for Programmers)'
>>> pythonBooks[0].URL >>> pythonBooks[0].URL
... ...
>>> pythonBooks[0].OurPrice >>> pythonBooks[0].OurPrice
... ...
Other available functions: Other available functions:
- browseBestSellers - browseBestSellers
- searchByASIN - searchByASIN
- searchByUPC - searchByUPC
- searchByAuthor - searchByAuthor
- searchByArtist - searchByArtist
- searchByActor - searchByActor
- searchByDirector - searchByDirector
- searchByManufacturer - searchByManufacturer
- searchByListMania - searchByListMania
- searchSimilar - searchSimilar
Other usage notes: Other usage notes:
- Most functions can take product_line as well, see source for possible values - Most functions can take product_line as well, see source for possible values
- All functions can take type="lite" to get less detail in results - All functions can take type="lite" to get less detail in results
- All functions can take page=N to get second, third, fourth page of results - All functions can take page=N to get second, third, fourth page of results
- All functions can take license_key="XYZ", instead of setting it globally - All functions can take license_key="XYZ", instead of setting it globally
- All functions can take http_proxy="http://x/y/z" which overrides your system setting - All functions can take http_proxy="http://x/y/z" which overrides your system setting
""" """
__author__ = "Mark Pilgrim (f8dy@diveintomark.org)" __author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
__version__ = "0.4" __version__ = "0.4"
__cvsversion__ = "$Revision$"[11:-2] __cvsversion__ = "$Revision$"[11:-2]
__date__ = "$Date$"[7:-2] __date__ = "$Date$"[7:-2]
__copyright__ = "Copyright (c) 2002 Mark Pilgrim" __copyright__ = "Copyright (c) 2002 Mark Pilgrim"
__license__ = "Python" __license__ = "Python"
from xml.dom import minidom from xml.dom import minidom
import os, sys, getopt, cgi, urllib import os, sys, getopt, cgi, urllib
try: try:
import timeoutsocket # http://www.timo-tasi.org/python/timeoutsocket.py import timeoutsocket # http://www.timo-tasi.org/python/timeoutsocket.py
timeoutsocket.setDefaultSocketTimeout(10) timeoutsocket.setDefaultSocketTimeout(10)
except ImportError: except ImportError:
pass pass
LICENSE_KEY = None LICENSE_KEY = None
HTTP_PROXY = None HTTP_PROXY = None
# don't touch the rest of these constants # don't touch the rest of these constants
class AmazonError(Exception): pass class AmazonError(Exception): pass
class NoLicenseKey(Exception): pass class NoLicenseKey(Exception): pass
_amazonfile1 = ".amazonkey" _amazonfile1 = ".amazonkey"
_amazonfile2 = "amazonkey.txt" _amazonfile2 = "amazonkey.txt"
_licenseLocations = ( _licenseLocations = (
(lambda key: key, 'passed to the function in license_key variable'), (lambda key: key, 'passed to the function in license_key variable'),
(lambda key: LICENSE_KEY, 'module-level LICENSE_KEY variable (call setLicense to set it)'), (lambda key: LICENSE_KEY, 'module-level LICENSE_KEY variable (call setLicense to set it)'),
(lambda key: os.environ.get('AMAZON_LICENSE_KEY', None), 'an environment variable called AMAZON_LICENSE_KEY'), (lambda key: os.environ.get('AMAZON_LICENSE_KEY', None), 'an environment variable called AMAZON_LICENSE_KEY'),
(lambda key: _contentsOf(os.getcwd(), _amazonfile1), '%s in the current directory' % _amazonfile1), (lambda key: _contentsOf(os.getcwd(), _amazonfile1), '%s in the current directory' % _amazonfile1),
(lambda key: _contentsOf(os.getcwd(), _amazonfile2), '%s in the current directory' % _amazonfile2), (lambda key: _contentsOf(os.getcwd(), _amazonfile2), '%s in the current directory' % _amazonfile2),
(lambda key: _contentsOf(os.environ.get('HOME', ''), _amazonfile1), '%s in your home directory' % _amazonfile1), (lambda key: _contentsOf(os.environ.get('HOME', ''), _amazonfile1), '%s in your home directory' % _amazonfile1),
(lambda key: _contentsOf(os.environ.get('HOME', ''), _amazonfile2), '%s in your home directory' % _amazonfile2), (lambda key: _contentsOf(os.environ.get('HOME', ''), _amazonfile2), '%s in your home directory' % _amazonfile2),
(lambda key: _contentsOf(_getScriptDir(), _amazonfile1), '%s in the amazon.py directory' % _amazonfile1), (lambda key: _contentsOf(_getScriptDir(), _amazonfile1), '%s in the amazon.py directory' % _amazonfile1),
(lambda key: _contentsOf(_getScriptDir(), _amazonfile2), '%s in the amazon.py directory' % _amazonfile2) (lambda key: _contentsOf(_getScriptDir(), _amazonfile2), '%s in the amazon.py directory' % _amazonfile2)
) )
## administrative functions ## administrative functions
def version(): def version():
print """PyAmazon %(__version__)s print """PyAmazon %(__version__)s
%(__copyright__)s %(__copyright__)s
released %(__date__)s released %(__date__)s
""" % globals() """ % globals()
## utility functions ## utility functions
def setLicense(license_key): def setLicense(license_key):
"""set license key""" """set license key"""
global LICENSE_KEY global LICENSE_KEY
LICENSE_KEY = license_key LICENSE_KEY = license_key
def getLicense(license_key = None): def getLicense(license_key = None):
"""get license key """get license key
license key can come from any number of locations; license key can come from any number of locations;
see module docs for search order""" see module docs for search order"""
for get, location in _licenseLocations: for get, location in _licenseLocations:
rc = get(license_key) rc = get(license_key)
if rc: return rc if rc: return rc
raise NoLicenseKey, 'get a license key at http://www.amazon.com/webservices' raise NoLicenseKey, 'get a license key at http://www.amazon.com/webservices'
def setProxy(http_proxy): def setProxy(http_proxy):
"""set HTTP proxy""" """set HTTP proxy"""
global HTTP_PROXY global HTTP_PROXY
HTTP_PROXY = http_proxy HTTP_PROXY = http_proxy
def getProxy(http_proxy = None): def getProxy(http_proxy = None):
"""get HTTP proxy""" """get HTTP proxy"""
return http_proxy or HTTP_PROXY return http_proxy or HTTP_PROXY
def getProxies(http_proxy = None): def getProxies(http_proxy = None):
http_proxy = getProxy(http_proxy) http_proxy = getProxy(http_proxy)
if http_proxy: if http_proxy:
proxies = {"http": http_proxy} proxies = {"http": http_proxy}
else: else:
proxies = None proxies = None
return proxies return proxies
def _contentsOf(dirname, filename): def _contentsOf(dirname, filename):
filename = os.path.join(dirname, filename) filename = os.path.join(dirname, filename)
if not os.path.exists(filename): return None if not os.path.exists(filename): return None
fsock = open(filename) fsock = open(filename)
contents = fsock.read() contents = fsock.read()
fsock.close() fsock.close()
return contents return contents
def _getScriptDir(): def _getScriptDir():
if __name__ == '__main__': if __name__ == '__main__':
return os.path.abspath(os.path.dirname(sys.argv[0])) return os.path.abspath(os.path.dirname(sys.argv[0]))
else: else:
return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__)) return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__))
class Bag: pass class Bag: pass
def unmarshal(element): def unmarshal(element):
rc = Bag() rc = Bag()
if isinstance(element, minidom.Element) and (element.tagName == 'Details'): if isinstance(element, minidom.Element) and (element.tagName == 'Details'):
rc.URL = element.attributes["url"].value rc.URL = element.attributes["url"].value
childElements = [e for e in element.childNodes if isinstance(e, minidom.Element)] childElements = [e for e in element.childNodes if isinstance(e, minidom.Element)]
if childElements: if childElements:
for child in childElements: for child in childElements:
key = child.tagName key = child.tagName
if hasattr(rc, key): if hasattr(rc, key):
if type(getattr(rc, key)) <> type([]): if type(getattr(rc, key)) <> type([]):
setattr(rc, key, [getattr(rc, key)]) setattr(rc, key, [getattr(rc, key)])
setattr(rc, key, getattr(rc, key) + [unmarshal(child)]) setattr(rc, key, getattr(rc, key) + [unmarshal(child)])
else: else:
setattr(rc, key, unmarshal(child)) setattr(rc, key, unmarshal(child))
else: else:
rc = "".join([e.data for e in element.childNodes if isinstance(e, minidom.Text)]) rc = "".join([e.data for e in element.childNodes if isinstance(e, minidom.Text)])
if element.tagName == 'SalesRank': if element.tagName == 'SalesRank':
rc = int(rc.replace(',', '')) rc = int(rc.replace(',', ''))
return rc return rc
def buildURL(search_type, keyword, product_line, type, page, license_key): def buildURL(search_type, keyword, product_line, type, page, license_key):
url = "http://xml.amazon.com/onca/xml?v=1.0&f=xml&t=webservices-20" url = "http://xml.amazon.com/onca/xml?v=1.0&f=xml&t=webservices-20"
url += "&dev-t=%s" % license_key.strip() url += "&dev-t=%s" % license_key.strip()
url += "&type=%s" % type url += "&type=%s" % type
if page: if page:
url += "&page=%s" % page url += "&page=%s" % page
if product_line: if product_line:
url += "&mode=%s" % product_line url += "&mode=%s" % product_line
url += "&%s=%s" % (search_type, urllib.quote(keyword)) url += "&%s=%s" % (search_type, urllib.quote(keyword))
# print url # print url
return url return url
## main functions ## main functions
def search(search_type, keyword, product_line, type="heavy", page=None, def search(search_type, keyword, product_line, type="heavy", page=None,
license_key = None, http_proxy = None): license_key = None, http_proxy = None):
"""search Amazon """search Amazon
You need a license key to call this function; see You need a license key to call this function; see
http://www.amazon.com/webservices http://www.amazon.com/webservices
to get one. Then you can either pass it to to get one. Then you can either pass it to
this function every time, or set it globally; see the module docs for details. this function every time, or set it globally; see the module docs for details.
Parameters: Parameters:
keyword - keyword to search keyword - keyword to search
search_type - in (KeywordSearch, BrowseNodeSearch, AsinSearch, UpcSearch, AuthorSearch, ArtistSearch, ActorSearch, DirectorSearch, ManufacturerSearch, ListManiaSearch, SimilaritySearch) search_type - in (KeywordSearch, BrowseNodeSearch, AsinSearch, UpcSearch, AuthorSearch, ArtistSearch, ActorSearch, DirectorSearch, ManufacturerSearch, ListManiaSearch, SimilaritySearch)
product_line - type of product to search for. restrictions based on search_type product_line - type of product to search for. restrictions based on search_type
UpcSearch - in (music, classical) UpcSearch - in (music, classical)
AuthorSearch - must be "books" AuthorSearch - must be "books"
ArtistSearch - in (music, classical) ArtistSearch - in (music, classical)
ActorSearch - in (dvd, vhs, video) ActorSearch - in (dvd, vhs, video)
DirectorSearch - in (dvd, vhs, video) DirectorSearch - in (dvd, vhs, video)
ManufacturerSearch - in (electronics, kitchen, videogames, software, photo, pc-hardware) ManufacturerSearch - in (electronics, kitchen, videogames, software, photo, pc-hardware)
http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages
Returns: list of Bags, each Bag may contain the following attributes: Returns: list of Bags, each Bag may contain the following attributes:
Asin - Amazon ID ("ASIN" number) of this item Asin - Amazon ID ("ASIN" number) of this item
Authors - list of authors Authors - list of authors
Availability - "available", etc. Availability - "available", etc.
BrowseList - list of related categories BrowseList - list of related categories
Catalog - catalog type ("Book", etc) Catalog - catalog type ("Book", etc)
CollectiblePrice - ?, format "$34.95" CollectiblePrice - ?, format "$34.95"
ImageUrlLarge - URL of large image of this item ImageUrlLarge - URL of large image of this item
ImageUrlMedium - URL of medium image of this item ImageUrlMedium - URL of medium image of this item
ImageUrlSmall - URL of small image of this item ImageUrlSmall - URL of small image of this item
Isbn - ISBN number Isbn - ISBN number
ListPrice - list price, format "$34.95" ListPrice - list price, format "$34.95"
Lists - list of ListMania lists that include this item Lists - list of ListMania lists that include this item
Manufacturer - manufacturer Manufacturer - manufacturer
Media - media ("Paperback", "Audio CD", etc) Media - media ("Paperback", "Audio CD", etc)
NumMedia - number of different media types in which this item is available NumMedia - number of different media types in which this item is available
OurPrice - Amazon price, format "$24.47" OurPrice - Amazon price, format "$24.47"
ProductName - name of this item ProductName - name of this item
ReleaseDate - release date, format "09 April, 1999" ReleaseDate - release date, format "09 April, 1999"
Reviews - reviews (AvgCustomerRating, plus list of CustomerReview with Rating, Summary, Content) Reviews - reviews (AvgCustomerRating, plus list of CustomerReview with Rating, Summary, Content)
SalesRank - sales rank (integer) SalesRank - sales rank (integer)
SimilarProducts - list of Product, which is ASIN number SimilarProducts - list of Product, which is ASIN number
ThirdPartyNewPrice - ?, format "$34.95" ThirdPartyNewPrice - ?, format "$34.95"
URL - URL of this item URL - URL of this item
""" """
license_key = getLicense(license_key) license_key = getLicense(license_key)
url = buildURL(search_type, keyword, product_line, type, page, license_key) url = buildURL(search_type, keyword, product_line, type, page, license_key)
proxies = getProxies(http_proxy) proxies = getProxies(http_proxy)
u = urllib.FancyURLopener(proxies) u = urllib.FancyURLopener(proxies)
usock = u.open(url) usock = u.open(url)
xmldoc = minidom.parse(usock) xmldoc = minidom.parse(usock)
usock.close() usock.close()
data = unmarshal(xmldoc).ProductInfo data = unmarshal(xmldoc).ProductInfo
if hasattr(data, 'ErrorMsg'): if hasattr(data, 'ErrorMsg'):
raise AmazonError, data.ErrorMsg raise AmazonError, data.ErrorMsg
else: else:
return data.Details return data.Details
def searchByKeyword(keyword, product_line="books", type="heavy", page=1, license_key=None, http_proxy=None): def searchByKeyword(keyword, product_line="books", type="heavy", page=1, license_key=None, http_proxy=None):
return search("KeywordSearch", keyword, product_line, type, page, license_key, http_proxy) return search("KeywordSearch", keyword, product_line, type, page, license_key, http_proxy)
def browseBestSellers(browse_node, product_line="books", type="heavy", page=1, license_key=None, http_proxy=None): def browseBestSellers(browse_node, product_line="books", type="heavy", page=1, license_key=None, http_proxy=None):
return search("BrowseNodeSearch", browse_node, product_line, type, page, license_key, http_proxy) return search("BrowseNodeSearch", browse_node, product_line, type, page, license_key, http_proxy)
def searchByASIN(ASIN, type="heavy", license_key=None, http_proxy=None): def searchByASIN(ASIN, type="heavy", license_key=None, http_proxy=None):
return search("AsinSearch", ASIN, None, type, None, license_key, http_proxy) return search("AsinSearch", ASIN, None, type, None, license_key, http_proxy)
def searchByUPC(UPC, type="heavy", license_key=None, http_proxy=None): def searchByUPC(UPC, type="heavy", license_key=None, http_proxy=None):
return search("UpcSearch", UPC, None, type, None, license_key, http_proxy) return search("UpcSearch", UPC, None, type, None, license_key, http_proxy)
def searchByAuthor(author, type="heavy", page=1, license_key=None, http_proxy=None): def searchByAuthor(author, type="heavy", page=1, license_key=None, http_proxy=None):
return search("AuthorSearch", author, "books", type, page, license_key, http_proxy) return search("AuthorSearch", author, "books", type, page, license_key, http_proxy)
def searchByArtist(artist, product_line="music", type="heavy", page=1, license_key=None, http_proxy=None): def searchByArtist(artist, product_line="music", type="heavy", page=1, license_key=None, http_proxy=None):
if product_line not in ("music", "classical"): if product_line not in ("music", "classical"):
raise AmazonError, "product_line must be in ('music', 'classical')" raise AmazonError, "product_line must be in ('music', 'classical')"
return search("ArtistSearch", artist, product_line, type, page, license_key, http_proxy) return search("ArtistSearch", artist, product_line, type, page, license_key, http_proxy)
def searchByActor(actor, product_line="dvd", type="heavy", page=1, license_key=None, http_proxy=None): def searchByActor(actor, product_line="dvd", type="heavy", page=1, license_key=None, http_proxy=None):
if product_line not in ("dvd", "vhs", "video"): if product_line not in ("dvd", "vhs", "video"):
raise AmazonError, "product_line must be in ('dvd', 'vhs', 'video')" raise AmazonError, "product_line must be in ('dvd', 'vhs', 'video')"
return search("ActorSearch", actor, product_line, type, page, license_key, http_proxy) return search("ActorSearch", actor, product_line, type, page, license_key, http_proxy)
def searchByDirector(director, product_line="dvd", type="heavy", page=1, license_key=None, http_proxy=None): def searchByDirector(director, product_line="dvd", type="heavy", page=1, license_key=None, http_proxy=None):
if product_line not in ("dvd", "vhs", "video"): if product_line not in ("dvd", "vhs", "video"):
raise AmazonError, "product_line must be in ('dvd', 'vhs', 'video')" raise AmazonError, "product_line must be in ('dvd', 'vhs', 'video')"
return search("DirectorSearch", director, product_line, type, page, license_key, http_proxy) return search("DirectorSearch", director, product_line, type, page, license_key, http_proxy)
def searchByManufacturer(manufacturer, product_line="pc-hardware", type="heavy", page=1, license_key=None, http_proxy=None): def searchByManufacturer(manufacturer, product_line="pc-hardware", type="heavy", page=1, license_key=None, http_proxy=None):
if product_line not in ("electronics", "kitchen", "videogames", "software", "photo", "pc-hardware"): if product_line not in ("electronics", "kitchen", "videogames", "software", "photo", "pc-hardware"):
raise AmazonError, "product_line must be in ('electronics', 'kitchen', 'videogames', 'software', 'photo', 'pc-hardware')" raise AmazonError, "product_line must be in ('electronics', 'kitchen', 'videogames', 'software', 'photo', 'pc-hardware')"
return search("ManufacturerSearch", manufacturer, product_line, type, page, license_key, http_proxy) return search("ManufacturerSearch", manufacturer, product_line, type, page, license_key, http_proxy)
def searchByListMania(listManiaID, type="heavy", page=1, license_key=None, http_proxy=None): def searchByListMania(listManiaID, type="heavy", page=1, license_key=None, http_proxy=None):
return search("ListManiaSearch", listManiaID, None, type, page, license_key, http_proxy) return search("ListManiaSearch", listManiaID, None, type, page, license_key, http_proxy)
def searchSimilar(ASIN, type="heavy", page=1, license_key=None, http_proxy=None): def searchSimilar(ASIN, type="heavy", page=1, license_key=None, http_proxy=None):
return search("SimilaritySearch", ASIN, None, type, page, license_key, http_proxy) return search("SimilaritySearch", ASIN, None, type, page, license_key, http_proxy)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -1,433 +1,433 @@
"""Python wrapper for Google web APIs """Python wrapper for Google web APIs
This module allows you to access Google's web APIs through SOAP, This module allows you to access Google's web APIs through SOAP,
to do things like search Google and get the results programmatically. to do things like search Google and get the results programmatically.
Described here: Described here:
http://www.google.com/apis/ http://www.google.com/apis/
You need a Google-provided license key to use these services. You need a Google-provided license key to use these services.
Follow the link above to get one. These functions will look in Follow the link above to get one. These functions will look in
several places (in this order) for the license key: several places (in this order) for the license key:
- the "license_key" argument of each function - the "license_key" argument of each function
- the module-level LICENSE_KEY variable (call setLicense once to set it) - the module-level LICENSE_KEY variable (call setLicense once to set it)
- an environment variable called GOOGLE_LICENSE_KEY - an environment variable called GOOGLE_LICENSE_KEY
- a file called ".googlekey" in the current directory - a file called ".googlekey" in the current directory
- a file called "googlekey.txt" in the current directory - a file called "googlekey.txt" in the current directory
- a file called ".googlekey" in your home directory - a file called ".googlekey" in your home directory
- a file called "googlekey.txt" in your home directory - a file called "googlekey.txt" in your home directory
- a file called ".googlekey" in the same directory as google.py - a file called ".googlekey" in the same directory as google.py
- a file called "googlekey.txt" in the same directory as google.py - a file called "googlekey.txt" in the same directory as google.py
Sample usage: Sample usage:
>>> import google >>> import google
>>> google.setLicense('...') # must get your own key! >>> google.setLicense('...') # must get your own key!
>>> data = google.doGoogleSearch('python') >>> data = google.doGoogleSearch('python')
>>> data.meta.searchTime >>> data.meta.searchTime
0.043221000000000002 0.043221000000000002
>>> data.results[0].URL >>> data.results[0].URL
'http://www.python.org/' 'http://www.python.org/'
>>> data.results[0].title >>> data.results[0].title
'<b>Python</b> Language Website' '<b>Python</b> Language Website'
See documentation of SearchResultsMetaData and SearchResult classes See documentation of SearchResultsMetaData and SearchResult classes
for other available attributes. for other available attributes.
""" """
__author__ = "Mark Pilgrim (f8dy@diveintomark.org)" __author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
__version__ = "0.5.2" __version__ = "0.5.2"
__cvsversion__ = "$Revision$"[11:-2] __cvsversion__ = "$Revision$"[11:-2]
__date__ = "$Date$"[7:-2] __date__ = "$Date$"[7:-2]
__copyright__ = "Copyright (c) 2002 Mark Pilgrim" __copyright__ = "Copyright (c) 2002 Mark Pilgrim"
__license__ = "Python" __license__ = "Python"
__credits__ = """David Ascher, for the install script __credits__ = """David Ascher, for the install script
Erik Max Francis, for the command line interface Erik Max Francis, for the command line interface
Michael Twomey, for HTTP proxy support""" Michael Twomey, for HTTP proxy support"""
import SOAP import SOAP
import os, sys, getopt import os, sys, getopt
LICENSE_KEY = None LICENSE_KEY = None
HTTP_PROXY = None HTTP_PROXY = None
# don't touch the rest of these constants # don't touch the rest of these constants
class NoLicenseKey(Exception): pass class NoLicenseKey(Exception): pass
_url = 'http://api.google.com/search/beta2' _url = 'http://api.google.com/search/beta2'
_namespace = 'urn:GoogleSearch' _namespace = 'urn:GoogleSearch'
_false = SOAP.booleanType(0) _false = SOAP.booleanType(0)
_true = SOAP.booleanType(1) _true = SOAP.booleanType(1)
_googlefile1 = ".googlekey" _googlefile1 = ".googlekey"
_googlefile2 = "googlekey.txt" _googlefile2 = "googlekey.txt"
_licenseLocations = ( _licenseLocations = (
(lambda key: key, 'passed to the function in license_key variable'), (lambda key: key, 'passed to the function in license_key variable'),
(lambda key: LICENSE_KEY, 'module-level LICENSE_KEY variable (call setLicense to set it)'), (lambda key: LICENSE_KEY, 'module-level LICENSE_KEY variable (call setLicense to set it)'),
(lambda key: os.environ.get('GOOGLE_LICENSE_KEY', None), 'an environment variable called GOOGLE_LICENSE_KEY'), (lambda key: os.environ.get('GOOGLE_LICENSE_KEY', None), 'an environment variable called GOOGLE_LICENSE_KEY'),
(lambda key: _contentsOf(os.getcwd(), _googlefile1), '%s in the current directory' % _googlefile1), (lambda key: _contentsOf(os.getcwd(), _googlefile1), '%s in the current directory' % _googlefile1),
(lambda key: _contentsOf(os.getcwd(), _googlefile2), '%s in the current directory' % _googlefile2), (lambda key: _contentsOf(os.getcwd(), _googlefile2), '%s in the current directory' % _googlefile2),
(lambda key: _contentsOf(os.environ.get('HOME', ''), _googlefile1), '%s in your home directory' % _googlefile1), (lambda key: _contentsOf(os.environ.get('HOME', ''), _googlefile1), '%s in your home directory' % _googlefile1),
(lambda key: _contentsOf(os.environ.get('HOME', ''), _googlefile2), '%s in your home directory' % _googlefile2), (lambda key: _contentsOf(os.environ.get('HOME', ''), _googlefile2), '%s in your home directory' % _googlefile2),
(lambda key: _contentsOf(_getScriptDir(), _googlefile1), '%s in the google.py directory' % _googlefile1), (lambda key: _contentsOf(_getScriptDir(), _googlefile1), '%s in the google.py directory' % _googlefile1),
(lambda key: _contentsOf(_getScriptDir(), _googlefile2), '%s in the google.py directory' % _googlefile2) (lambda key: _contentsOf(_getScriptDir(), _googlefile2), '%s in the google.py directory' % _googlefile2)
) )
## administrative functions ## administrative functions
def version(): def version():
print """PyGoogle %(__version__)s print """PyGoogle %(__version__)s
%(__copyright__)s %(__copyright__)s
released %(__date__)s released %(__date__)s
Thanks to: Thanks to:
%(__credits__)s""" % globals() %(__credits__)s""" % globals()
def usage(): def usage():
program = os.path.basename(sys.argv[0]) program = os.path.basename(sys.argv[0])
print """Usage: %(program)s [options] [querytype] query print """Usage: %(program)s [options] [querytype] query
options: options:
-k, --key= <license key> Google license key (see important note below) -k, --key= <license key> Google license key (see important note below)
-1, -l, --lucky show only first hit -1, -l, --lucky show only first hit
-m, --meta show meta information -m, --meta show meta information
-r, --reverse show results in reverse order -r, --reverse show results in reverse order
-x, --proxy= <url> use HTTP proxy -x, --proxy= <url> use HTTP proxy
-h, --help print this help -h, --help print this help
-v, --version print version and copyright information -v, --version print version and copyright information
-t, --test run test queries -t, --test run test queries
querytype: querytype:
-s, --search= <query> search (default) -s, --search= <query> search (default)
-c, --cache= <url> retrieve cached page -c, --cache= <url> retrieve cached page
-p, --spelling= <word> check spelling -p, --spelling= <word> check spelling
IMPORTANT NOTE: all Google functions require a valid license key; IMPORTANT NOTE: all Google functions require a valid license key;
visit http://www.google.com/apis/ to get one. %(program)s will look in visit http://www.google.com/apis/ to get one. %(program)s will look in
these places (in order) and use the first license key it finds: these places (in order) and use the first license key it finds:
* the key specified on the command line""" % vars() * the key specified on the command line""" % vars()
for get, location in _licenseLocations[2:]: for get, location in _licenseLocations[2:]:
print " *", location print " *", location
## utility functions ## utility functions
def setLicense(license_key): def setLicense(license_key):
"""set license key""" """set license key"""
global LICENSE_KEY global LICENSE_KEY
LICENSE_KEY = license_key LICENSE_KEY = license_key
def getLicense(license_key = None): def getLicense(license_key = None):
"""get license key """get license key
license key can come from any number of locations; license key can come from any number of locations;
see module docs for search order""" see module docs for search order"""
for get, location in _licenseLocations: for get, location in _licenseLocations:
rc = get(license_key) rc = get(license_key)
if rc: return rc if rc: return rc
usage() usage()
raise NoLicenseKey, 'get a license key at http://www.google.com/apis/' raise NoLicenseKey, 'get a license key at http://www.google.com/apis/'
def setProxy(http_proxy): def setProxy(http_proxy):
"""set HTTP proxy""" """set HTTP proxy"""
global HTTP_PROXY global HTTP_PROXY
HTTP_PROXY = http_proxy HTTP_PROXY = http_proxy
def getProxy(http_proxy = None): def getProxy(http_proxy = None):
"""get HTTP proxy""" """get HTTP proxy"""
return http_proxy or HTTP_PROXY return http_proxy or HTTP_PROXY
def _contentsOf(dirname, filename): def _contentsOf(dirname, filename):
filename = os.path.join(dirname, filename) filename = os.path.join(dirname, filename)
if not os.path.exists(filename): return None if not os.path.exists(filename): return None
fsock = open(filename) fsock = open(filename)
contents = fsock.read() contents = fsock.read()
fsock.close() fsock.close()
return contents return contents
def _getScriptDir(): def _getScriptDir():
if __name__ == '__main__': if __name__ == '__main__':
return os.path.abspath(os.path.dirname(sys.argv[0])) return os.path.abspath(os.path.dirname(sys.argv[0]))
else: else:
return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__)) return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__))
def _marshalBoolean(value): def _marshalBoolean(value):
if value: if value:
return _true return _true
else: else:
return _false return _false
## output formatters ## output formatters
def makeFormatter(outputFormat): def makeFormatter(outputFormat):
classname = "%sOutputFormatter" % outputFormat.capitalize() classname = "%sOutputFormatter" % outputFormat.capitalize()
return globals()[classname]() return globals()[classname]()
def output(results, params): def output(results, params):
formatter = makeFormatter(params.get("outputFormat", "text")) formatter = makeFormatter(params.get("outputFormat", "text"))
outputmethod = getattr(formatter, params["func"]) outputmethod = getattr(formatter, params["func"])
outputmethod(results, params) outputmethod(results, params)
class OutputFormatter: class OutputFormatter:
def boil(self, data): def boil(self, data):
if type(data) == type(u""): if type(data) == type(u""):
return data.encode("ISO-8859-1", "replace") return data.encode("ISO-8859-1", "replace")
else: else:
return data return data
class TextOutputFormatter(OutputFormatter): class TextOutputFormatter(OutputFormatter):
def common(self, data, params): def common(self, data, params):
if params.get("showMeta", 0): if params.get("showMeta", 0):
meta = data.meta meta = data.meta
for category in meta.directoryCategories: for category in meta.directoryCategories:
print "directoryCategory: %s" % self.boil(category["fullViewableName"]) print "directoryCategory: %s" % self.boil(category["fullViewableName"])
for attr in [node for node in dir(meta) if node <> "directoryCategories" and node[:2] <> '__']: for attr in [node for node in dir(meta) if node <> "directoryCategories" and node[:2] <> '__']:
print "%s:" % attr, self.boil(getattr(meta, attr)) print "%s:" % attr, self.boil(getattr(meta, attr))
def doGoogleSearch(self, data, params): def doGoogleSearch(self, data, params):
results = data.results results = data.results
if params.get("feelingLucky", 0): if params.get("feelingLucky", 0):
results = results[:1] results = results[:1]
if params.get("reverseOrder", 0): if params.get("reverseOrder", 0):
results.reverse() results.reverse()
for result in results: for result in results:
for attr in dir(result): for attr in dir(result):
if attr == "directoryCategory": if attr == "directoryCategory":
print "directoryCategory:", self.boil(result.directoryCategory["fullViewableName"]) print "directoryCategory:", self.boil(result.directoryCategory["fullViewableName"])
elif attr[:2] <> '__': elif attr[:2] <> '__':
print "%s:" % attr, self.boil(getattr(result, attr)) print "%s:" % attr, self.boil(getattr(result, attr))
print print
self.common(data, params) self.common(data, params)
def doGetCachedPage(self, data, params): def doGetCachedPage(self, data, params):
print data print data
self.common(data, params) self.common(data, params)
doSpellingSuggestion = doGetCachedPage doSpellingSuggestion = doGetCachedPage
## search results classes ## search results classes
class _SearchBase: class _SearchBase:
def __init__(self, params): def __init__(self, params):
for k, v in params.items(): for k, v in params.items():
if isinstance(v, SOAP.structType): if isinstance(v, SOAP.structType):
v = v._asdict v = v._asdict
try: try:
if isinstance(v[0], SOAP.structType): if isinstance(v[0], SOAP.structType):
v = [node._asdict for node in v] v = [node._asdict for node in v]
except: except:
pass pass
self.__dict__[str(k)] = v self.__dict__[str(k)] = v
class SearchResultsMetaData(_SearchBase): class SearchResultsMetaData(_SearchBase):
"""metadata of search query results """metadata of search query results
Available attributes: Available attributes:
documentFiltering - flag indicates whether duplicate page filtering was perfomed in this search documentFiltering - flag indicates whether duplicate page filtering was perfomed in this search
searchComments - human-readable informational message (example: "'the' is a very common word searchComments - human-readable informational message (example: "'the' is a very common word
and was not included in your search") and was not included in your search")
estimatedTotalResultsCount - estimated total number of results for this query estimatedTotalResultsCount - estimated total number of results for this query
estimateIsExact - flag indicates whether estimatedTotalResultsCount is an exact value estimateIsExact - flag indicates whether estimatedTotalResultsCount is an exact value
searchQuery - search string that initiated this search searchQuery - search string that initiated this search
startIndex - index of first result returned (zero-based) startIndex - index of first result returned (zero-based)
endIndex - index of last result returned (zero-based) endIndex - index of last result returned (zero-based)
searchTips - human-readable informational message on how to use Google bette searchTips - human-readable informational message on how to use Google bette
directoryCategories - list of dictionaries like this: directoryCategories - list of dictionaries like this:
{'fullViewableName': Open Directory category, {'fullViewableName': Open Directory category,
'specialEncoding': encoding scheme of this directory category} 'specialEncoding': encoding scheme of this directory category}
searchTime - total search time, in seconds searchTime - total search time, in seconds
""" """
pass pass
class SearchResult(_SearchBase): class SearchResult(_SearchBase):
"""search result """search result
Available attributes: Available attributes:
URL - URL URL - URL
title - title (HTML) title - title (HTML)
snippet - snippet showing query context (HTML) snippet - snippet showing query context (HTML)
cachedSize - size of cached version of this result, (KB) cachedSize - size of cached version of this result, (KB)
relatedInformationPresent - flag indicates that the "related:" keyword is supported for this URL relatedInformationPresent - flag indicates that the "related:" keyword is supported for this URL
hostName: When filtering occurs, a maximum of two results from any given host is returned. hostName: When filtering occurs, a maximum of two results from any given host is returned.
When this occurs, the second resultElement that comes from that host contains When this occurs, the second resultElement that comes from that host contains
the host name in this parameter. the host name in this parameter.
directoryCategory: dictionary like this: directoryCategory: dictionary like this:
{'fullViewableName': Open Directory category, {'fullViewableName': Open Directory category,
'specialEncoding': encoding scheme of this directory category} 'specialEncoding': encoding scheme of this directory category}
directoryTitle: Open Directory title of this result (or blank) directoryTitle: Open Directory title of this result (or blank)
summary - Open Directory summary for this result (or blank) summary - Open Directory summary for this result (or blank)
""" """
pass pass
class SearchReturnValue: class SearchReturnValue:
"""complete search results for a single query """complete search results for a single query
Available attributes: Available attributes:
meta - SearchResultsMetaData meta - SearchResultsMetaData
results - list of SearchResult results - list of SearchResult
""" """
def __init__(self, metadata, results): def __init__(self, metadata, results):
self.meta = metadata self.meta = metadata
self.results = results self.results = results
## main functions ## main functions
def doGoogleSearch(q, start=0, maxResults=10, filter=1, restrict='', def doGoogleSearch(q, start=0, maxResults=10, filter=1, restrict='',
safeSearch=0, language='', inputencoding='', outputencoding='', safeSearch=0, language='', inputencoding='', outputencoding='',
license_key = None, http_proxy = None): license_key = None, http_proxy = None):
"""search Google """search Google
You need a license key to call this function; see You need a license key to call this function; see
http://www.google.com/apis/ to get one. Then you can either pass it to http://www.google.com/apis/ to get one. Then you can either pass it to
this function every time, or set it globally; see the module docs for details. this function every time, or set it globally; see the module docs for details.
Parameters: Parameters:
q - search string. Anything you could type at google.com, you can pass here. q - search string. Anything you could type at google.com, you can pass here.
See http://www.google.com/help/features.html for examples of advanced features. See http://www.google.com/help/features.html for examples of advanced features.
start (optional) - zero-based index of first desired result (for paging through start (optional) - zero-based index of first desired result (for paging through
multiple pages of results) multiple pages of results)
maxResults (optional) - maximum number of results, currently capped at 10 maxResults (optional) - maximum number of results, currently capped at 10
filter (optional) - set to 1 to filter out similar results, set to 0 to see everything filter (optional) - set to 1 to filter out similar results, set to 0 to see everything
restrict (optional) - restrict results by country or topic. Examples: restrict (optional) - restrict results by country or topic. Examples:
Ukraine - search only sites located in Ukraine Ukraine - search only sites located in Ukraine
linux - search Linux sites only linux - search Linux sites only
mac - search Mac sites only mac - search Mac sites only
bsd - search FreeBSD sites only bsd - search FreeBSD sites only
See the APIs_reference.html file in the SDK (http://www.google.com/apis/download.html) See the APIs_reference.html file in the SDK (http://www.google.com/apis/download.html)
for more advanced examples and a full list of country codes and topics. for more advanced examples and a full list of country codes and topics.
safeSearch (optional) - set to 1 to filter results with SafeSearch (no adult material) safeSearch (optional) - set to 1 to filter results with SafeSearch (no adult material)
language (optional) - restricts search to documents in one or more languages. Example: language (optional) - restricts search to documents in one or more languages. Example:
lang_en - only return pages in English lang_en - only return pages in English
lang_fr - only return pages in French lang_fr - only return pages in French
See the APIs_reference.html file in the SDK (http://www.google.com/apis/download.html) See the APIs_reference.html file in the SDK (http://www.google.com/apis/download.html)
for more advanced examples and a full list of language codes. for more advanced examples and a full list of language codes.
inputencoding (optional) - sets the character encoding of q parameter inputencoding (optional) - sets the character encoding of q parameter
outputencoding (optional) - sets the character encoding of the returned results outputencoding (optional) - sets the character encoding of the returned results
See the APIs_reference.html file in the SDK (http://www.google.com/apis/download.html) See the APIs_reference.html file in the SDK (http://www.google.com/apis/download.html)
for a full list of encodings. for a full list of encodings.
http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages
Returns: SearchReturnValue Returns: SearchReturnValue
.meta - SearchMetaData .meta - SearchMetaData
.results - list of SearchResult .results - list of SearchResult
See documentation of these individual classes for list of available attributes See documentation of these individual classes for list of available attributes
""" """
http_proxy = getProxy(http_proxy) http_proxy = getProxy(http_proxy)
remoteserver = SOAP.SOAPProxy(_url, namespace=_namespace, http_proxy=http_proxy) remoteserver = SOAP.SOAPProxy(_url, namespace=_namespace, http_proxy=http_proxy)
license_key = getLicense(license_key) license_key = getLicense(license_key)
filter = _marshalBoolean(filter) filter = _marshalBoolean(filter)
safeSearch = _marshalBoolean(safeSearch) safeSearch = _marshalBoolean(safeSearch)
data = remoteserver.doGoogleSearch(license_key, q, start, maxResults, filter, restrict, data = remoteserver.doGoogleSearch(license_key, q, start, maxResults, filter, restrict,
safeSearch, language, inputencoding, outputencoding) safeSearch, language, inputencoding, outputencoding)
metadata = data._asdict metadata = data._asdict
del metadata["resultElements"] del metadata["resultElements"]
metadata = SearchResultsMetaData(metadata) metadata = SearchResultsMetaData(metadata)
results = [SearchResult(node._asdict) for node in data.resultElements] results = [SearchResult(node._asdict) for node in data.resultElements]
return SearchReturnValue(metadata, results) return SearchReturnValue(metadata, results)
def doGetCachedPage(url, license_key = None, http_proxy = None): def doGetCachedPage(url, license_key = None, http_proxy = None):
"""get page from Google cache """get page from Google cache
You need a license key to call this function; see You need a license key to call this function; see
http://www.google.com/apis/ to get one. Then you can either pass it to http://www.google.com/apis/ to get one. Then you can either pass it to
this function every time, or set it globally; see the module docs for details. this function every time, or set it globally; see the module docs for details.
Parameters: Parameters:
url - address of page to get url - address of page to get
license_key (optional) - Google license key license_key (optional) - Google license key
http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages
Returns: string, text of cached page Returns: string, text of cached page
""" """
http_proxy = getProxy(http_proxy) http_proxy = getProxy(http_proxy)
remoteserver = SOAP.SOAPProxy(_url, namespace=_namespace, http_proxy=http_proxy) remoteserver = SOAP.SOAPProxy(_url, namespace=_namespace, http_proxy=http_proxy)
license_key = getLicense(license_key) license_key = getLicense(license_key)
return remoteserver.doGetCachedPage(license_key, url) return remoteserver.doGetCachedPage(license_key, url)
def doSpellingSuggestion(phrase, license_key = None, http_proxy = None): def doSpellingSuggestion(phrase, license_key = None, http_proxy = None):
"""get spelling suggestions from Google """get spelling suggestions from Google
You need a license key to call this function; see You need a license key to call this function; see
http://www.google.com/apis/ to get one. Then you can either pass it to http://www.google.com/apis/ to get one. Then you can either pass it to
this function every time, or set it globally; see the module docs for details. this function every time, or set it globally; see the module docs for details.
Parameters: Parameters:
phrase - word or phrase to spell-check phrase - word or phrase to spell-check
http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages http_proxy (optional) - address of HTTP proxy to use for sending and receiving SOAP messages
Returns: text of suggested replacement, or None Returns: text of suggested replacement, or None
""" """
http_proxy = getProxy(http_proxy) http_proxy = getProxy(http_proxy)
remoteserver = SOAP.SOAPProxy(_url, namespace=_namespace, http_proxy=http_proxy) remoteserver = SOAP.SOAPProxy(_url, namespace=_namespace, http_proxy=http_proxy)
license_key = getLicense(license_key) license_key = getLicense(license_key)
return remoteserver.doSpellingSuggestion(license_key, phrase) return remoteserver.doSpellingSuggestion(license_key, phrase)
## functional test suite (see googletest.py for unit test suite) ## functional test suite (see googletest.py for unit test suite)
def test(): def test():
try: try:
getLicense(None) getLicense(None)
except NoLicenseKey: except NoLicenseKey:
return return
print "Searching for Python at google.com..." print "Searching for Python at google.com..."
data = doGoogleSearch("Python") data = doGoogleSearch("Python")
output(data, {"func": "doGoogleSearch"}) output(data, {"func": "doGoogleSearch"})
print "\nSearching for 5 _French_ pages about Python, encoded in ISO-8859-1..." print "\nSearching for 5 _French_ pages about Python, encoded in ISO-8859-1..."
data = doGoogleSearch("Python", language='lang_fr', outputencoding='ISO-8859-1', maxResults=5) data = doGoogleSearch("Python", language='lang_fr', outputencoding='ISO-8859-1', maxResults=5)
output(data, {"func": "doGoogleSearch"}) output(data, {"func": "doGoogleSearch"})
phrase = "Pyhton programming languager" phrase = "Pyhton programming languager"
print "\nTesting spelling suggetions for '%s'..." % phrase print "\nTesting spelling suggetions for '%s'..." % phrase
data = doSpellingSuggestion(phrase) data = doSpellingSuggestion(phrase)
output(data, {"func": "doSpellingSuggestion"}) output(data, {"func": "doSpellingSuggestion"})
## main driver for command-line use ## main driver for command-line use
def main(argv): def main(argv):
if not argv: if not argv:
usage() usage()
return return
q = None q = None
func = None func = None
http_proxy = None http_proxy = None
license_key = None license_key = None
feelingLucky = 0 feelingLucky = 0
showMeta = 0 showMeta = 0
reverseOrder = 0 reverseOrder = 0
runTest = 0 runTest = 0
outputFormat = "text" outputFormat = "text"
try: try:
opts, args = getopt.getopt(argv, "s:c:p:k:lmrx:hvt1", opts, args = getopt.getopt(argv, "s:c:p:k:lmrx:hvt1",
["search=", "cache=", "spelling=", "key=", "lucky", "meta", "reverse", "proxy=", "help", "version", "test"]) ["search=", "cache=", "spelling=", "key=", "lucky", "meta", "reverse", "proxy=", "help", "version", "test"])
except getopt.GetoptError: except getopt.GetoptError:
usage() usage()
sys.exit(2) sys.exit(2)
for opt, arg in opts: for opt, arg in opts:
if opt in ("-s", "--search"): if opt in ("-s", "--search"):
q = arg q = arg
func = "doGoogleSearch" func = "doGoogleSearch"
elif opt in ("-c", "--cache"): elif opt in ("-c", "--cache"):
q = arg q = arg
func = "doGetCachedPage" func = "doGetCachedPage"
elif opt in ("-p", "--spelling"): elif opt in ("-p", "--spelling"):
q = arg q = arg
func = "doSpellingSuggestion" func = "doSpellingSuggestion"
elif opt in ("-k", "--key"): elif opt in ("-k", "--key"):
license_key = arg license_key = arg
elif opt in ("-l", "-1", "--lucky"): elif opt in ("-l", "-1", "--lucky"):
feelingLucky = 1 feelingLucky = 1
elif opt in ("-m", "--meta"): elif opt in ("-m", "--meta"):
showMeta = 1 showMeta = 1
elif opt in ("-r", "--reverse"): elif opt in ("-r", "--reverse"):
reverseOrder = 1 reverseOrder = 1
elif opt in ("-x", "--proxy"): elif opt in ("-x", "--proxy"):
http_proxy = arg http_proxy = arg
elif opt in ("-h", "--help"): elif opt in ("-h", "--help"):
usage() usage()
elif opt in ("-v", "--version"): elif opt in ("-v", "--version"):
version() version()
elif opt in ("-t", "--test"): elif opt in ("-t", "--test"):
runTest = 1 runTest = 1
if runTest: if runTest:
setLicense(license_key) setLicense(license_key)
setProxy(http_proxy) setProxy(http_proxy)
test() test()
if args and not q: if args and not q:
q = args[0] q = args[0]
func = "doGoogleSearch" func = "doGoogleSearch"
if func: if func:
results = globals()[func](q, http_proxy=http_proxy, license_key=license_key) results = globals()[func](q, http_proxy=http_proxy, license_key=license_key)
output(results, locals()) output(results, locals())
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv[1:]) main(sys.argv[1:])
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -387,7 +387,7 @@ class Message:
def __contains__(self, name): def __contains__(self, name):
return name.lower() in self.dict return name.lower() in self.dict
def __getitem__(self, name): def __getitem__(self, name):
"""Get a specific header, as from a dictionary.""" """Get a specific header, as from a dictionary."""
return self.dict[name.lower()] return self.dict[name.lower()]

File diff suppressed because it is too large Load Diff

View File

@ -263,7 +263,7 @@ class TestCase:
def _fail(self, msg): def _fail(self, msg):
"""Underlying implementation of failure.""" """Underlying implementation of failure."""
raise self.failureException, msg raise self.failureException, msg
def fail(self, msg=None): def fail(self, msg=None):
"""Fail immediately, with the given message.""" """Fail immediately, with the given message."""
global asserts global asserts

View File

@ -66,7 +66,7 @@ def findBiggestDollar(alias):
return int(dollars[-1]) return int(dollars[-1])
else: else:
return None return None
def makeNewAlias(name, alias): def makeNewAlias(name, alias):
if findAliasCommand(name, alias): if findAliasCommand(name, alias):
raise RecursiveAlias raise RecursiveAlias
@ -105,7 +105,7 @@ class Alias(callbacks.Privmsg):
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
self.frozen = sets.Set() self.frozen = sets.Set()
def freeze(self, irc, msg, args): def freeze(self, irc, msg, args):
"""<alias> """<alias>
@ -133,7 +133,7 @@ class Alias(callbacks.Privmsg):
else: else:
irc.error(msg, 'There is no such alias.') irc.error(msg, 'There is no such alias.')
unfreeze = privmsgs.checkCapability(unfreeze, 'admin') unfreeze = privmsgs.checkCapability(unfreeze, 'admin')
def alias(self, irc, msg, args): def alias(self, irc, msg, args):
"""<name> <alias commands> """<name> <alias commands>
@ -164,7 +164,7 @@ class Alias(callbacks.Privmsg):
print debug.exnToString(e) print debug.exnToString(e)
except: except:
print 'exception raised' print 'exception raised'
def unalias(self, irc, msg, args): def unalias(self, irc, msg, args):
"""<name> """<name>
@ -181,8 +181,8 @@ class Alias(callbacks.Privmsg):
irc.error(msg, 'That alias is frozen.') irc.error(msg, 'That alias is frozen.')
else: else:
irc.error(msg, 'There is no such alias.') irc.error(msg, 'There is no such alias.')
Class = Alias Class = Alias

View File

@ -111,7 +111,7 @@ class ChannelDB(callbacks.PrivmsgCommandAndRegexp, ChannelDBHandler):
)""") )""")
db.commit() db.commit()
return db return db
def doPrivmsg(self, irc, msg): def doPrivmsg(self, irc, msg):
callbacks.PrivmsgCommandAndRegexp.doPrivmsg(self, irc, msg) callbacks.PrivmsgCommandAndRegexp.doPrivmsg(self, irc, msg)
if ircutils.isChannel(msg.args[0]): if ircutils.isChannel(msg.args[0]):
@ -174,7 +174,7 @@ class ChannelDB(callbacks.PrivmsgCommandAndRegexp, ChannelDBHandler):
except KeyError: except KeyError:
pass pass
db.commit() db.commit()
def doPart(self, irc, msg): def doPart(self, irc, msg):
channel = msg.args[0] channel = msg.args[0]
db = self.getDb(channel) db = self.getDb(channel)

View File

@ -30,7 +30,7 @@
### ###
""" """
Handles standard CTCP responses to PING, TIME, SOURCE, VERSION, USERINFO, Handles standard CTCP responses to PING, TIME, SOURCE, VERSION, USERINFO,
and FINGER. and FINGER.
""" """

View File

@ -30,7 +30,7 @@
### ###
""" """
Handles "factoids," little tidbits of information held in a database and Handles "factoids," little tidbits of information held in a database and
available on demand via several commands. available on demand via several commands.
Commands include: Commands include:
@ -59,7 +59,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
def __init__(self): def __init__(self):
ChannelDBHandler.__init__(self) ChannelDBHandler.__init__(self)
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
def makeDb(self, filename): def makeDb(self, filename):
if os.path.exists(filename): if os.path.exists(filename):
return sqlite.connect(filename) return sqlite.connect(filename)
@ -120,7 +120,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
else: else:
irc.error(msg, 'That factoid is locked.') irc.error(msg, 'That factoid is locked.')
def lookup(self, irc, msg, args): def lookup(self, irc, msg, args):
"[<channel>] (If not sent in the channel itself) <key> [<number>]" "[<channel>] (If not sent in the channel itself) <key> [<number>]"
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
@ -141,7 +141,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
else: else:
factoid = results[number][0] factoid = results[number][0]
irc.reply(msg, '%s/%s: %s' % (key, number, factoid)) irc.reply(msg, '%s/%s: %s' % (key, number, factoid))
def lock(self, irc, msg, args): def lock(self, irc, msg, args):
"[<channel>] (If not sent in the channel itself) <key>" "[<channel>] (If not sent in the channel itself) <key>"
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
@ -155,7 +155,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
else: else:
irc.error(msg, conf.replyNoCapability % capability) irc.error(msg, conf.replyNoCapability % capability)
def unlock(self, irc, msg, args): def unlock(self, irc, msg, args):
"[<channel>] (If not sent in the channel itself) <key>" "[<channel>] (If not sent in the channel itself) <key>"
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
@ -183,7 +183,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
else: else:
irc.error(msg, conf.replyNoCapability % capability) irc.error(msg, conf.replyNoCapability % capability)
def randomfactoid(self, irc, msg, args): def randomfactoid(self, irc, msg, args):
"[<channel>] (If not sent in the channel itself)" "[<channel>] (If not sent in the channel itself)"
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
@ -226,7 +226,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
s = 'Key %r is %s and has %s factoids associated with it: %s' % \ s = 'Key %r is %s and has %s factoids associated with it: %s' % \
(key, locked and 'locked' or 'not locked', counter, '; '.join(L)) (key, locked and 'locked' or 'not locked', counter, '; '.join(L))
irc.reply(msg, s) irc.reply(msg, s)
Class = Factoids Class = Factoids

View File

@ -204,8 +204,8 @@ class FreeBSD(callbacks.Privmsg):
'Please narrow your search.' % cursor.rowcount) 'Please narrow your search.' % cursor.rowcount)
else: else:
irc.reply(msg, ', '.join(names)) irc.reply(msg, ', '.join(names))
def numports(self, irc, msg, args): def numports(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -249,10 +249,10 @@ class FreeBSD(callbacks.Privmsg):
categories = map(lambda t: t[0], cursor.fetchall()) categories = map(lambda t: t[0], cursor.fetchall())
irc.reply(msg, '%s; Categories: %s; Maintainer: %s; Website: %s' % irc.reply(msg, '%s; Categories: %s; Maintainer: %s; Website: %s' %
(info, ', '.join(categories), maintainer, website)) (info, ', '.join(categories), maintainer, website))
Class = FreeBSD Class = FreeBSD
if __name__ == '__main__': if __name__ == '__main__':
makeDb(dbFile, getIndex(), replace=True) makeDb(dbFile, getIndex(), replace=True)

View File

@ -52,7 +52,7 @@ class Friendly(callbacks.PrivmsgRegexp):
if match.group(1) == irc.nick: if match.group(1) == irc.nick:
irc.queueMsg(ircmsgs.privmsg(msg.args[0], '%s!' % msg.nick)) irc.queueMsg(ircmsgs.privmsg(msg.args[0], '%s!' % msg.nick))
Class = Friendly Class = Friendly
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -122,7 +122,7 @@ class FunCommands(callbacks.Privmsg):
irc.reply(msg, chr(i)) irc.reply(msg, chr(i))
except ValueError: except ValueError:
irc.error(msg, 'That number doesn\'t map to an 8-bit character.') irc.error(msg, 'That number doesn\'t map to an 8-bit character.')
def base(self, irc, msg, args): def base(self, irc, msg, args):
"""<base> <number> """<base> <number>
@ -154,7 +154,7 @@ class FunCommands(callbacks.Privmsg):
LL.reverse() LL.reverse()
L.extend(LL) L.extend(LL)
irc.reply(msg, ''.join(L)) irc.reply(msg, ''.join(L))
def encode(self, irc, msg, args): def encode(self, irc, msg, args):
"""<encoding> <text> """<encoding> <text>
@ -173,7 +173,7 @@ class FunCommands(callbacks.Privmsg):
""" """
encoding, text = privmsgs.getArgs(args, needed=2) encoding, text = privmsgs.getArgs(args, needed=2)
irc.reply(msg, text.decode(encoding).encode('utf-8')) irc.reply(msg, text.decode(encoding).encode('utf-8'))
def hexlify(self, irc, msg, args): def hexlify(self, irc, msg, args):
"""<text> """<text>
@ -323,7 +323,7 @@ class FunCommands(callbacks.Privmsg):
text = text.replace('x', 'kth') text = text.replace('x', 'kth')
text = text.replace('X', 'KTH') text = text.replace('X', 'KTH')
irc.reply(msg, text) irc.reply(msg, text)
_leettrans = string.maketrans('oOaAeElBTiIts', '004433187!1+5') _leettrans = string.maketrans('oOaAeElBTiIts', '004433187!1+5')
_leetres = ((re.compile(r'\b(?:(?:[yY][o0O][oO0uU])|u)\b'), 'j00'), _leetres = ((re.compile(r'\b(?:(?:[yY][o0O][oO0uU])|u)\b'), 'j00'),
(re.compile(r'fear'), 'ph33r'), (re.compile(r'fear'), 'ph33r'),
@ -405,7 +405,7 @@ class FunCommands(callbacks.Privmsg):
return '%s*i' % imag return '%s*i' % imag
else: else:
return '%s+%si' % (real, imag) return '%s+%si' % (real, imag)
def calc(self, irc, msg, args): def calc(self, irc, msg, args):
"""<math expression> """<math expression>
@ -540,7 +540,7 @@ class FunCommands(callbacks.Privmsg):
def lastfrom(self, irc, msg, args): def lastfrom(self, irc, msg, args):
"""[<channel>] <nick> """[<channel>] <nick>
Returns the last message in <channel> from <nick>. <channel> is only Returns the last message in <channel> from <nick>. <channel> is only
necessary if the message isn't sent in the channel itself. necessary if the message isn't sent in the channel itself.
""" """
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
@ -616,7 +616,7 @@ class FunCommands(callbacks.Privmsg):
irc.error(msg, 'That function has no documentation.') irc.error(msg, 'That function has no documentation.')
return return
irc.reply(msg, s) irc.reply(msg, s)
Class = FunCommands Class = FunCommands

View File

@ -104,7 +104,7 @@ def addWord(db, word, commit=False):
WHERE word=%s))""", word, sorted) WHERE word=%s))""", word, sorted)
if commit: if commit:
db.commit() db.commit()
class FunDB(callbacks.Privmsg): class FunDB(callbacks.Privmsg):
""" """
@ -119,7 +119,7 @@ class FunDB(callbacks.Privmsg):
def die(self): def die(self):
self.db.commit() self.db.commit()
self.db.close() self.db.close()
''' '''
def praise(self, irc, msg, args): def praise(self, irc, msg, args):
"""<something> """<something>
@ -182,7 +182,7 @@ class FunDB(callbacks.Privmsg):
irc.error(msg, 'There is no such insult.') irc.error(msg, 'There is no such insult.')
else: else:
irc.reply(msg, cursor.fetchone()[0]) irc.reply(msg, cursor.fetchone()[0])
def addinsult(self, irc, msg, args): def addinsult(self, irc, msg, args):
"""<insult> """<insult>
@ -267,7 +267,7 @@ class FunDB(callbacks.Privmsg):
irc.error(msg, 'There is no such excuse.') irc.error(msg, 'There is no such excuse.')
else: else:
irc.reply(msg, cursor.fetchone()[0]) irc.reply(msg, cursor.fetchone()[0])
def addexcuse(self, irc, msg, args): def addexcuse(self, irc, msg, args):
"""<excuse> """<excuse>
@ -294,7 +294,7 @@ class FunDB(callbacks.Privmsg):
cursor.execute("""DELETE FROM excuses WHERE id=%s""", id) cursor.execute("""DELETE FROM excuses WHERE id=%s""", id)
self.db.commit() self.db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def numexcuses(self, irc, msg, args): def numexcuses(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -343,7 +343,7 @@ class FunDB(callbacks.Privmsg):
irc.error(msg, 'There is no such lart.') irc.error(msg, 'There is no such lart.')
else: else:
irc.reply(msg, cursor.fetchone()[0]) irc.reply(msg, cursor.fetchone()[0])
def addlart(self, irc, msg, args): def addlart(self, irc, msg, args):
"""<lart> """<lart>
@ -377,7 +377,7 @@ class FunDB(callbacks.Privmsg):
def numlarts(self, irc, msg, args): def numlarts(self, irc, msg, args):
"""takes no arguments """takes no arguments
Returns the number of larts currently in the database. Returns the number of larts currently in the database.
""" """
cursor = self.db.cursor() cursor = self.db.cursor()
@ -443,7 +443,7 @@ class FunDB(callbacks.Privmsg):
else: else:
(city, state) = cursor.fetchone() (city, state) = cursor.fetchone()
irc.reply(msg, '%s, %s' % (city, state)) irc.reply(msg, '%s, %s' % (city, state))
def zipcodefor(self, irc, msg, args): def zipcodefor(self, irc, msg, args):
"""<city> <state> """<city> <state>
@ -474,7 +474,7 @@ class FunDB(callbacks.Privmsg):
random.shuffle(zipcodes) random.shuffle(zipcodes)
irc.reply(msg, '(%s shown of %s): %s' % \ irc.reply(msg, '(%s shown of %s): %s' % \
(len(zipcodes), cursor.rowcount, ', '.join(zipcodes))) (len(zipcodes), cursor.rowcount, ', '.join(zipcodes)))
Class = FunDB Class = FunDB
@ -520,5 +520,5 @@ if __name__ == '__main__':
zipcode, city, state) zipcode, city, state)
db.commit() db.commit()
db.close() db.close()
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -116,8 +116,8 @@ class Gameknot(callbacks.PrivmsgCommandAndRegexp):
raise callbacks.Error, 'The format of the page was odd.' raise callbacks.Error, 'The format of the page was odd.'
except urllib2.URLError: except urllib2.URLError:
raise callbacks.Error, 'Couldn\'t connect to gameknot.com' raise callbacks.Error, 'Couldn\'t connect to gameknot.com'
def gkstats(self, irc, msg, args): def gkstats(self, irc, msg, args):
"""<name> """<name>

View File

@ -212,7 +212,7 @@ class Http(callbacks.Privmsg):
irc.error(msg, 'Couldn\'t open search page.') irc.error(msg, 'Couldn\'t open search page.')
''' '''
_tempregex = re.compile('CLASS=obsTempTextA>(\d+)&deg;F</b></td>',\ _tempregex = re.compile('CLASS=obsTempTextA>(\d+)&deg;F</b></td>',\
re.IGNORECASE) re.IGNORECASE)
_cityregex = re.compile(r'Local Forecast for (.*), (.*?) ') _cityregex = re.compile(r'Local Forecast for (.*), (.*?) ')
@ -263,7 +263,7 @@ class Http(callbacks.Privmsg):
quote = utils.htmlToText(m.group(1)) quote = utils.htmlToText(m.group(1))
quote = ' // '.join(quote.splitlines()) quote = ' // '.join(quote.splitlines())
irc.reply(msg, quote) irc.reply(msg, quote)
_acronymre = re.compile(r'<td[^w]+width="70[^>]+>(?:<b>)?([^<]+)(?:</b>)?') _acronymre = re.compile(r'<td[^w]+width="70[^>]+>(?:<b>)?([^<]+)(?:</b>)?')
def acronym(self, irc, msg, args): def acronym(self, irc, msg, args):
"""<acronym> """<acronym>
@ -321,7 +321,7 @@ class Http(callbacks.Privmsg):
_debBranches = ('stable', 'testing', 'unstable', 'experimental') _debBranches = ('stable', 'testing', 'unstable', 'experimental')
def debversion(self, irc, msg, args): def debversion(self, irc, msg, args):
"""<package name> [stable|testing|unstable|experimental] """<package name> [stable|testing|unstable|experimental]
Returns the current version(s) of a Debian package in the given branch Returns the current version(s) of a Debian package in the given branch
(if any, otherwise all available ones are displayed). (if any, otherwise all available ones are displayed).
""" """
@ -363,7 +363,7 @@ class Http(callbacks.Privmsg):
(numberOfPackages, len(responses), ', '.join(responses)) (numberOfPackages, len(responses), ', '.join(responses))
irc.reply(msg, s) irc.reply(msg, s)
Class = Http Class = Http

View File

@ -131,7 +131,7 @@ class Infobot(callbacks.PrivmsgRegexp):
irc.queueMsg(ircmsgs.privmsg(nick, s)) irc.queueMsg(ircmsgs.privmsg(nick, s))
except KeyError: except KeyError:
irc.reply(msg, 'I don\'t know anything about %s' % key) irc.reply(msg, 'I don\'t know anything about %s' % key)
def factoid(self, irc, msg, match): def factoid(self, irc, msg, match):
r"^(no[ :,-]+)?(.+?)\s+(was|is|am|were|are)\s+(also\s+)?(.+?)(?!\?+)$" r"^(no[ :,-]+)?(.+?)\s+(was|is|am|were|are)\s+(also\s+)?(.+?)(?!\?+)$"
(correction, key, isAre, addition, value) = match.groups() (correction, key, isAre, addition, value) = match.groups()
@ -151,7 +151,7 @@ class Infobot(callbacks.PrivmsgRegexp):
else: else:
self.insertFactoid(key, isAre, value) self.insertFactoid(key, isAre, value)
irc.reply(msg, self.getRandomSaying('confirms')) irc.reply(msg, self.getRandomSaying('confirms'))
def unknown(self, irc, msg, match): def unknown(self, irc, msg, match):
r"^(.+?)\?[?.! ]*$" r"^(.+?)\?[?.! ]*$"
key = match.group(1) key = match.group(1)
@ -169,12 +169,12 @@ class Infobot(callbacks.PrivmsgRegexp):
numAre = cursor.fetchone()[0] numAre = cursor.fetchone()[0]
s = 'I have %s is factoids and %s are factoids' % (numIs, numAre) s = 'I have %s is factoids and %s are factoids' % (numIs, numAre)
irc.reply(msg, s) irc.reply(msg, s)
Class = Infobot Class = Infobot
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
if len(sys.argv) < 2 and sys.argv[1] not in ('is', 'are'): if len(sys.argv) < 2 and sys.argv[1] not in ('is', 'are'):
@ -202,5 +202,5 @@ if __name__ == '__main__':
print 'Invalid line (%s): %r' %(debug.exnToString(e),line) print 'Invalid line (%s): %r' %(debug.exnToString(e),line)
db.commit() db.commit()
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -53,7 +53,7 @@ class KillBold(callbacks.Privmsg):
return ircmsgs.privmsg(msg.args[0],msg.args[1].replace('\x02', '')) return ircmsgs.privmsg(msg.args[0],msg.args[1].replace('\x02', ''))
else: else:
return msg return msg
Class = KillBold Class = KillBold
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -39,12 +39,14 @@ Commands include:
from baseplugin import * from baseplugin import *
import re import re
import time
import conf import conf
import ircdb import ircdb
import ircmsgs import ircmsgs
import privmsgs import privmsgs
import ircutils import ircutils
import schedule
import callbacks import callbacks
def configure(onStart, afterConnect, advanced): def configure(onStart, afterConnect, advanced):
@ -55,20 +57,21 @@ def configure(onStart, afterConnect, advanced):
onStart.append('startnickserv %s %s' % (nick, password)) onStart.append('startnickserv %s %s' % (nick, password))
class NickServ(privmsgs.CapabilityCheckingPrivmsg): class NickServ(privmsgs.CapabilityCheckingPrivmsg):
capability = 'owner' capability = 'admin'
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
self.nickserv = '' self.nickserv = ''
def startnickserv(self, irc, msg, args): def startnickserv(self, irc, msg, args):
"<bot's nick> <password> <NickServ's nick (defaults to NickServ)>" "<bot's nick> <password> <NickServ's nick (defaults to NickServ)> " \
"<ChanServ's nick (defaults to ChanServ)"
if ircutils.isChannel(msg.args[0]): if ircutils.isChannel(msg.args[0]):
irc.error(msg, conf.replyRequiresPrivacy) irc.error(msg, conf.replyRequiresPrivacy)
return return
(self.nick, self.password, nickserv) = privmsgs.getArgs(args, (self.nick, self.password, nickserv, chanserv) = \
needed=2, privmsgs.getArgs(args, needed=2, optional=2)
optional=1)
self.nickserv = nickserv or 'NickServ' self.nickserv = nickserv or 'NickServ'
self.chanserv = chanserv or 'ChanServ'
self.sentGhost = False self.sentGhost = False
self._ghosted = re.compile('%s.*killed' % self.nick) self._ghosted = re.compile('%s.*killed' % self.nick)
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
@ -94,6 +97,19 @@ class NickServ(privmsgs.CapabilityCheckingPrivmsg):
ghost = 'GHOST %s %s' % (self.nick, self.password) ghost = 'GHOST %s %s' % (self.nick, self.password)
irc.queueMsg(ircmsgs.privmsg(self.nickserv, ghost)) irc.queueMsg(ircmsgs.privmsg(self.nickserv, ghost))
self.sentGhost = True self.sentGhost = True
def flipSentGhost():
self.sentGhost = False
schedule.addEvent(flipSentGhost, time.time() + 300)
def getops(self, irc, msg, args):
"""[<channel>]
Attempts to get ops from ChanServ in <channel>. If no channel is
given, the current channel is assumed.
"""
channel = privmsgs.getChannel(msg, args)
irc.sendMsg(ircmsgs.privmsg(self.chanserv, 'op %s' % channel))
Class = NickServ Class = NickServ

View File

@ -79,20 +79,20 @@ class Notes(callbacks.Privmsg):
note TEXT note TEXT
)""") )""")
self.db.commit() self.db.commit()
def _addUser(self, username): def _addUser(self, username):
"Not callable from channel, used to add users to database." "Not callable from channel, used to add users to database."
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute('INSERT INTO users VALUES (NULL, %s)', username) cursor.execute('INSERT INTO users VALUES (NULL, %s)', username)
self.db.commit() self.db.commit()
def getUserId(self, username): def getUserId(self, username):
"Returns the user id matching the given username from the users table." "Returns the user id matching the given username from the users table."
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute('SELECT id FROM users where name=%s', username) cursor.execute('SELECT id FROM users where name=%s', username)
if cursor.rowcount != 0: if cursor.rowcount != 0:
return cursor.fetchone()[0] return cursor.fetchone()[0]
else: else:
raise KeyError, username raise KeyError, username
def getUserName(self, userid): def getUserName(self, userid):
@ -116,7 +116,7 @@ class Notes(callbacks.Privmsg):
"Called when module is unloaded/reloaded." "Called when module is unloaded/reloaded."
self.db.commit() self.db.commit()
self.db.close() self.db.close()
def doJoin(self, irc, msg): def doJoin(self, irc, msg):
try: try:
name = ircdb.users.getUserName(msg.prefix) name = ircdb.users.getUserName(msg.prefix)
@ -151,7 +151,7 @@ class Notes(callbacks.Privmsg):
def sendnote(self, irc, msg, args): def sendnote(self, irc, msg, args):
"""<recipient> <text> """<recipient> <text>
Sends a new note to the user specified. Sends a new note to the user specified.
""" """
(name, note) = privmsgs.getArgs(args, needed=2) (name, note) = privmsgs.getArgs(args, needed=2)
@ -165,13 +165,13 @@ class Notes(callbacks.Privmsg):
self._addUser(recipient) self._addUser(recipient)
senderId = self.getUserId(sender) senderId = self.getUserId(sender)
recipId = self.getUserId(recipient) recipId = self.getUserId(recipient)
if ircutils.isChannel(msg.args[0]): if ircutils.isChannel(msg.args[0]):
public = 1 public = 1
else: else:
public = 0 public = 0
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute("""INSERT INTO notes VALUES cursor.execute("""INSERT INTO notes VALUES
(NULL, %s, %s, %s, 0, 0, %s, %s)""", (NULL, %s, %s, %s, 0, 0, %s, %s)""",
senderId, recipId, int(time.time()), senderId, recipId, int(time.time()),
public, note) public, note)
self.db.commit() self.db.commit()
@ -179,7 +179,7 @@ class Notes(callbacks.Privmsg):
def note(self, irc, msg, args): def note(self, irc, msg, args):
"""<note id> """<note id>
Retrieves a single note by unique note id. Retrieves a single note by unique note id.
""" """
noteid = privmsgs.getArgs(args) noteid = privmsgs.getArgs(args)
@ -191,7 +191,7 @@ class Notes(callbacks.Privmsg):
return return
cursor = self.db.cursor() cursor = self.db.cursor()
cursor.execute("""SELECT notes.note, notes.to_id, notes.from_id, cursor.execute("""SELECT notes.note, notes.to_id, notes.from_id,
notes.added_at, notes.public notes.added_at, notes.public
FROM users, notes FROM users, notes
WHERE users.name=%s AND WHERE users.name=%s AND
notes.to_id=users.id AND notes.to_id=users.id AND
@ -216,7 +216,7 @@ class Notes(callbacks.Privmsg):
def notes(self, irc, msg, args): def notes(self, irc, msg, args):
"""takes no arguments """takes no arguments
Retrieves all your unread notes. Retrieves all your unread notes.
""" """
try: try:
@ -275,7 +275,7 @@ class Notes(callbacks.Privmsg):
ircutils.shrinkList(ids, ', ', 425) ircutils.shrinkList(ids, ', ', 425)
ids.reverse() ids.reverse()
irc.reply(msg, ', '.join(ids)) irc.reply(msg, ', '.join(ids))
Class = Notes Class = Notes

View File

@ -179,7 +179,7 @@ class Quotes(ChannelDBHandler, callbacks.Privmsg):
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
else: else:
irc.error(msg, conf.replyNoCapability % capability) irc.error(msg, conf.replyNoCapability % capability)
Class = Quotes Class = Quotes
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -84,7 +84,7 @@ def configure(onStart, afterConnect, advanced):
if infocmd: if infocmd:
onStart.append('alias %s "rssinfo %s"' % (infocmd, url)) onStart.append('alias %s "rssinfo %s"' % (infocmd, url))
onStart.append('freeze %s' % infocmd) onStart.append('freeze %s' % infocmd)
class RSS(callbacks.Privmsg): class RSS(callbacks.Privmsg):
threaded = True threaded = True
@ -114,7 +114,7 @@ class RSS(callbacks.Privmsg):
irc.error(msg, 'Error grabbing RSS feed') irc.error(msg, 'Error grabbing RSS feed')
return return
irc.reply(msg, payload) irc.reply(msg, payload)
def rssinfo(self, irc, msg, args): def rssinfo(self, irc, msg, args):
"""<url> """<url>
@ -142,7 +142,7 @@ class RSS(callbacks.Privmsg):
# The rest of the entries are all available in the channel key # The rest of the entries are all available in the channel key
response = 'Title: %s; URL: <%s>; ' \ response = 'Title: %s; URL: <%s>; ' \
'Description: %s; Last updated %s.' % ( 'Description: %s; Last updated %s.' % (
info.get('title', 'unavailable').strip(), info.get('title', 'unavailable').strip(),
info.get('link', 'unavailable').strip(), info.get('link', 'unavailable').strip(),
info.get('description', 'unavailable').strip(), info.get('description', 'unavailable').strip(),
when) when)

View File

@ -87,7 +87,7 @@ def configure(onStart, afterConnect, advanced):
while yn('Would like to relay between any more channels?') == 'y': while yn('Would like to relay between any more channels?') == 'y':
channel = anything('What channel?') channel = anything('What channel?')
afterConnect.append('relayjoin %s' % channel) afterConnect.append('relayjoin %s' % channel)
class Relay(callbacks.Privmsg): class Relay(callbacks.Privmsg):
def __init__(self): def __init__(self):
@ -99,7 +99,7 @@ class Relay(callbacks.Privmsg):
self.lastmsg = ircmsgs.ping('this is just a fake message') self.lastmsg = ircmsgs.ping('this is just a fake message')
self.channels = sets.Set() self.channels = sets.Set()
self.abbreviations = {} self.abbreviations = {}
def inFilter(self, irc, msg): def inFilter(self, irc, msg):
if not isinstance(irc, irclib.Irc): if not isinstance(irc, irclib.Irc):
irc = irc.getRealIrc() irc = irc.getRealIrc()
@ -110,7 +110,7 @@ class Relay(callbacks.Privmsg):
self.ircstates[irc].addMsg(irc, self.lastmsg) self.ircstates[irc].addMsg(irc, self.lastmsg)
self.lastmsg = msg self.lastmsg = msg
return msg return msg
def startrelay(self, irc, msg, args): def startrelay(self, irc, msg, args):
"""<network abbreviation for current server> """<network abbreviation for current server>
@ -131,7 +131,7 @@ class Relay(callbacks.Privmsg):
def relayconnect(self, irc, msg, args): def relayconnect(self, irc, msg, args):
"""<network abbreviation> <domain:port> (port defaults to 6667) """<network abbreviation> <domain:port> (port defaults to 6667)
Connects to another network at <domain:port>. The network Connects to another network at <domain:port>. The network
abbreviation <network abbreviation> is used when relaying messages from abbreviation <network abbreviation> is used when relaying messages from
that network to other networks. that network to other networks.
@ -267,7 +267,7 @@ class Relay(callbacks.Privmsg):
do312 = do311 do312 = do311
do317 = do311 do317 = do311
do319 = do311 do319 = do311
def do318(self, irc, msg): def do318(self, irc, msg):
if not isinstance(irc, irclib.Irc): if not isinstance(irc, irclib.Irc):
irc = irc.getRealIrc() irc = irc.getRealIrc()
@ -289,7 +289,7 @@ class Relay(callbacks.Privmsg):
s = '%s (%s) has been online since %s (idle for %s) and is on %s' % \ s = '%s (%s) has been online since %s (idle for %s) and is on %s' % \
(nick, hostmask, signon, idle, channels) (nick, hostmask, signon, idle, channels)
replyIrc.reply(replyMsg, s) replyIrc.reply(replyMsg, s)
def _formatPrivmsg(self, nick, network, msg): def _formatPrivmsg(self, nick, network, msg):
# colorize nicks # colorize nicks
nick = ircutils.mircColor(nick, *ircutils.canonicalColor(nick)) nick = ircutils.mircColor(nick, *ircutils.canonicalColor(nick))
@ -358,7 +358,7 @@ class Relay(callbacks.Privmsg):
for otherIrc in self.ircs.itervalues(): for otherIrc in self.ircs.itervalues():
if otherIrc != irc: if otherIrc != irc:
otherIrc.queueMsg(ircmsgs.privmsg(channel, s)) otherIrc.queueMsg(ircmsgs.privmsg(channel, s))
def doNick(self, irc, msg): def doNick(self, irc, msg):
if self.started: if self.started:
if not isinstance(irc, irclib.Irc): if not isinstance(irc, irclib.Irc):
@ -386,7 +386,7 @@ class Relay(callbacks.Privmsg):
for otherIrc in self.ircs.itervalues(): for otherIrc in self.ircs.itervalues():
if otherIrc != irc: if otherIrc != irc:
otherIrc.queueMsg(ircmsgs.privmsg(channel, s)) otherIrc.queueMsg(ircmsgs.privmsg(channel, s))
def outFilter(self, irc, msg): def outFilter(self, irc, msg):
if not self.started: if not self.started:
return msg return msg
@ -419,9 +419,9 @@ class Relay(callbacks.Privmsg):
if otherIrc != irc: if otherIrc != irc:
if otherIrc.state.getTopic(channel) != topic: if otherIrc.state.getTopic(channel) != topic:
otherIrc.queueMsg(ircmsgs.topic(channel, topic)) otherIrc.queueMsg(ircmsgs.topic(channel, topic))
return msg return msg
Class = Relay Class = Relay
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -75,6 +75,6 @@ class ThreadedFunCommands(callbacks.Privmsg):
beta = version.strip() beta = version.strip()
irc.reply(msg, 'The latest stable kernel is %s; ' \ irc.reply(msg, 'The latest stable kernel is %s; ' \
'the latest beta kernel is %s.' % (stable, beta)) 'the latest beta kernel is %s.' % (stable, beta))
Class = ThreadedFunCommands Class = ThreadedFunCommands
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -119,7 +119,7 @@ class Topic(callbacks.Privmsg):
except IndexError: except IndexError:
irc.error(msg, 'That\'s not a valid index.') irc.error(msg, 'That\'s not a valid index.')
return return
def changetopic(self, irc, msg, args): def changetopic(self, irc, msg, args):
"""[<channel>] <number> <regexp> """[<channel>] <number> <regexp>
@ -159,7 +159,7 @@ class Topic(callbacks.Privmsg):
topics.insert(number, newTopic) topics.insert(number, newTopic)
newTopic = self.topicSeparator.join(topics) newTopic = self.topicSeparator.join(topics)
irc.queueMsg(ircmsgs.topic(channel, newTopic)) irc.queueMsg(ircmsgs.topic(channel, newTopic))
def removetopic(self, irc, msg, args): def removetopic(self, irc, msg, args):
"[<channel>] (if not sent in the channel itself) <topic number>" "[<channel>] (if not sent in the channel itself) <topic number>"
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)

View File

@ -58,7 +58,7 @@ class TwistedCommands(callbacks.Privmsg):
failure.printDetailedTraceback() failure.printDetailedTraceback()
irc.error(msg, failure.getErrorMessage()) irc.error(msg, failure.getErrorMessage())
return errback return errback
dictnumberre = re.compile('^\d+:\s*(.*)$') dictnumberre = re.compile('^\d+:\s*(.*)$')
def dictCallback(self, irc, msg, word): def dictCallback(self, irc, msg, word):
def formatDictResults(definitions): def formatDictResults(definitions):
@ -91,7 +91,7 @@ class TwistedCommands(callbacks.Privmsg):
deferred = dict.define('dict.org', 2628, 'wn', word) deferred = dict.define('dict.org', 2628, 'wn', word)
deferred.addCallback(self.dictCallback(irc, msg, word)) deferred.addCallback(self.dictCallback(irc, msg, word))
deferred.addErrback(self.defaultErrback(irc, msg)) deferred.addErrback(self.defaultErrback(irc, msg))
class TwistedRegexp(callbacks.PrivmsgRegexp): class TwistedRegexp(callbacks.PrivmsgRegexp):
def dccrecv(self, irc, msg, match): def dccrecv(self, irc, msg, match):

View File

@ -187,8 +187,8 @@ class URLSnarfer(callbacks.Privmsg, ChannelDBHandler):
else: else:
(url,) = cursor.fetchone() (url,) = cursor.fetchone()
irc.reply(msg, url) irc.reply(msg, url)
Class = URLSnarfer Class = URLSnarfer
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -61,7 +61,7 @@ def configure(onStart, afterConnect, advanced):
print 'choose to install it later, and then the module will' print 'choose to install it later, and then the module will'
print 'automatically work, as long as it is in the path of the' print 'automatically work, as long as it is in the path of the'
print 'user that supybot runs under.' print 'user that supybot runs under.'
print print
print 'The "progstats" command can reveal potentially sensitive' print 'The "progstats" command can reveal potentially sensitive'
print 'information about your machine. Here\'s an example of its output:' print 'information about your machine. Here\'s an example of its output:'
@ -80,7 +80,7 @@ def progstats():
os.getcwd(), " ".join(sys.argv), os.getcwd(), " ".join(sys.argv),
sys.version.translate(string.ascii, '\r\n')) sys.version.translate(string.ascii, '\r\n'))
return response return response
class Unix(callbacks.Privmsg): class Unix(callbacks.Privmsg):
def __init__(self): def __init__(self):
@ -117,7 +117,7 @@ class Unix(callbacks.Privmsg):
except KeyError: except KeyError:
name = '(unknown)' name = '(unknown)'
irc.reply(msg, '%s (#%s): %s' % (name, i, os.strerror(i))) irc.reply(msg, '%s (#%s): %s' % (name, i, os.strerror(i)))
def progstats(self, irc, msg, args): def progstats(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -144,7 +144,7 @@ class Unix(callbacks.Privmsg):
salt = makeSalt() salt = makeSalt()
irc.reply(msg, crypt.crypt(password, salt)) irc.reply(msg, crypt.crypt(password, salt))
def spell(self, irc, msg, args): def spell(self, irc, msg, args):
"""<word> """<word>
Returns the result of passing <word> to aspell/ispell. The results Returns the result of passing <word> to aspell/ispell. The results
@ -158,7 +158,7 @@ class Unix(callbacks.Privmsg):
return return
self._spellWrite.write(word) self._spellWrite.write(word)
self._spellWrite.write('\n') self._spellWrite.write('\n')
line = self._spellRead.readline() line = self._spellRead.readline()
# aspell puts extra whitespace, ignore it # aspell puts extra whitespace, ignore it
while line == '\n': while line == '\n':
line = self._spellRead.readline() line = self._spellRead.readline()
@ -178,7 +178,7 @@ class Unix(callbacks.Privmsg):
else: else:
resp = 'Something unexpected was seen in the [ai]spell output.' resp = 'Something unexpected was seen in the [ai]spell output.'
irc.reply(msg, resp) irc.reply(msg, resp)
Class = Unix Class = Unix
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -48,11 +48,11 @@ def configure(onStart, afterConnect, advanced):
class Utilities(callbacks.Privmsg): class Utilities(callbacks.Privmsg):
def ignore(self, irc, msg, args): def ignore(self, irc, msg, args):
pass pass
def shrink(self, irc, msg, args): def shrink(self, irc, msg, args):
text = privmsgs.getArgs(args) text = privmsgs.getArgs(args)
irc.reply(msg, text[:400]) irc.reply(msg, text[:400])
def strjoin(self, irc, msg, args): def strjoin(self, irc, msg, args):
"<separator> <strings to join>" "<separator> <strings to join>"
sep = args.pop(0) sep = args.pop(0)
@ -128,7 +128,7 @@ class Utilities(callbacks.Privmsg):
irc.error(msg, 'Invalid regexp: %s' % e.args[0]) irc.error(msg, 'Invalid regexp: %s' % e.args[0])
return return
irc.reply(msg, ' '.join(r.findall(text))) irc.reply(msg, ' '.join(r.findall(text)))
Class = Utilities Class = Utilities
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -170,5 +170,5 @@ if __name__ == '__main__':
print 'You\'re done! Now run the bot with the command line:' print 'You\'re done! Now run the bot with the command line:'
print 'src/bot.py conf/%s' % name print 'src/bot.py conf/%s' % name
print print
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -131,7 +131,7 @@ class MiscCommands(callbacks.Privmsg):
(command, simplehelp)) (command, simplehelp))
else: else:
irc.error(msg, 'That command has no help at all.') irc.error(msg, 'That command has no help at all.')
def bug(self, irc, msg, args): def bug(self, irc, msg, args):
"""takes no arguments """takes no arguments

View File

@ -126,7 +126,7 @@ class AsyncoreDriver(asynchat.async_chat, object):
def die(self): def die(self):
self.close() self.close()
class ReplListener(asyncore.dispatcher, object): class ReplListener(asyncore.dispatcher, object):
def __init__(self, port=conf.telnetPort): def __init__(self, port=conf.telnetPort):
asyncore.dispatcher.__init__(self) asyncore.dispatcher.__init__(self)

View File

@ -98,7 +98,7 @@ def reply(msg, s):
if len(m) > 512: if len(m) > 512:
m = reply(msg, 'My response would\'ve been too long.') m = reply(msg, 'My response would\'ve been too long.')
return m return m
class RateLimiter: class RateLimiter:
lastRequest = {} lastRequest = {}
def __init__(self): def __init__(self):
@ -232,8 +232,8 @@ def tokenize(s):
raise SyntaxError, str(e) raise SyntaxError, str(e)
debug.msg('tokenize took %s seconds.' % (time.time() - start), 'verbose') debug.msg('tokenize took %s seconds.' % (time.time() - start), 'verbose')
return args return args
class IrcObjectProxy: class IrcObjectProxy:
def __init__(self, irc, msg, args): def __init__(self, irc, msg, args):
@ -340,7 +340,7 @@ class CommandThread(threading.Thread):
self.irc = irc self.irc = irc
self.msg = msg self.msg = msg
self.setDaemon(True) self.setDaemon(True)
def run(self): def run(self):
try: try:
start = time.time() start = time.time()
@ -356,7 +356,7 @@ class CommandThread(threading.Thread):
debug.recoverableException() debug.recoverableException()
self.irc.error(self.msg, debug.exnToString(e)) self.irc.error(self.msg, debug.exnToString(e))
class Privmsg(irclib.IrcCallback): class Privmsg(irclib.IrcCallback):
"""Base class for all Privmsg handlers.""" """Base class for all Privmsg handlers."""
threaded = False threaded = False
@ -508,7 +508,7 @@ class PrivmsgCommandAndRegexp(Privmsg):
method = getattr(self, name) method = getattr(self, name)
r = re.compile(method.__doc__, self.flags) r = re.compile(method.__doc__, self.flags)
self.res.append((r, method)) self.res.append((r, method))
def doPrivmsg(self, irc, msg): def doPrivmsg(self, irc, msg):
if ircdb.checkIgnored(msg.prefix, msg.args[0]): if ircdb.checkIgnored(msg.prefix, msg.args[0]):
return return
@ -528,10 +528,10 @@ class PrivmsgCommandAndRegexp(Privmsg):
if msg: if msg:
args = tokenize(s) args = tokenize(s)
self.Proxy(irc, msg, args) self.Proxy(irc, msg, args)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -188,3 +188,6 @@ driverModule = 'asyncoreDrivers'
############################### ###############################
############################### ###############################
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -45,7 +45,7 @@ except ImportError:
class conf: class conf:
logDir = '.' logDir = '.'
detailedTracebacks = True detailedTracebacks = True
import world import world
### ###

View File

@ -284,5 +284,5 @@ def partition(p, L):
def flip((x, y)): def flip((x, y)):
return (y, x) return (y, x)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -152,7 +152,7 @@ class UserCapabilitySet(CapabilitySet):
capability = ircutils.toLower(capability) capability = ircutils.toLower(capability)
assert capability != '!owner', '"!owner" disallowed.' assert capability != '!owner', '"!owner" disallowed.'
CapabilitySet.add(self, capability) CapabilitySet.add(self, capability)
class IrcUser(object): class IrcUser(object):
"""This class holds the capabilities and authentications for a user. """This class holds the capabilities and authentications for a user.
""" """
@ -498,7 +498,7 @@ def _x(capability, ret):
return not ret return not ret
else: else:
return ret return ret
def checkCapability(hostmask, capability, users=users, channels=channels): def checkCapability(hostmask, capability, users=users, channels=channels):
#debug.printf('*** checking %s for %s' % (hostmask, capability)) #debug.printf('*** checking %s for %s' % (hostmask, capability))
if world.startup: if world.startup:
@ -560,7 +560,7 @@ def checkCapability(hostmask, capability, users=users, channels=channels):
else: else:
#debug.printf('returning appropriate value given no good reason') #debug.printf('returning appropriate value given no good reason')
return _x(capability, conf.defaultAllow) return _x(capability, conf.defaultAllow)
def checkCapabilities(hostmask, capabilities, requireAll=False): def checkCapabilities(hostmask, capabilities, requireAll=False):
"""Checks that a user has capabilities in a list. """Checks that a user has capabilities in a list.

View File

@ -227,7 +227,7 @@ class IrcState(IrcCommandDispatcher):
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other
def copy(self): def copy(self):
ret = self.__class__() ret = self.__class__()
ret.history = copy.copy(self.history) ret.history = copy.copy(self.history)

View File

@ -112,7 +112,7 @@ class IrcMsg(object):
else: else:
(self.nick, self.user, self.host) = (self.prefix,)*3 (self.nick, self.user, self.host) = (self.prefix,)*3
self.args = tuple(self.args) self.args = tuple(self.args)
def __str__(self): def __str__(self):
if self._str is not None: if self._str is not None:
return self._str return self._str
@ -155,7 +155,7 @@ class IrcMsg(object):
self._len += len(arg) + 1 # Remember space prior to the arg. self._len += len(arg) + 1 # Remember space prior to the arg.
self._len += 2 # For colon before the prefix and before the last arg. self._len += 2 # For colon before the prefix and before the last arg.
return self._len return self._len
def __eq__(self, other): def __eq__(self, other):
return hash(self) == hash(other) and \ return hash(self) == hash(other) and \
self.command == other.command and \ self.command == other.command and \
@ -249,7 +249,7 @@ def prettyPrint(msg, addRecipients=False):
elif msg.command == 'TOPIC': elif msg.command == 'TOPIC':
s = '*** %s changes topic to %s' % (nickorprefix(), msg.args[1]) s = '*** %s changes topic to %s' % (nickorprefix(), msg.args[1])
return s return s
### ###
# Various IrcMsg functions # Various IrcMsg functions
### ###

View File

@ -309,7 +309,7 @@ class IrcString(str):
def __str__(self): def __str__(self):
return str(self.original) return str(self.original)
def __eq__(self, s): def __eq__(self, s):
try: try:
return toLower(s) == self.lowered return toLower(s) == self.lowered

View File

@ -100,7 +100,7 @@ def getKeywordArgs(irc, msg, d=None):
args.append(left) args.append(left)
del args[0] # The command name itself. del args[0] # The command name itself.
return (args, d) return (args, d)
def checkCapability(f, capability): def checkCapability(f, capability):
def newf(self, irc, msg, args): def newf(self, irc, msg, args):
if ircdb.checkCapability(msg.prefix, capability): if ircdb.checkCapability(msg.prefix, capability):
@ -225,7 +225,7 @@ class OwnerCommands(CapabilityCheckingPrivmsg):
""" """
world.upkeep() world.upkeep()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def set(self, irc, msg, args): def set(self, irc, msg, args):
"""<name> <value> """<name> <value>
@ -280,7 +280,7 @@ class OwnerCommands(CapabilityCheckingPrivmsg):
world.superReload(__import__(name)) world.superReload(__import__(name))
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
''' '''
def reload(self, irc, msg, args): def reload(self, irc, msg, args):
"""<callback name> """<callback name>
@ -307,7 +307,7 @@ class OwnerCommands(CapabilityCheckingPrivmsg):
irc.error(msg, 'No plugin %s exists.' % name) irc.error(msg, 'No plugin %s exists.' % name)
else: else:
irc.error(msg, 'There was no callback %s.' % name) irc.error(msg, 'There was no callback %s.' % name)
def unload(self, irc, msg, args): def unload(self, irc, msg, args):
"""<callback name> """<callback name>

View File

@ -47,7 +47,7 @@ class RTuple(tuple):
return not tuple.__le__(self, other) return not tuple.__le__(self, other)
def __cmp__(self, other): def __cmp__(self, other):
return -1*tuple.__cmp__(self, other) return -1*tuple.__cmp__(self, other)
class Schedule(drivers.IrcDriver): class Schedule(drivers.IrcDriver):
def __init__(self): def __init__(self):
drivers.IrcDriver.__init__(self) drivers.IrcDriver.__init__(self)

View File

@ -66,10 +66,10 @@ class RingBuffer(object):
return False return False
return True return True
return False return False
def __nonzero__(self): def __nonzero__(self):
return len(self) > 0 return len(self) > 0
def __contains__(self, elt): def __contains__(self, elt):
return elt in self.L return elt in self.L
@ -88,7 +88,7 @@ class RingBuffer(object):
def extend(self, seq): def extend(self, seq):
for elt in seq: for elt in seq:
self.append(elt) self.append(elt)
def __getitem__(self, idx): def __getitem__(self, idx):
if self.full: if self.full:
oidx = idx oidx = idx
@ -226,7 +226,7 @@ class queue(object):
return self.front[-(idx+1)] return self.front[-(idx+1)]
else: else:
return self.back[(idx-len(self.front))] return self.back[(idx-len(self.front))]
def __setitem__(self, oidx, value): def __setitem__(self, oidx, value):
if len(self) == 0: if len(self) == 0:
raise IndexError, 'queue index out of range' raise IndexError, 'queue index out of range'
@ -272,7 +272,7 @@ class queue(object):
self.front = L self.front = L
self.back = [] self.back = []
class MaxLengthQueue(queue): class MaxLengthQueue(queue):
__slots__ = ('length',) __slots__ = ('length',)
def __init__(self, length, seq=()): def __init__(self, length, seq=()):
@ -285,7 +285,7 @@ class MaxLengthQueue(queue):
def __setstate__(self, (length, q)): def __setstate__(self, (length, q)):
self.length = length self.length = length
queue.__setstate__(self, q) queue.__setstate__(self, q)
def enqueue(self, elt): def enqueue(self, elt):
queue.enqueue(self, elt) queue.enqueue(self, elt)
if len(self) > self.length: if len(self) > self.length:

View File

@ -87,7 +87,7 @@ class SupyIrcProtocol(LineReceiver):
self.transport.loseConnection() self.transport.loseConnection()
reconnect = die reconnect = die
class SupyReconnectingFactory(ReconnectingClientFactory): class SupyReconnectingFactory(ReconnectingClientFactory):
maxDelay = 600 maxDelay = 600
@ -96,7 +96,7 @@ class SupyReconnectingFactory(ReconnectingClientFactory):
self.irc = irc self.irc = irc
self.server = (server, port) self.server = (server, port)
reactor.connectTCP(server, port, self) reactor.connectTCP(server, port, self)
class MyShell(Shell): class MyShell(Shell):
def checkUserAndPass(self, username, password): def checkUserAndPass(self, username, password):
@ -112,13 +112,13 @@ class MyShell(Shell):
return False return False
except KeyError: except KeyError:
return False return False
class MyShellFactory(ShellFactory): class MyShellFactory(ShellFactory):
protocol = MyShell protocol = MyShell
if conf.telnetEnable and __name__ != '__main__': if conf.telnetEnable and __name__ != '__main__':
reactor.listenTCP(conf.telnetPort, MyShellFactory()) reactor.listenTCP(conf.telnetPort, MyShellFactory())
Driver = SupyReconnectingFactory Driver = SupyReconnectingFactory

View File

@ -160,8 +160,8 @@ class PluginTestCase(unittest.TestCase):
self.assertEqual(len(expectedResponses), len(responses)) self.assertEqual(len(expectedResponses), len(responses))
for (m, expected) in zip(responses, expectedResponses): for (m, expected) in zip(responses, expectedResponses):
self.assertEqual(m.args[1], expected) self.assertEqual(m.args[1], expected)
if __name__ == '__main__': if __name__ == '__main__':
world.testing = True world.testing = True
if len(sys.argv) > 1: if len(sys.argv) > 1:

View File

@ -78,7 +78,7 @@ class FunCommandsTest(PluginTestCase):
def testPydoc(self): def testPydoc(self):
self.assertNotError('pydoc str') self.assertNotError('pydoc str')
self.assertError('pydoc foobar') self.assertError('pydoc foobar')
def testOrd(self): def testOrd(self):
for c in map(chr, range(256)): for c in map(chr, range(256)):
i = ord(c) i = ord(c)

View File

@ -103,7 +103,7 @@ class UserCapabilitySetTestCase(unittest.TestCase):
d.add('owner') d.add('owner')
self.failUnless(d.check('owner')) self.failUnless(d.check('owner'))
class CapabilitySetTestCase(unittest.TestCase): class CapabilitySetTestCase(unittest.TestCase):
def testContains(self): def testContains(self):
@ -119,7 +119,7 @@ class CapabilitySetTestCase(unittest.TestCase):
s.add('!foo') s.add('!foo')
self.failUnless('foo' in s) self.failUnless('foo' in s)
self.failUnless('!foo' in s) self.failUnless('!foo' in s)
def testCheck(self): def testCheck(self):
s = ircdb.CapabilitySet() s = ircdb.CapabilitySet()
self.assertRaises(KeyError, s.check, 'foo') self.assertRaises(KeyError, s.check, 'foo')
@ -146,7 +146,7 @@ class CapabilitySetTestCase(unittest.TestCase):
s.add('foo') s.add('foo')
self.failUnless(s.check('foo')) self.failUnless(s.check('foo'))
self.failIf(s.check('!foo')) self.failIf(s.check('!foo'))
class UserCapabilitySetTestCase(unittest.TestCase): class UserCapabilitySetTestCase(unittest.TestCase):
def testOwner(self): def testOwner(self):
@ -179,7 +179,7 @@ class IrcUserTestCase(unittest.TestCase):
u.addCapability('owner') u.addCapability('owner')
self.failUnless(u.checkCapability('foo')) self.failUnless(u.checkCapability('foo'))
self.failIf(u.checkCapability('!foo')) self.failIf(u.checkCapability('!foo'))
def testInitCapabilities(self): def testInitCapabilities(self):
u = ircdb.IrcUser(capabilities=['foo']) u = ircdb.IrcUser(capabilities=['foo'])
self.failUnless(u.checkCapability('foo')) self.failUnless(u.checkCapability('foo'))
@ -211,7 +211,7 @@ class IrcUserTestCase(unittest.TestCase):
u = ircdb.IrcUser(ignore=True) u = ircdb.IrcUser(ignore=True)
self.failIf(u.checkCapability('foo')) self.failIf(u.checkCapability('foo'))
self.failUnless(u.checkCapability('!foo')) self.failUnless(u.checkCapability('!foo'))
class IrcChannelTestCase(unittest.TestCase): class IrcChannelTestCase(unittest.TestCase):
def testInit(self): def testInit(self):
c = ircdb.IrcChannel() c = ircdb.IrcChannel()
@ -240,7 +240,7 @@ class IrcChannelTestCase(unittest.TestCase):
def testLobotomized(self): def testLobotomized(self):
c = ircdb.IrcChannel(lobotomized=True) c = ircdb.IrcChannel(lobotomized=True)
self.failUnless(c.checkIgnored('')) self.failUnless(c.checkIgnored(''))
def testIgnored(self): def testIgnored(self):
prefix = 'foo!bar@baz' prefix = 'foo!bar@baz'
banmask = ircutils.banmask(prefix) banmask = ircutils.banmask(prefix)
@ -262,10 +262,10 @@ class UsersDictionaryTestCase(unittest.TestCase):
fd.write('{}\n') fd.write('{}\n')
fd.close() fd.close()
self.users = ircdb.UsersDictionary(self.filename) self.users = ircdb.UsersDictionary(self.filename)
def tearDown(self): def tearDown(self):
os.remove(self.filename) os.remove(self.filename)
def testGetSetDelUser(self): def testGetSetDelUser(self):
self.assertRaises(KeyError, self.users.getUser, 'foo') self.assertRaises(KeyError, self.users.getUser, 'foo')
self.assertRaises(KeyError, self.users.getUser, 'foo!bar@baz') self.assertRaises(KeyError, self.users.getUser, 'foo!bar@baz')
@ -283,7 +283,7 @@ class UsersDictionaryTestCase(unittest.TestCase):
u.removeHostmask(banmask) u.removeHostmask(banmask)
u.addHostmask('*!*@*') u.addHostmask('*!*@*')
self.assertRaises(ValueError, self.users.setUser, 'biff', u) self.assertRaises(ValueError, self.users.setUser, 'biff', u)
class CheckCapabilityTestCase(unittest.TestCase): class CheckCapabilityTestCase(unittest.TestCase):
filename = 'CheckCapabilityTestCase.conf' filename = 'CheckCapabilityTestCase.conf'
@ -334,7 +334,7 @@ class CheckCapabilityTestCase(unittest.TestCase):
self.users.setUser('antichanfoo', antichanfoo) self.users.setUser('antichanfoo', antichanfoo)
channel = ircdb.IrcChannel() channel = ircdb.IrcChannel()
self.channels.setChannel(self.channel, channel) self.channels.setChannel(self.channel, channel)
def tearDown(self): def tearDown(self):
os.remove(self.filename) os.remove(self.filename)
@ -383,10 +383,10 @@ class CheckCapabilityTestCase(unittest.TestCase):
def testJustChanFoo(self): def testJustChanFoo(self):
self.channels.setChannel(self.channel, self.channelnothing) self.channels.setChannel(self.channel, self.channelnothing)
self.failUnless(self.checkCapability(self.justchanfoo, self.chancap)) self.failUnless(self.checkCapability(self.justchanfoo, self.chancap))
self.failIf(self.checkCapability(self.justchanfoo, self.antichancap)) self.failIf(self.checkCapability(self.justchanfoo, self.antichancap))
self.channelnothing.defaultAllow = not self.channelnothing.defaultAllow self.channelnothing.defaultAllow = not self.channelnothing.defaultAllow
self.failUnless(self.checkCapability(self.justchanfoo, self.chancap)) self.failUnless(self.checkCapability(self.justchanfoo, self.chancap))
self.failIf(self.checkCapability(self.justchanfoo, self.antichancap)) self.failIf(self.checkCapability(self.justchanfoo, self.antichancap))
self.channels.setChannel(self.channel, self.channelanticap) self.channels.setChannel(self.channel, self.channelanticap)
self.failUnless(self.checkCapability(self.justchanfoo, self.chancap)) self.failUnless(self.checkCapability(self.justchanfoo, self.chancap))
self.failIf(self.checkCapability(self.justchanfoo, self.antichancap)) self.failIf(self.checkCapability(self.justchanfoo, self.antichancap))

View File

@ -50,7 +50,7 @@ class IrcMsgQueueTestCase(unittest.TestCase):
def testEmpty(self): def testEmpty(self):
q = irclib.IrcMsgQueue() q = irclib.IrcMsgQueue()
self.failIf(q) self.failIf(q)
def testEnqueueDequeue(self): def testEnqueueDequeue(self):
q = irclib.IrcMsgQueue() q = irclib.IrcMsgQueue()
q.enqueue(self.msg) q.enqueue(self.msg)
@ -131,7 +131,7 @@ class ChannelTestCase(unittest.TestCase):
self.failIf('quuz' in c.halfops) self.failIf('quuz' in c.halfops)
self.failIf('quuz' in c.voices) self.failIf('quuz' in c.voices)
class IrcStateTestCase(unittest.TestCase): class IrcStateTestCase(unittest.TestCase):
class FakeIrc: class FakeIrc:
nick = 'nick' nick = 'nick'
@ -171,11 +171,11 @@ class IrcStateTestCase(unittest.TestCase):
self.assertEqual(state1, state2) self.assertEqual(state1, state2)
except Exception: except Exception:
pass pass
""" """
def testChannels(self): def testChannels(self):
channel = channel =
state = irclib.IrcState() state = irclib.IrcState()
state.addMsg(self.irc, ircmsgs.join('#foo')) state.addMsg(self.irc, ircmsgs.join('#foo'))
""" """

View File

@ -126,7 +126,7 @@ class FunctionsTestCase(unittest.TestCase):
withException = ircmsgs.ban(channel, ban, exception) withException = ircmsgs.ban(channel, ban, exception)
self.assertEqual(ircutils.separateModes(withException.args[1:]), self.assertEqual(ircutils.separateModes(withException.args[1:]),
[('+b', ban), ('+e', exception)]) [('+b', ban), ('+e', exception)])
def testBans(self): def testBans(self):
channel = '#osu' channel = '#osu'
bans = ['*!*@*', 'jemfinch!*@*'] bans = ['*!*@*', 'jemfinch!*@*']

View File

@ -84,14 +84,14 @@ class FunctionsTestCase(unittest.TestCase):
self.assertEqual('\x03,5foo\x03', ircutils.mircColor(s, bg='brown')) self.assertEqual('\x03,5foo\x03', ircutils.mircColor(s, bg='brown'))
self.assertEqual('\x036,7foo\x03', self.assertEqual('\x036,7foo\x03',
ircutils.mircColor(s, bg='orange', fg='purple')) ircutils.mircColor(s, bg='orange', fg='purple'))
def testMircColors(self): def testMircColors(self):
# Make sure all (k, v) pairs are also (v, k) pairs. # Make sure all (k, v) pairs are also (v, k) pairs.
for (k, v) in ircutils.mircColors.items(): for (k, v) in ircutils.mircColors.items():
if k: if k:
self.assertEqual(ircutils.mircColors[v], k) self.assertEqual(ircutils.mircColors[v], k)
def testSafeArgument(self): def testSafeArgument(self):
s = 'I have been running for 9 seconds' s = 'I have been running for 9 seconds'
bolds = ircutils.bold(s) bolds = ircutils.bold(s)

View File

@ -63,8 +63,8 @@ class TestSchedule(unittest.TestCase):
self.assertEqual(i[0], 11) self.assertEqual(i[0], 11)
time.sleep(3) time.sleep(3)
self.assertEqual(i[0], 11) self.assertEqual(i[0], 11)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -183,7 +183,7 @@ class RingBufferTestCase(unittest.TestCase):
b = RingBuffer(100, range(10)) b = RingBuffer(100, range(10))
b1 = RingBuffer(10, range(10)) b1 = RingBuffer(10, range(10))
self.failIf(b == b1) self.failIf(b == b1)
def testIter(self): def testIter(self):
b = RingBuffer(3, range(3)) b = RingBuffer(3, range(3))
L = [] L = []
@ -214,7 +214,7 @@ class QueueTest(unittest.TestCase):
self.assertRaises(IndexError, q.__getitem__, -(n+1)) self.assertRaises(IndexError, q.__getitem__, -(n+1))
self.assertRaises(IndexError, q.__getitem__, n) self.assertRaises(IndexError, q.__getitem__, n)
self.assertEqual(q[3:7], queue([3, 4, 5, 6])) self.assertEqual(q[3:7], queue([3, 4, 5, 6]))
def testSetitem(self): def testSetitem(self):
q1 = queue() q1 = queue()
self.assertRaises(IndexError, q1.__setitem__, 0, 0) self.assertRaises(IndexError, q1.__setitem__, 0, 0)
@ -224,7 +224,7 @@ class QueueTest(unittest.TestCase):
for (i, elt) in enumerate(q2): for (i, elt) in enumerate(q2):
q2[i] = elt*2 q2[i] = elt*2
self.assertEqual([x*2 for x in q1], list(q2)) self.assertEqual([x*2 for x in q1], list(q2))
def testNonzero(self): def testNonzero(self):
q = queue() q = queue()
self.failIf(q, 'queue not zero after initialization') self.failIf(q, 'queue not zero after initialization')
@ -301,7 +301,7 @@ class QueueTest(unittest.TestCase):
self.assertEqual(q, eval(repr(q)), 'repr doesn\'t eval to same queue') self.assertEqual(q, eval(repr(q)), 'repr doesn\'t eval to same queue')
q.enqueue((1,)) q.enqueue((1,))
self.assertEqual(q, eval(repr(q)), 'repr doesn\'t eval to same queue') self.assertEqual(q, eval(repr(q)), 'repr doesn\'t eval to same queue')
def testEnqueueDequeue(self): def testEnqueueDequeue(self):
q = queue() q = queue()
self.assertRaises(IndexError, q.dequeue) self.assertRaises(IndexError, q.dequeue)
@ -360,7 +360,7 @@ class MaxLengthQueueTestCase(unittest.TestCase):
q = MaxLengthQueue(3, (1, 2, 3)) q = MaxLengthQueue(3, (1, 2, 3))
self.assertEqual(list(q), [1, 2, 3]) self.assertEqual(list(q), [1, 2, 3])
self.assertRaises(TypeError, MaxLengthQueue, 3, 1, 2, 3) self.assertRaises(TypeError, MaxLengthQueue, 3, 1, 2, 3)
def testMaxLength(self): def testMaxLength(self):
q = MaxLengthQueue(3) q = MaxLengthQueue(3)
q.enqueue(1) q.enqueue(1)