mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-01-11 12:42:34 +01:00
Rewrite most of Google to use the new AJAX API.
Add a translate command as well.
This commit is contained in:
parent
de69f9da17
commit
62a8306fbb
@ -1,83 +0,0 @@
|
||||
"""
|
||||
Facade that hides the differences between the SOAPpy and SOAP.py
|
||||
libraries, so that google.py doesn't have to deal with them.
|
||||
|
||||
@author: Brian Landers <brian@bluecoat93.org>
|
||||
@license: Python
|
||||
@version: 0.5.4
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
__author__ = "Brian Landers <brian@bluecoat93.org>"
|
||||
__version__ = "0.6"
|
||||
__license__ = "Python"
|
||||
|
||||
#
|
||||
# Wrapper around the python 'warnings' facility
|
||||
#
|
||||
def warn( message, level=RuntimeWarning ):
|
||||
warnings.warn( message, level, stacklevel=3 )
|
||||
|
||||
# We can't use older version of SOAPpy, due to bugs that break the Google API
|
||||
minSOAPpyVersion = "0.11.3"
|
||||
|
||||
#
|
||||
# Try loading SOAPpy first. If that fails, fall back to the old SOAP.py
|
||||
#
|
||||
SOAPpy = None
|
||||
try:
|
||||
import SOAPpy
|
||||
from SOAPpy import SOAPProxy, Types
|
||||
|
||||
if LooseVersion( minSOAPpyVersion ) > \
|
||||
LooseVersion( SOAPpy.version.__version__ ):
|
||||
|
||||
warn( "Versions of SOAPpy before %s have known bugs that prevent " +
|
||||
"PyGoogle from functioning." % minSOAPpyVersion )
|
||||
raise ImportError
|
||||
|
||||
except ImportError:
|
||||
try:
|
||||
import SOAP
|
||||
except ImportError:
|
||||
raise RuntimeError( "Unable to find SOAPpy or SOAP. Can't continue.\n" )
|
||||
|
||||
#
|
||||
# Constants that differ between the modules
|
||||
#
|
||||
if SOAPpy:
|
||||
false = Types.booleanType(0)
|
||||
true = Types.booleanType(1)
|
||||
structType = Types.structType
|
||||
faultType = Types.faultType
|
||||
else:
|
||||
false = SOAP.booleanType(0)
|
||||
true = SOAP.booleanType(1)
|
||||
structType = SOAP.structType
|
||||
faultType = SOAP.faultType
|
||||
|
||||
#
|
||||
# Get a SOAP Proxy object in the correct way for the module we're using
|
||||
#
|
||||
def getProxy( url, namespace, http_proxy ):
|
||||
if SOAPpy:
|
||||
return SOAPProxy( url,
|
||||
namespace = namespace,
|
||||
http_proxy = http_proxy )
|
||||
|
||||
else:
|
||||
return SOAP.SOAPProxy( url,
|
||||
namespace = namespace,
|
||||
http_proxy = http_proxy )
|
||||
|
||||
#
|
||||
# Convert an object to a dictionary in the proper way for the module
|
||||
# we're using for SOAP
|
||||
#
|
||||
def toDict( obj ):
|
||||
if SOAPpy:
|
||||
return obj._asdict()
|
||||
else:
|
||||
return obj._asdict
|
@ -1 +1,5 @@
|
||||
Insert a description of your plugin here, with any notes, etc. about using it.
|
||||
This is a simple plugin to provide access to the Google services we all know
|
||||
and love from our favorite IRC bot.
|
||||
|
||||
In order to use this plugin you must have the following modules installed:
|
||||
- simplejson: http://undefined.org/python/#simplejson
|
||||
|
@ -1,5 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2005, Jeremiah Fincher
|
||||
# Copyright (c) 2008, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -30,74 +31,50 @@
|
||||
import supybot.conf as conf
|
||||
import supybot.registry as registry
|
||||
|
||||
import google
|
||||
|
||||
def configure(advanced):
|
||||
from supybot.questions import output, expect, anything, something, yn
|
||||
output('To use Google\'t Web Services, you must have a license key.')
|
||||
if yn('Do you have a license key?'):
|
||||
key = something('What is it?')
|
||||
while len(key) != 32:
|
||||
output('That\'s not a valid Google license key.')
|
||||
if yn('Are you sure you have a valid Google license key?'):
|
||||
key = something('What is it?')
|
||||
else:
|
||||
key = ''
|
||||
break
|
||||
if key:
|
||||
conf.registerPlugin('Google', True)
|
||||
conf.supybot.plugins.Google.licenseKey.setValue(key)
|
||||
output("""The Google plugin has the functionality to watch for URLs
|
||||
that match a specific pattern. (We call this a snarfer)
|
||||
When supybot sees such a URL, it will parse the web page
|
||||
for information and reply with the results.
|
||||
from supybot.questions import output, yn
|
||||
conf.registerPlugin('Google', True)
|
||||
output("""The Google plugin has the functionality to watch for URLs
|
||||
that match a specific pattern. (We call this a snarfer)
|
||||
When supybot sees such a URL, it will parse the web page
|
||||
for information and reply with the results.
|
||||
|
||||
Google has two available snarfers: Google Groups link
|
||||
snarfing and a google search snarfer.""")
|
||||
if yn('Do you want the Google Groups link snarfer enabled by '
|
||||
'default?'):
|
||||
conf.supybot.plugins.Google.groupsSnarfer.setValue(True)
|
||||
if yn('Do you want the Google search snarfer enabled by default?'):
|
||||
conf.supybot.plugins.Google.searchSnarfer.setValue(True)
|
||||
else:
|
||||
output("""You'll need to get a key before you can use this plugin.
|
||||
You can apply for a key at
|
||||
http://code.google.com/apis/soapsearch/""")
|
||||
|
||||
class LicenseKey(registry.String):
|
||||
def setValue(self, s):
|
||||
if s and len(s) != 32:
|
||||
raise registry.InvalidRegistryValue, 'Invalid Google license key.'
|
||||
try:
|
||||
s = s or ''
|
||||
registry.String.setValue(self, s)
|
||||
if s:
|
||||
google.setLicense(self.value)
|
||||
except AttributeError:
|
||||
if world and not world.dying: # At shutdown world can be None.
|
||||
raise callbacks.Error, \
|
||||
'It appears that the initial import of ' \
|
||||
'our underlying google.py module has ' \
|
||||
'failed. Once the cause of that problem ' \
|
||||
'has been diagnosed and fixed, the bot ' \
|
||||
'will need to be restarted in order to ' \
|
||||
'load this plugin.'
|
||||
Google has two available snarfers: Google Groups link
|
||||
snarfing and a google search snarfer.""")
|
||||
if yn('Do you want the Google Groups link snarfer enabled by '
|
||||
'default?'):
|
||||
conf.supybot.plugins.Google.groupsSnarfer.setValue(True)
|
||||
if yn('Do you want the Google search snarfer enabled by default?'):
|
||||
conf.supybot.plugins.Google.searchSnarfer.setValue(True)
|
||||
|
||||
class Language(registry.OnlySomeStrings):
|
||||
validStrings = ['lang_' + s for s in 'ar zh-CN zh-TW cs da nl en et fi fr '
|
||||
'de el iw hu is it ja ko lv lt no pt '
|
||||
'pl ro ru es sv tr'.split()]
|
||||
validStrings = ['lang_' + s for s in 'ar bg ca zh-CN zh-TW hr cs da nl en '
|
||||
'et fi fr de el iw hu is id it ja ko '
|
||||
'lv lt no pl pt ro ru sr sk sl es sv '
|
||||
'tr'.split()]
|
||||
validStrings.append('')
|
||||
def normalize(self, s):
|
||||
if not s.startswith('lang_'):
|
||||
s = 'lang_' + s
|
||||
if not s.endswith('CN') or s.endswith('TW'):
|
||||
s = s.lower()
|
||||
else:
|
||||
s = s.lower()[:-2] + s[-2:]
|
||||
s = s[:-2].lower() + s[-2:]
|
||||
return s
|
||||
|
||||
class NumSearchResults(registry.PositiveInteger):
|
||||
"""Value must be 1 <= n <= 8"""
|
||||
def setValue(self, v):
|
||||
if v > 8:
|
||||
self.error()
|
||||
super(NumSearchResults, self).setValue(v)
|
||||
|
||||
class SafeSearch(registry.OnlySomeStrings):
|
||||
validStrings = ['active', 'moderate', 'off']
|
||||
|
||||
Google = conf.registerPlugin('Google')
|
||||
conf.registerGlobalValue(Google, 'referer',
|
||||
registry.String('', """Determines the URL that will be sent to Google for
|
||||
the Referer field of the search requests. If this value is empty, a
|
||||
Referer will be generated in the following format:
|
||||
http://$server/$botName"""))
|
||||
conf.registerChannelValue(Google, 'groupsSnarfer',
|
||||
registry.Boolean(False, """Determines whether the groups snarfer is
|
||||
enabled. If so, URLs at groups.google.com will be snarfed and their
|
||||
@ -113,25 +90,14 @@ conf.registerChannelValue(Google, 'colorfulFilter',
|
||||
conf.registerChannelValue(Google, 'bold',
|
||||
registry.Boolean(True, """Determines whether results are bolded."""))
|
||||
conf.registerChannelValue(Google, 'maximumResults',
|
||||
registry.PositiveInteger(10, """Determines the maximum number of results
|
||||
returned from the google command."""))
|
||||
NumSearchResults(8, """Determines the maximum number of results returned
|
||||
from the google command."""))
|
||||
conf.registerChannelValue(Google, 'defaultLanguage',
|
||||
Language('lang_en', """Determines what default language is used in
|
||||
searches. If left empty, no specific language will be requested."""))
|
||||
conf.registerChannelValue(Google, 'safeSearch',
|
||||
registry.Boolean(True, "Determines whether safeSearch is on by default."))
|
||||
conf.registerGlobalValue(Google, 'licenseKey',
|
||||
LicenseKey('', """Sets the Google license key for using Google's Web
|
||||
Services API. This is necessary before you can do any searching with this
|
||||
module.""", private=True))
|
||||
|
||||
conf.registerGroup(Google, 'state')
|
||||
conf.registerGlobalValue(Google.state, 'searches',
|
||||
registry.Integer(0, """Used to keep the total number of searches Google has
|
||||
done for this bot. You shouldn't modify this."""))
|
||||
conf.registerGlobalValue(Google.state, 'time',
|
||||
registry.Float(0.0, """Used to keep the total amount of time Google has
|
||||
spent searching for this bot. You shouldn't modify this."""))
|
||||
|
||||
SafeSearch('moderate', """Determines what level of safeSearch to use by
|
||||
default. 'active' - most filtering, 'moderate' - default filtering, 'off'
|
||||
- no filtering"""))
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
||||
|
@ -1,641 +0,0 @@
|
||||
"""
|
||||
Python wrapper for Google web APIs
|
||||
|
||||
This module allows you to access Google's web APIs through SOAP,
|
||||
to do things like search Google and get the results programmatically.
|
||||
Described U{here <http://code.google.com/apis/soapsearch/>}
|
||||
|
||||
You need a Google-provided license key to use these services.
|
||||
Follow the link above to get one. These functions will look in
|
||||
several places (in this order) for the license key:
|
||||
|
||||
- the "license_key" argument of each function
|
||||
- the module-level LICENSE_KEY variable (call setLicense once to set it)
|
||||
- an environment variable called GOOGLE_LICENSE_KEY
|
||||
- a file called ".googlekey" 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.txt" in your home directory
|
||||
- a file called ".googlekey" in the same directory as google.py
|
||||
- a file called "googlekey.txt" in the same directory as google.py
|
||||
|
||||
Sample usage::
|
||||
|
||||
>>> import google
|
||||
>>> google.setLicense('...') # must get your own key!
|
||||
>>> data = google.doGoogleSearch('python')
|
||||
>>> data.meta.searchTime
|
||||
0.043221000000000002
|
||||
|
||||
>>> data.results[0].URL
|
||||
'http://www.python.org/'
|
||||
|
||||
>>> data.results[0].title
|
||||
'<b>Python</b> Language Website'
|
||||
|
||||
@newfield contrib: Contributors
|
||||
@author: Mark Pilgrim <f8dy@diveintomark.org>
|
||||
@author: Brian Landers <brian@bluecoat93.org>
|
||||
@license: Python
|
||||
@version: 0.6
|
||||
@contrib: David Ascher, for the install script
|
||||
@contrib: Erik Max Francis, for the command line interface
|
||||
@contrib: Michael Twomey, for HTTP proxy support
|
||||
@contrib: Mark Recht, for patches to support SOAPpy
|
||||
"""
|
||||
|
||||
__author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
|
||||
__version__ = "0.6"
|
||||
__cvsversion__ = "$Revision: 1.6 $"[11:-2]
|
||||
__date__ = "$Date: 2004/09/30 08:09:09 $"[7:-2]
|
||||
__copyright__ = "Copyright (c) 2002 Mark Pilgrim"
|
||||
__license__ = "Python"
|
||||
__credits__ = """David Ascher, for the install script
|
||||
Erik Max Francis, for the command line interface
|
||||
Michael Twomey, for HTTP proxy support"""
|
||||
|
||||
import os, sys, getopt
|
||||
import GoogleSOAPFacade
|
||||
|
||||
LICENSE_KEY = None
|
||||
HTTP_PROXY = None
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
_url = 'http://api.google.com/search/beta2'
|
||||
_namespace = 'urn:GoogleSearch'
|
||||
_googlefile1 = ".googlekey"
|
||||
_googlefile2 = "googlekey.txt"
|
||||
|
||||
_false = GoogleSOAPFacade.false
|
||||
_true = GoogleSOAPFacade.true
|
||||
|
||||
_licenseLocations = (
|
||||
( 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: 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(), _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', '' ), _googlefile2 ),
|
||||
'%s in your home directory' % _googlefile2 ),
|
||||
( lambda key: _contentsOf( _getScriptDir(), _googlefile1 ),
|
||||
'%s in the google.py directory' % _googlefile1 ),
|
||||
( lambda key: _contentsOf( _getScriptDir(), _googlefile2 ),
|
||||
'%s in the google.py directory' % _googlefile2 )
|
||||
)
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## Exceptions
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class NoLicenseKey(Exception):
|
||||
"""
|
||||
Thrown when the API is unable to find a valid license key.
|
||||
"""
|
||||
pass
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## administrative functions (non-API)
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def _version():
|
||||
"""
|
||||
Display a formatted version string for the module
|
||||
"""
|
||||
print """PyGoogle %(__version__)s
|
||||
%(__copyright__)s
|
||||
released %(__date__)s
|
||||
|
||||
Thanks to:
|
||||
%(__credits__)s""" % globals()
|
||||
|
||||
|
||||
def _usage():
|
||||
"""
|
||||
Display usage information for the command-line interface
|
||||
"""
|
||||
program = os.path.basename(sys.argv[0])
|
||||
print """Usage: %(program)s [options] [querytype] query
|
||||
|
||||
options:
|
||||
-k, --key= <license key> Google license key (see important note below)
|
||||
-1, -l, --lucky show only first hit
|
||||
-m, --meta show meta information
|
||||
-r, --reverse show results in reverse order
|
||||
-x, --proxy= <url> use HTTP proxy
|
||||
-h, --help print this help
|
||||
-v, --version print version and copyright information
|
||||
-t, --test run test queries
|
||||
|
||||
querytype:
|
||||
-s, --search= <query> search (default)
|
||||
-c, --cache= <url> retrieve cached page
|
||||
-p, --spelling= <word> check spelling
|
||||
|
||||
IMPORTANT NOTE: all Google functions require a valid license key;
|
||||
visit http://code.google.com/apis/soapsearch/ to get one. %(program)s will
|
||||
look in these places (in order) and use the first license key it finds:
|
||||
* the key specified on the command line""" % vars()
|
||||
for get, location in _licenseLocations[2:]:
|
||||
print " *", location
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## utility functions (API)
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def setLicense(license_key):
|
||||
"""
|
||||
Set the U{Google APIs <http://code.google.com/apis/soapsearch/>} license
|
||||
key
|
||||
|
||||
@param license_key: The new key to use
|
||||
@type license_key: String
|
||||
@todo: validate the key?
|
||||
"""
|
||||
global LICENSE_KEY
|
||||
LICENSE_KEY = license_key
|
||||
|
||||
|
||||
def getLicense(license_key = None):
|
||||
"""
|
||||
Get the U{Google APIs <http://code.google.com/apis/soapsearch/>} license
|
||||
key
|
||||
|
||||
The key can be read from any number of locations. See the module-leve
|
||||
documentation for the search order.
|
||||
|
||||
@return: the license key
|
||||
@rtype: String
|
||||
@raise NoLicenseKey: if no valid key could be found
|
||||
"""
|
||||
for get, location in _licenseLocations:
|
||||
rc = get(license_key)
|
||||
if rc: return rc
|
||||
_usage()
|
||||
raise NoLicenseKey, 'get a license key at '\
|
||||
'http://code.google.com/apis/soapsearch/'
|
||||
|
||||
|
||||
def setProxy(http_proxy):
|
||||
"""
|
||||
Set the HTTP proxy to be used when accessing Google
|
||||
|
||||
@param http_proxy: the proxy to use
|
||||
@type http_proxy: String
|
||||
@todo: validiate the input?
|
||||
"""
|
||||
global HTTP_PROXY
|
||||
HTTP_PROXY = http_proxy
|
||||
|
||||
|
||||
def getProxy(http_proxy = None):
|
||||
"""
|
||||
Get the HTTP proxy we use for accessing Google
|
||||
|
||||
@return: the proxy
|
||||
@rtype: String
|
||||
"""
|
||||
return http_proxy or HTTP_PROXY
|
||||
|
||||
|
||||
def _contentsOf(dirname, filename):
|
||||
filename = os.path.join(dirname, filename)
|
||||
if not os.path.exists(filename): return None
|
||||
fsock = open(filename)
|
||||
contents = fsock.read()
|
||||
fsock.close()
|
||||
return contents
|
||||
|
||||
|
||||
def _getScriptDir():
|
||||
if __name__ == '__main__':
|
||||
return os.path.abspath(os.path.dirname(sys.argv[0]))
|
||||
else:
|
||||
return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__))
|
||||
|
||||
|
||||
def _marshalBoolean(value):
|
||||
if value:
|
||||
return _true
|
||||
else:
|
||||
return _false
|
||||
|
||||
|
||||
def _getRemoteServer( http_proxy ):
|
||||
return GoogleSOAPFacade.getProxy( _url, _namespace, http_proxy )
|
||||
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## search results classes
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class _SearchBase:
|
||||
def __init__(self, params):
|
||||
for k, v in params.items():
|
||||
if isinstance(v, GoogleSOAPFacade.structType):
|
||||
v = GoogleSOAPFacade.toDict( v )
|
||||
|
||||
try:
|
||||
if isinstance(v[0], GoogleSOAPFacade.structType):
|
||||
v = [ SOAPProxy.toDict( node ) for node in v ]
|
||||
|
||||
except:
|
||||
pass
|
||||
self.__dict__[str(k)] = v
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class SearchResultsMetaData(_SearchBase):
|
||||
"""
|
||||
Container class for metadata about a given search query's results.
|
||||
|
||||
@ivar documentFiltering: is duplicate page filtering active?
|
||||
|
||||
@ivar searchComments: human-readable informational message
|
||||
|
||||
example::
|
||||
|
||||
"'the' is a very common word and was not included in your search"
|
||||
|
||||
@ivar estimatedTotalResultsCount: estimated total number of results
|
||||
for this query.
|
||||
|
||||
@ivar estimateIsExact: is estimatedTotalResultsCount an exact value?
|
||||
|
||||
@ivar searchQuery: search string that initiated this search
|
||||
|
||||
@ivar startIndex: index of the first result returned (zero-based)
|
||||
|
||||
@ivar endIndex: index of the last result returned (zero-based)
|
||||
|
||||
@ivar searchTips: human-readable informational message on how to better
|
||||
use Google.
|
||||
|
||||
@ivar directoryCategories: list of categories for the search results
|
||||
|
||||
This field is a list of dictionaries, like so::
|
||||
|
||||
{ 'fullViewableName': 'the Open Directory category',
|
||||
'specialEncoding': 'encoding scheme of this directory category'
|
||||
}
|
||||
|
||||
@ivar searchTime: total search time, in seconds
|
||||
"""
|
||||
pass
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class SearchResult(_SearchBase):
|
||||
"""
|
||||
Encapsulates the results from a search.
|
||||
|
||||
@ivar URL: URL
|
||||
|
||||
@ivar title: title (HTML)
|
||||
|
||||
@ivar snippet: snippet showing query context (HTML
|
||||
|
||||
@ivar cachedSize: size of cached version of this result, (KB)
|
||||
|
||||
@ivar relatedInformationPresent: is the "related:" keyword supported?
|
||||
|
||||
Flag indicates that the "related:" keyword is supported for this URL
|
||||
|
||||
@ivar hostName: used when filtering occurs
|
||||
|
||||
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 the host name in this parameter.
|
||||
|
||||
@ivar directoryCategory: Open Directory category information
|
||||
|
||||
This field is a dictionary with the following values::
|
||||
|
||||
{ 'fullViewableName': 'the Open Directory category',
|
||||
'specialEncoding' : 'encoding scheme of this directory category'
|
||||
}
|
||||
|
||||
@ivar directoryTitle: Open Directory title of this result (or blank)
|
||||
|
||||
@ivar summary: Open Directory summary for this result (or blank)
|
||||
"""
|
||||
pass
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class SearchReturnValue:
|
||||
"""
|
||||
complete search results for a single query
|
||||
|
||||
@ivar meta: L{SearchResultsMetaData} instance for this query
|
||||
|
||||
@ivar results: list of L{SearchResult} objects for this query
|
||||
"""
|
||||
def __init__( self, metadata, results ):
|
||||
self.meta = metadata
|
||||
self.results = results
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## main functions
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def doGoogleSearch( q, start = 0, maxResults = 10, filter = 1,
|
||||
restrict='', safeSearch = 0, language = '',
|
||||
inputencoding = '', outputencoding = '',\
|
||||
license_key = None, http_proxy = None ):
|
||||
"""
|
||||
Search Google using the SOAP API and return the results.
|
||||
|
||||
You need a license key to call this function; see the U{Google APIs
|
||||
<http://code.google.com/apis/soapsearch/>} site to get one. Then you can
|
||||
either pass it to this function every time, or set it globally; see the
|
||||
L{google} module-level docs for details.
|
||||
|
||||
See U{http://www.google.com/help/features.html}
|
||||
for examples of advanced features. Anything that works at the
|
||||
Google web site will work as a query string in this method.
|
||||
|
||||
You can use the C{start} and C{maxResults} parameters to page
|
||||
through multiple pages of results. Note that 'maxResults' is
|
||||
currently limited by Google to 10.
|
||||
|
||||
See the API reference for more advanced examples and a full list of
|
||||
country codes and topics for use in the C{restrict} parameter, along
|
||||
with legal values for the C{language}, C{inputencoding}, and
|
||||
C{outputencoding} parameters.
|
||||
|
||||
You can download the API documentation
|
||||
U{http://code.google.com/apis/soapsearch/download.html <here>}.
|
||||
|
||||
@param q: search string.
|
||||
@type q: String
|
||||
|
||||
@param start: (optional) zero-based index of first desired result.
|
||||
@type start: int
|
||||
|
||||
@param maxResults: (optional) maximum number of results to return.
|
||||
@type maxResults: int
|
||||
|
||||
@param filter: (optional) flag to request filtering of similar results
|
||||
@type filter: int
|
||||
|
||||
@param restrict: (optional) restrict results by country or topic.
|
||||
@type restrict: String
|
||||
|
||||
@param safeSearch: (optional)
|
||||
@type safeSearch: int
|
||||
|
||||
@param language: (optional)
|
||||
@type language: String
|
||||
|
||||
@param inputencoding: (optional)
|
||||
@type inputencoding: String
|
||||
|
||||
@param outputencoding: (optional)
|
||||
@type outputencoding: String
|
||||
|
||||
@param license_key: (optional) the Google API license key to use
|
||||
@type license_key: String
|
||||
|
||||
@param http_proxy: (optional) the HTTP proxy to use for talking to Google
|
||||
@type http_proxy: String
|
||||
|
||||
@return: the search results encapsulated in an object
|
||||
@rtype: L{SearchReturnValue}
|
||||
"""
|
||||
license_key = getLicense( license_key )
|
||||
http_proxy = getProxy( http_proxy )
|
||||
remoteserver = _getRemoteServer( http_proxy )
|
||||
|
||||
filter = _marshalBoolean( filter )
|
||||
safeSearch = _marshalBoolean( safeSearch )
|
||||
|
||||
data = remoteserver.doGoogleSearch( license_key, q, start, maxResults,
|
||||
filter, restrict, safeSearch,
|
||||
language, inputencoding,
|
||||
outputencoding )
|
||||
|
||||
metadata = GoogleSOAPFacade.toDict( data )
|
||||
del metadata["resultElements"]
|
||||
|
||||
metadata = SearchResultsMetaData( metadata )
|
||||
|
||||
results = [ SearchResult( GoogleSOAPFacade.toDict( node ) ) \
|
||||
for node in data.resultElements ]
|
||||
|
||||
return SearchReturnValue( metadata, results )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def doGetCachedPage( url, license_key = None, http_proxy = None ):
|
||||
"""
|
||||
Retrieve a page from the Google cache.
|
||||
|
||||
You need a license key to call this function; see the
|
||||
U{Google APIs <http://code.google.com/apis/soapsearch/>} site to get one.
|
||||
Then you can either pass it to this function every time, or
|
||||
set it globally; see the L{google} module-level docs for details.
|
||||
|
||||
@param url: full URL to the page to retrieve
|
||||
@type url: String
|
||||
|
||||
@param license_key: (optional) the Google API key to use
|
||||
@type license_key: String
|
||||
|
||||
@param http_proxy: (optional) the HTTP proxy server to use
|
||||
@type http_proxy: String
|
||||
|
||||
@return: full text of the cached page
|
||||
@rtype: String
|
||||
"""
|
||||
license_key = getLicense( license_key )
|
||||
http_proxy = getProxy( http_proxy )
|
||||
remoteserver = _getRemoteServer( http_proxy )
|
||||
|
||||
return remoteserver.doGetCachedPage( license_key, url )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def doSpellingSuggestion( phrase, license_key = None, http_proxy = None ):
|
||||
"""
|
||||
Get spelling suggestions from Google
|
||||
|
||||
You need a license key to call this function; see the
|
||||
U{Google APIs <http://code.google.com/apis/soapsearch/>} site to get one.
|
||||
Then you can either pass it to this function every time, or
|
||||
set it globally; see the L{google} module-level docs for details.
|
||||
|
||||
@param phrase: word or phrase to spell-check
|
||||
@type phrase: String
|
||||
|
||||
@param license_key: (optional) the Google API key to use
|
||||
@type license_key: String
|
||||
|
||||
@param http_proxy: (optional) the HTTP proxy to use
|
||||
@type http_proxy: String
|
||||
|
||||
@return: text of any suggested replacement, or None
|
||||
"""
|
||||
license_key = getLicense( license_key )
|
||||
http_proxy = getProxy( http_proxy)
|
||||
remoteserver = _getRemoteServer( http_proxy )
|
||||
|
||||
return remoteserver.doSpellingSuggestion( license_key, phrase )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## functional test suite (see googletest.py for unit test suite)
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
def _test():
|
||||
"""
|
||||
Run functional test suite.
|
||||
"""
|
||||
try:
|
||||
getLicense(None)
|
||||
except NoLicenseKey:
|
||||
return
|
||||
|
||||
print "Searching for Python at google.com..."
|
||||
data = doGoogleSearch( "Python" )
|
||||
_output( data, { "func": "doGoogleSearch"} )
|
||||
|
||||
print "\nSearching for 5 _French_ pages about Python, "
|
||||
print "encoded in ISO-8859-1..."
|
||||
|
||||
data = doGoogleSearch( "Python", language = 'lang_fr',
|
||||
outputencoding = 'ISO-8859-1',
|
||||
maxResults = 5 )
|
||||
|
||||
_output( data, { "func": "doGoogleSearch" } )
|
||||
|
||||
phrase = "Pyhton programming languager"
|
||||
print "\nTesting spelling suggestions for '%s'..." % phrase
|
||||
|
||||
data = doSpellingSuggestion( phrase )
|
||||
|
||||
_output( data, { "func": "doSpellingSuggestion" } )
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## Command-line interface
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
class _OutputFormatter:
|
||||
def boil(self, data):
|
||||
if type(data) == type(u""):
|
||||
return data.encode("ISO-8859-1", "replace")
|
||||
else:
|
||||
return data
|
||||
|
||||
class _TextOutputFormatter(_OutputFormatter):
|
||||
def common(self, data, params):
|
||||
if params.get("showMeta", 0):
|
||||
meta = data.meta
|
||||
for category in meta.directoryCategories:
|
||||
print "directoryCategory: %s" % \
|
||||
self.boil(category["fullViewableName"])
|
||||
for attr in [node for node in dir(meta) if \
|
||||
node <> "directoryCategories" and node[:2] <> '__']:
|
||||
print "%s:" % attr, self.boil(getattr(meta, attr))
|
||||
|
||||
def doGoogleSearch(self, data, params):
|
||||
results = data.results
|
||||
if params.get("feelingLucky", 0):
|
||||
results = results[:1]
|
||||
if params.get("reverseOrder", 0):
|
||||
results.reverse()
|
||||
for result in results:
|
||||
for attr in dir(result):
|
||||
if attr == "directoryCategory":
|
||||
print "directoryCategory:", \
|
||||
self.boil(result.directoryCategory["fullViewableName"])
|
||||
elif attr[:2] <> '__':
|
||||
print "%s:" % attr, self.boil(getattr(result, attr))
|
||||
print
|
||||
self.common(data, params)
|
||||
|
||||
def doGetCachedPage(self, data, params):
|
||||
print data
|
||||
self.common(data, params)
|
||||
|
||||
doSpellingSuggestion = doGetCachedPage
|
||||
|
||||
def _makeFormatter(outputFormat):
|
||||
classname = "_%sOutputFormatter" % outputFormat.capitalize()
|
||||
return globals()[classname]()
|
||||
|
||||
def _output(results, params):
|
||||
formatter = _makeFormatter(params.get("outputFormat", "text"))
|
||||
outputmethod = getattr(formatter, params["func"])
|
||||
outputmethod(results, params)
|
||||
|
||||
def main(argv):
|
||||
"""
|
||||
Command-line interface.
|
||||
"""
|
||||
if not argv:
|
||||
_usage()
|
||||
return
|
||||
q = None
|
||||
func = None
|
||||
http_proxy = None
|
||||
license_key = None
|
||||
feelingLucky = 0
|
||||
showMeta = 0
|
||||
reverseOrder = 0
|
||||
runTest = 0
|
||||
outputFormat = "text"
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "s:c:p:k:lmrx:hvt1",
|
||||
["search=", "cache=", "spelling=", "key=", "lucky", "meta",
|
||||
"reverse", "proxy=", "help", "version", "test"])
|
||||
except getopt.GetoptError:
|
||||
_usage()
|
||||
sys.exit(2)
|
||||
for opt, arg in opts:
|
||||
if opt in ("-s", "--search"):
|
||||
q = arg
|
||||
func = "doGoogleSearch"
|
||||
elif opt in ("-c", "--cache"):
|
||||
q = arg
|
||||
func = "doGetCachedPage"
|
||||
elif opt in ("-p", "--spelling"):
|
||||
q = arg
|
||||
func = "doSpellingSuggestion"
|
||||
elif opt in ("-k", "--key"):
|
||||
license_key = arg
|
||||
elif opt in ("-l", "-1", "--lucky"):
|
||||
feelingLucky = 1
|
||||
elif opt in ("-m", "--meta"):
|
||||
showMeta = 1
|
||||
elif opt in ("-r", "--reverse"):
|
||||
reverseOrder = 1
|
||||
elif opt in ("-x", "--proxy"):
|
||||
http_proxy = arg
|
||||
elif opt in ("-h", "--help"):
|
||||
_usage()
|
||||
elif opt in ("-v", "--version"):
|
||||
_version()
|
||||
elif opt in ("-t", "--test"):
|
||||
runTest = 1
|
||||
if runTest:
|
||||
setLicense(license_key)
|
||||
setProxy(http_proxy)
|
||||
_test()
|
||||
if args and not q:
|
||||
q = args[0]
|
||||
func = "doGoogleSearch"
|
||||
if func:
|
||||
results = globals()[func]( q, http_proxy=http_proxy,
|
||||
license_key=license_key )
|
||||
_output(results, locals())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
@ -1,5 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||
# Copyright (c) 2008, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -32,10 +33,8 @@ import cgi
|
||||
import time
|
||||
import socket
|
||||
import urllib
|
||||
import xml.sax
|
||||
|
||||
import SOAP
|
||||
import google
|
||||
import simplejson
|
||||
|
||||
import supybot.conf as conf
|
||||
import supybot.utils as utils
|
||||
@ -45,74 +44,10 @@ import supybot.ircmsgs as ircmsgs
|
||||
import supybot.ircutils as ircutils
|
||||
import supybot.callbacks as callbacks
|
||||
|
||||
def search(log, queries, **kwargs):
|
||||
# We have to keep stats here, rather than in formatData or elsewhere,
|
||||
# because not all searching functions use formatData -- fight, lucky, etc.
|
||||
assert not isinstance(queries, basestring), 'Old code: queries is a list.'
|
||||
try:
|
||||
for (i, query) in enumerate(queries):
|
||||
if len(query.split(None, 1)) > 1:
|
||||
queries[i] = repr(query)
|
||||
proxy = conf.supybot.protocols.http.proxy()
|
||||
if proxy:
|
||||
kwargs['http_proxy'] = proxy
|
||||
query = ' '.join(queries).decode('utf-8')
|
||||
data = google.doGoogleSearch(query, **kwargs)
|
||||
searches = conf.supybot.plugins.Google.state.searches() + 1
|
||||
conf.supybot.plugins.Google.state.searches.setValue(searches)
|
||||
time = conf.supybot.plugins.Google.state.time() + data.meta.searchTime
|
||||
conf.supybot.plugins.Google.state.time.setValue(time)
|
||||
last24hours.enqueue(None)
|
||||
return data
|
||||
except socket.error, e:
|
||||
if e.args[0] == 110:
|
||||
raise callbacks.Error, 'Connection timed out to Google.com.'
|
||||
else:
|
||||
raise callbacks.Error, 'Error connecting to Google.com.'
|
||||
except SOAP.HTTPError, e:
|
||||
log.info('HTTP Error accessing Google: %s', e)
|
||||
raise callbacks.Error, 'Error connecting to Google.com.'
|
||||
except SOAP.faultType, e:
|
||||
if 'Invalid authorization key' in e.faultstring:
|
||||
raise callbacks.Error, 'Invalid Google license key.'
|
||||
elif 'Problem looking up user record' in e.faultstring:
|
||||
raise callbacks.Error, \
|
||||
'Google seems to be having trouble looking up the user for '\
|
||||
'your license key. This probably isn\'t a problem on your '\
|
||||
'side; it\'s probably a bug on Google\'s side. It seems '\
|
||||
'to happen intermittently.'
|
||||
else:
|
||||
log.exception('Unexpected SOAPpy error:')
|
||||
raise callbacks.Error, \
|
||||
'Unexpected error from Google; please report this to the ' \
|
||||
'Supybot developers.'
|
||||
except xml.sax.SAXException, e:
|
||||
log.exception('Uncaught SAX error:')
|
||||
raise callbacks.Error, 'Google returned an unparsable response. ' \
|
||||
'The full traceback has been logged.'
|
||||
# We don't use SOAPpy anymore, apparently.
|
||||
## except SOAPpy.Error, e:
|
||||
## log.exception('Uncaught SOAP exception in Google.search:')
|
||||
## raise callbacks.Error, 'Error connecting to Google.com.'
|
||||
|
||||
last24hours = utils.structures.TimeoutQueue(86400)
|
||||
totalTime = conf.supybot.plugins.Google.state.time()
|
||||
searches = conf.supybot.plugins.Google.state.searches()
|
||||
|
||||
class Google(callbacks.PluginRegexp):
|
||||
threaded = True
|
||||
callBefore = ['Web']
|
||||
regexps = ['googleSnarfer', 'googleGroups']
|
||||
def __init__(self, irc):
|
||||
self.__parent = super(Google, self)
|
||||
self.__parent.__init__(irc)
|
||||
google.setLicense(self.registryValue('licenseKey'))
|
||||
|
||||
def callCommand(self, command, irc, msg, *args, **kwargs):
|
||||
try:
|
||||
self.__parent.callCommand(command, irc, msg, *args, **kwargs)
|
||||
except xml.sax.SAXReaderNotAvailable, e:
|
||||
irc.error('No XML parser available.')
|
||||
|
||||
_colorGoogles = {}
|
||||
def _getColorGoogle(self, m):
|
||||
@ -139,16 +74,61 @@ class Google(callbacks.PluginRegexp):
|
||||
msg = ircmsgs.privmsg(msg.args[0], s, msg=msg)
|
||||
return msg
|
||||
|
||||
_gsearchUrl = 'http://ajax.googleapis.com/ajax/services/search/web'
|
||||
def search(self, query, channel, options={}):
|
||||
"""Perform a search using Google's AJAX API.
|
||||
search("search phrase", options={})
|
||||
|
||||
Valid options are:
|
||||
smallsearch - True/False (Default: False)
|
||||
safesearch - {active,moderate,off} (Default: "moderate")
|
||||
language - Restrict search to documents in the given language
|
||||
(Default: "lang_en")
|
||||
"""
|
||||
ref = self.registryValue('referer')
|
||||
if not ref:
|
||||
ref = 'http://%s/%s' % (dynamic.irc.server,
|
||||
dynamic.irc.nick)
|
||||
headers = utils.web.defaultHeaders
|
||||
headers['Referer'] = ref
|
||||
opts = {'q': query, 'v': '1.0'}
|
||||
for (k, v) in options.iteritems():
|
||||
if k == 'smallsearch':
|
||||
if v:
|
||||
opts['rsz'] = 'small'
|
||||
else:
|
||||
opts['rsz'] = 'large'
|
||||
elif k == 'safesearch':
|
||||
opts['safe'] = v
|
||||
elif k == 'language':
|
||||
opts['lr'] = v
|
||||
defLang = self.registryValue('defaultLanguage', channel)
|
||||
if 'lr' not in opts and defLang:
|
||||
opts['lr'] = defLang
|
||||
if 'safe' not in opts:
|
||||
opts['safe'] = self.registryValue('safeSearch', dynamic.channel)
|
||||
if 'rsz' not in opts:
|
||||
opts['safe'] = 'large'
|
||||
|
||||
fd = utils.web.getUrlFd('%s?%s' % (self._gsearchUrl,
|
||||
urllib.urlencode(opts)),
|
||||
headers)
|
||||
json = simplejson.load(fd)
|
||||
fd.close()
|
||||
if json['responseStatus'] != 200:
|
||||
raise callbacks.Error, 'We broke The Google!'
|
||||
return json
|
||||
|
||||
def formatData(self, data, bold=True, max=0):
|
||||
if isinstance(data, basestring):
|
||||
return data
|
||||
t = format('Search took %.2f seconds', data.meta.searchTime)
|
||||
results = []
|
||||
if max:
|
||||
data.results = data.results[:max]
|
||||
for result in data.results:
|
||||
title = utils.web.htmlToText(result.title.encode('utf-8'))
|
||||
url = result.URL
|
||||
data = data[:max]
|
||||
for result in data:
|
||||
title = utils.web.htmlToText(result['titleNoFormatting']\
|
||||
.encode('utf-8'))
|
||||
url = result['unescapedUrl']
|
||||
if title:
|
||||
if bold:
|
||||
title = ircutils.bold(title)
|
||||
@ -156,107 +136,58 @@ class Google(callbacks.PluginRegexp):
|
||||
else:
|
||||
results.append(url)
|
||||
if not results:
|
||||
return format('No matches found. (%s)', t)
|
||||
return format('No matches found.')
|
||||
else:
|
||||
return format('%s: %s', t, '; '.join(results))
|
||||
return format('; '.join(results))
|
||||
|
||||
def lucky(self, irc, msg, args, text):
|
||||
"""<search>
|
||||
|
||||
Does a google search, but only returns the first result.
|
||||
"""
|
||||
data = search(self.log, text)
|
||||
if data.results:
|
||||
url = data.results[0].URL
|
||||
irc.reply(url)
|
||||
data = self.search(text, msg.args[0], {'smallsearch': True})
|
||||
if data['responseData']['results']:
|
||||
url = data['responseData']['results'][0]['unescapedUrl']
|
||||
irc.reply(url.encode('utf-8'))
|
||||
else:
|
||||
irc.reply('Google found nothing.')
|
||||
lucky = wrap(lucky, [many('something')])
|
||||
lucky = wrap(lucky, ['text'])
|
||||
|
||||
def google(self, irc, msg, args, optlist, text):
|
||||
"""<search> [--{language,restrict} <value>] [--{notsafe,similar}]
|
||||
"""<search> [--{safesearch,language} <value>]
|
||||
|
||||
Searches google.com for the given string. As many results as can fit
|
||||
are included. --language accepts a language abbreviation; --restrict
|
||||
restricts the results to certain classes of things; --similar tells
|
||||
Google not to filter similar results. --notsafe allows possibly
|
||||
work-unsafe results.
|
||||
are included. --language accepts a language abbreviation; --safesearch
|
||||
accepts a filtering level ('active', 'moderate', 'off').
|
||||
"""
|
||||
kwargs = {}
|
||||
if self.registryValue('safeSearch', channel=msg.args[0]):
|
||||
kwargs['safeSearch'] = 1
|
||||
lang = self.registryValue('defaultLanguage', channel=msg.args[0])
|
||||
if lang:
|
||||
kwargs['language'] = lang
|
||||
for (option, argument) in optlist:
|
||||
if option == 'notsafe':
|
||||
kwargs['safeSearch'] = False
|
||||
elif option == 'similar':
|
||||
kwargs['filter'] = False
|
||||
else:
|
||||
kwargs[option] = argument
|
||||
try:
|
||||
data = search(self.log, text, **kwargs)
|
||||
except google.NoLicenseKey, e:
|
||||
irc.error('You must have a free Google web services license key '
|
||||
'in order to use this command. You can get one at '
|
||||
'<http://code.google.com/apis/soapsearch/>. Once you '
|
||||
'have one, you can set it with the command '
|
||||
'"config supybot.plugins.Google.licenseKey <key>".')
|
||||
if 'language' in optlist and optlist['language'].lower() not in \
|
||||
conf.supybot.plugins.Google.safesearch.validStrings:
|
||||
irc.errorInvalid('language')
|
||||
data = self.search(text, msg.args[0], dict(optlist))
|
||||
if data['responseStatus'] != 200:
|
||||
irc.reply('We broke The Google!')
|
||||
return
|
||||
bold = self.registryValue('bold', msg.args[0])
|
||||
max = self.registryValue('maximumResults', msg.args[0])
|
||||
irc.reply(self.formatData(data, bold=bold, max=max))
|
||||
irc.reply(self.formatData(data['responseData']['results'],
|
||||
bold=bold, max=max))
|
||||
google = wrap(google, [getopts({'language':'something',
|
||||
'restrict':'something',
|
||||
'notsafe':'', 'similar':''}),
|
||||
many('something')])
|
||||
'safesearch':''}),
|
||||
'text'])
|
||||
|
||||
def meta(self, irc, msg, args, optlist, text):
|
||||
"""<search> [--{language,restrict} <value>] [--{similar,notsafe}]
|
||||
|
||||
Searches google and gives all the interesting meta information about
|
||||
the search. See the help for the google command for a detailed
|
||||
description of the parameters.
|
||||
"""
|
||||
kwargs = {'language': 'lang_en', 'safeSearch': 1}
|
||||
for option, argument in optlist:
|
||||
if option == 'notsafe':
|
||||
kwargs['safeSearch'] = False
|
||||
elif option == 'similar':
|
||||
kwargs['filter'] = False
|
||||
else:
|
||||
kwargs[option[2:]] = argument
|
||||
data = search(self.log, text, **kwargs)
|
||||
meta = data.meta
|
||||
categories = [d['fullViewableName'] for d in meta.directoryCategories]
|
||||
categories = [format('%q', s.replace('_', ' ')) for s in categories]
|
||||
s = format('Search for %q returned %s %i results in %.2f seconds.%s',
|
||||
meta.searchQuery,
|
||||
meta.estimateIsExact and 'exactly' or 'approximately',
|
||||
meta.estimatedTotalResultsCount,
|
||||
meta.searchTime,
|
||||
categories and format(' Categories include %L.',categories))
|
||||
irc.reply(s)
|
||||
meta= wrap(meta, [getopts({'language':'something',
|
||||
'restrict':'something',
|
||||
'notsafe':'', 'similar':''}),
|
||||
many('something')])
|
||||
|
||||
_cacheUrlRe = re.compile('<code>([^<]+)</code>')
|
||||
def cache(self, irc, msg, args, url):
|
||||
"""<url>
|
||||
|
||||
Returns a link to the cached version of <url> if it is available.
|
||||
"""
|
||||
html = google.doGetCachedPage(url)
|
||||
m = self._cacheUrlRe.search(html)
|
||||
if m is not None:
|
||||
url = m.group(1)
|
||||
url = utils.web.htmlToText(url)
|
||||
irc.reply(url)
|
||||
else:
|
||||
irc.error('Google seems to have no cache for that site.')
|
||||
data = self.search(url, msg.args[0], {'smallsearch': True})
|
||||
if data['responseData']['results']:
|
||||
m = data['responseData']['results'][0]
|
||||
if m['cacheUrl']:
|
||||
url = m['cacheUrl'].encode('utf-8')
|
||||
irc.reply(url)
|
||||
return
|
||||
irc.error('Google seems to have no cache for that site.')
|
||||
cache = wrap(cache, ['url'])
|
||||
|
||||
def fight(self, irc, msg, args):
|
||||
@ -265,11 +196,12 @@ class Google(callbacks.PluginRegexp):
|
||||
Returns the results of each search, in order, from greatest number
|
||||
of results to least.
|
||||
"""
|
||||
|
||||
channel = msg.args[0]
|
||||
results = []
|
||||
for arg in args:
|
||||
data = search(self.log, [arg])
|
||||
results.append((data.meta.estimatedTotalResultsCount, arg))
|
||||
data = self.search(arg, channel, {'smallsearch': True})
|
||||
count = data['responseData']['cursor']['estimatedResultCount']
|
||||
results.append((int(count), arg))
|
||||
results.sort()
|
||||
results.reverse()
|
||||
if self.registryValue('bold', msg.args[0]):
|
||||
@ -279,35 +211,38 @@ class Google(callbacks.PluginRegexp):
|
||||
s = ', '.join([format('%s: %i', bold(s), i) for (i, s) in results])
|
||||
irc.reply(s)
|
||||
|
||||
def spell(self, irc, msg, args, word):
|
||||
"""<word>
|
||||
_gtranslateUrl='http://ajax.googleapis.com/ajax/services/language/translate'
|
||||
def translate(self, irc, msg, args, fromLang, toLang, text):
|
||||
"""<from-language> [to] <to-language> <text>
|
||||
|
||||
Returns Google's spelling recommendation for <word>.
|
||||
Returns <text> translated from <from-language> into <to-language>.
|
||||
Beware that translating to or from languages that use multi-byte
|
||||
characters may result in some very odd results.
|
||||
"""
|
||||
result = google.doSpellingSuggestion(word)
|
||||
if result:
|
||||
irc.reply(result)
|
||||
else:
|
||||
irc.reply('No spelling suggestion made. This could mean that '
|
||||
'the word you gave is spelled right; it could also '
|
||||
'mean that its spelling was too whacked out even for '
|
||||
'Google to figure out.')
|
||||
spell = wrap(spell, ['text'])
|
||||
|
||||
def stats(self, irc, msg, args):
|
||||
"""takes no arguments
|
||||
|
||||
Returns interesting information about this Google module. Mostly
|
||||
useful for making sure you don't go over your 1000 requests/day limit.
|
||||
"""
|
||||
recent = len(last24hours)
|
||||
time = self.registryValue('state.time')
|
||||
searches = self.registryValue('state.searches')
|
||||
irc.reply(format('This google module has made %n total; '
|
||||
'%i in the past 24 hours. '
|
||||
'Google has spent %.2f seconds searching for me.',
|
||||
(searches, 'search'), recent, time))
|
||||
stats = wrap(stats)
|
||||
channel = msg.args[0]
|
||||
ref = self.registryValue('referer')
|
||||
if not ref:
|
||||
ref = 'http://%s/%s' % (dynamic.irc.server,
|
||||
dynamic.irc.nick)
|
||||
headers = utils.web.defaultHeaders
|
||||
headers['Referer'] = ref
|
||||
opts = {'q': text, 'v': '1.0'}
|
||||
if 'lang_%s' % fromLang.lower() not in \
|
||||
conf.supybot.plugins.Google.defaultLanguage.validStrings:
|
||||
irc.errorInvalid('from language')
|
||||
if 'lang_%s' % toLang.lower() not in \
|
||||
conf.supybot.plugins.Google.defaultLanguage.validStrings:
|
||||
irc.errorInvalid('to language')
|
||||
opts['langpair'] = '%s|%s' % (fromLang, toLang)
|
||||
fd = utils.web.getUrlFd('%s?%s' % (self._gtranslateUrl,
|
||||
urllib.urlencode(opts)),
|
||||
headers)
|
||||
json = simplejson.load(fd)
|
||||
fd.close()
|
||||
if json['responseStatus'] != 200:
|
||||
raise callbacks.Error, 'We broke The Google!'
|
||||
irc.reply(json['responseData']['translatedText'].encode('utf-8'))
|
||||
translate = wrap(translate, ['something', 'to', 'something', 'text'])
|
||||
|
||||
def googleSnarfer(self, irc, msg, match):
|
||||
r"^google\s+(.*)$"
|
||||
@ -315,8 +250,9 @@ class Google(callbacks.PluginRegexp):
|
||||
return
|
||||
searchString = match.group(1)
|
||||
try:
|
||||
data = search(self.log, [searchString], safeSearch=1)
|
||||
except google.NoLicenseKey:
|
||||
data = self.search(searchString, msg.args[0],
|
||||
{'smallsearch': True})
|
||||
except callbacks.Error:
|
||||
return
|
||||
if data.results:
|
||||
url = data.results[0].URL
|
||||
|
@ -1,5 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||
# Copyright (c) 2008, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -46,17 +47,18 @@ class GoogleTestCase(ChannelPluginTestCase):
|
||||
'the speed of light '
|
||||
'in microns / fortnight', '×')
|
||||
|
||||
def testSearch(self):
|
||||
self.assertNotError('google foo')
|
||||
|
||||
def testFight(self):
|
||||
self.assertRegexp('fight supybot moobot', r'.*supybot.*: \d+')
|
||||
|
||||
def testTranslate(self):
|
||||
self.assertRegexp('translate en es hello world', 'mundo')
|
||||
|
||||
def testCalcDoesNotHaveExtraSpaces(self):
|
||||
self.assertNotRegexp('google calc 1000^2', r'\s+,\s+')
|
||||
|
||||
def testNoNoLicenseKeyError(self):
|
||||
orig = conf.supybot.plugins.Google.searchSnarfer()
|
||||
try:
|
||||
conf.supybot.plugins.Google.searchSnarfer.setValue(True)
|
||||
self.assertSnarfNoResponse('google blah')
|
||||
finally:
|
||||
conf.supybot.plugins.Google.searchSnarfer.setValue(orig)
|
||||
|
||||
def testGroupsSnarfer(self):
|
||||
orig = conf.supybot.plugins.Google.groupsSnarfer()
|
||||
try:
|
||||
@ -113,14 +115,4 @@ class GoogleTestCase(ChannelPluginTestCase):
|
||||
finally:
|
||||
conf.supybot.plugins.Google.groupsSnarfer.setValue(orig)
|
||||
|
||||
def testInvalidKeyCaught(self):
|
||||
conf.supybot.plugins.Google.licenseKey.set(
|
||||
'abcdefghijklmnopqrstuvwxyz123456')
|
||||
self.assertNotRegexp('google foobar', 'faultType')
|
||||
self.assertNotRegexp('google foobar', 'SOAP')
|
||||
|
||||
def testStats(self):
|
||||
self.assertNotError('google stats')
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
||||
|
Loading…
Reference in New Issue
Block a user