mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-30 06:49:24 +01:00
Added the Dict plugin in the new plugin format.
This commit is contained in:
parent
ddeac18563
commit
0e9a3c326e
1
plugins/Dict/README.txt
Normal file
1
plugins/Dict/README.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Insert a description of your plugin here, with any notes, etc. about using it.
|
54
plugins/Dict/__init__.py
Normal file
54
plugins/Dict/__init__.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
###
|
||||||
|
# Copyright (c) 2004, Jeremiah Fincher
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
###
|
||||||
|
|
||||||
|
"""
|
||||||
|
Commands that use the dictd protocol to define word.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import supybot
|
||||||
|
import supybot.world as world
|
||||||
|
|
||||||
|
__author__ = supybot.authors.jemfinch
|
||||||
|
|
||||||
|
# This is a dictionary mapping supybot.Author instances to lists of
|
||||||
|
# contributions.
|
||||||
|
__contributors__ = {}
|
||||||
|
|
||||||
|
import config
|
||||||
|
import plugin
|
||||||
|
reload(plugin) # In case we're being reloaded.
|
||||||
|
|
||||||
|
if world.testing:
|
||||||
|
import test
|
||||||
|
|
||||||
|
Class = plugin.Class
|
||||||
|
configure = config.configure
|
||||||
|
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
50
plugins/Dict/config.py
Normal file
50
plugins/Dict/config.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
###
|
||||||
|
# Copyright (c) 2004, Jeremiah Fincher
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
###
|
||||||
|
|
||||||
|
import supybot.conf as conf
|
||||||
|
import supybot.registry as registry
|
||||||
|
|
||||||
|
def configure(advanced):
|
||||||
|
from supybot.questions import output, expect, anything, something, yn
|
||||||
|
conf.registerPlugin('Dict', True)
|
||||||
|
output('The default dictd server is dict.org.')
|
||||||
|
if yn('Would you like to specify a different dictd server?'):
|
||||||
|
server = something('What server?')
|
||||||
|
conf.supybot.plugins.Dict.server.set(server)
|
||||||
|
|
||||||
|
Dict = conf.registerPlugin('Dict')
|
||||||
|
conf.registerGlobalValue(Dict, 'server',
|
||||||
|
registry.String('dict.org', """Determines what server the bot will
|
||||||
|
retrieve definitions from."""))
|
||||||
|
conf.registerChannelValue(Dict, 'default',
|
||||||
|
registry.String('', """Determines the default dictionary the bot will
|
||||||
|
ask for definitions in. If this value is '*' (without the quotes) the bot
|
||||||
|
will use all dictionaries to define words."""))
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78
|
323
plugins/Dict/dictclient.py
Normal file
323
plugins/Dict/dictclient.py
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
# Client for the DICT protocol (RFC2229)
|
||||||
|
#
|
||||||
|
# Copyright (C) 2002 John Goerzen
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
# Retrieved from: http://gopher.quux.org:80/devel
|
||||||
|
|
||||||
|
import socket, re
|
||||||
|
|
||||||
|
version = '1.0'
|
||||||
|
|
||||||
|
def dequote(s):
|
||||||
|
"""Will remove single or double quotes from the start and end of a string
|
||||||
|
and return the result."""
|
||||||
|
quotechars = "'\""
|
||||||
|
while len(s) and s[0] in quotechars:
|
||||||
|
s = s[1:]
|
||||||
|
while len(s) and s[-1] in quotechars:
|
||||||
|
s = s[0:-1]
|
||||||
|
return s
|
||||||
|
|
||||||
|
def enquote(s):
|
||||||
|
"""This function will put a string in double quotes, properly
|
||||||
|
escaping any existing double quotes with a backslash. It will
|
||||||
|
return the result."""
|
||||||
|
return '"%s"' % s.replace('"', "\\\"")
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
"""This class is used to establish a connection to a database server.
|
||||||
|
You will usually use this as the first call into the dictclient library.
|
||||||
|
Instantiating it takes two optional arguments: a hostname (a string)
|
||||||
|
and a port (an int). The hostname defaults to localhost
|
||||||
|
and the port to 2628, the port specified in RFC."""
|
||||||
|
def __init__(self, hostname='localhost', port=2628):
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.sock.connect((hostname, port))
|
||||||
|
self.rfile = self.sock.makefile("rt")
|
||||||
|
self.wfile = self.sock.makefile("wt", 0)
|
||||||
|
self.saveconnectioninfo()
|
||||||
|
|
||||||
|
def getresultcode(self):
|
||||||
|
"""Generic function to get a result code. It will return a list
|
||||||
|
consisting of two items: the integer result code and the text
|
||||||
|
following. You will not usually use this function directly."""
|
||||||
|
line = self.rfile.readline().strip()
|
||||||
|
code, text = line.split(' ', 1)
|
||||||
|
return [int(code), text]
|
||||||
|
|
||||||
|
def get200result(self):
|
||||||
|
"""Used when expecting a single line of text -- a 200-class
|
||||||
|
result. Returns [intcode, remaindertext]"""
|
||||||
|
|
||||||
|
code, text = self.getresultcode()
|
||||||
|
if code < 200 or code >= 300:
|
||||||
|
raise Exception, "Got '%s' when 200-class response expected" % \
|
||||||
|
line
|
||||||
|
return [code, text]
|
||||||
|
|
||||||
|
def get100block(self):
|
||||||
|
"""Used when expecting multiple lines of text -- gets the block
|
||||||
|
part only. Does not get any codes or anything! Returns a string."""
|
||||||
|
data = []
|
||||||
|
while 1:
|
||||||
|
line = self.rfile.readline().strip()
|
||||||
|
if line == '.':
|
||||||
|
break
|
||||||
|
data.append(line)
|
||||||
|
return "\n".join(data)
|
||||||
|
|
||||||
|
def get100result(self):
|
||||||
|
"""Used when expecting multiple lines of text, terminated by a period
|
||||||
|
and a 200 code. Returns: [initialcode, [bodytext_1lineperentry],
|
||||||
|
finalcode]"""
|
||||||
|
code, text = self.getresultcode()
|
||||||
|
if code < 100 or code >= 200:
|
||||||
|
raise Exception, "Got '%s' when 100-class response expected" % \
|
||||||
|
code
|
||||||
|
|
||||||
|
bodylines = self.get100block().split("\n")
|
||||||
|
|
||||||
|
code2 = self.get200result()[0]
|
||||||
|
return [code, bodylines, code2]
|
||||||
|
|
||||||
|
def get100dict(self):
|
||||||
|
"""Used when expecting a dictionary of results. Will read from
|
||||||
|
the initial 100 code, to a period and the 200 code."""
|
||||||
|
dict = {}
|
||||||
|
for line in self.get100result()[1]:
|
||||||
|
key, val = line.split(' ', 1)
|
||||||
|
dict[key] = dequote(val)
|
||||||
|
return dict
|
||||||
|
|
||||||
|
def saveconnectioninfo(self):
|
||||||
|
"""Called by __init__ to handle the initial connection. Will
|
||||||
|
save off the capabilities and messageid."""
|
||||||
|
code, string = self.get200result()
|
||||||
|
assert code == 220
|
||||||
|
m = re.search('<(.*)> (<.*>)$', string)
|
||||||
|
assert m is not None
|
||||||
|
capstr, msgid = m.groups()
|
||||||
|
self.capabilities = capstr.split('.')
|
||||||
|
self.messageid = msgid
|
||||||
|
|
||||||
|
def getcapabilities(self):
|
||||||
|
"""Returns a list of the capabilities advertised by the server."""
|
||||||
|
return self.capabilities
|
||||||
|
|
||||||
|
def getmessageid(self):
|
||||||
|
"""Returns the message id, including angle brackets."""
|
||||||
|
return self.messageid
|
||||||
|
|
||||||
|
def getdbdescs(self):
|
||||||
|
"""Gets a dict of available databases. The key is the db name
|
||||||
|
and the value is the db description. This command may generate
|
||||||
|
network traffic!"""
|
||||||
|
if hasattr(self, 'dbdescs'):
|
||||||
|
return self.dbdescs
|
||||||
|
|
||||||
|
self.sendcommand("SHOW DB")
|
||||||
|
self.dbdescs = self.get100dict()
|
||||||
|
return self.dbdescs
|
||||||
|
|
||||||
|
def getstratdescs(self):
|
||||||
|
"""Gets a dict of available strategies. The key is the strat
|
||||||
|
name and the value is the strat description. This call may
|
||||||
|
generate network traffic!"""
|
||||||
|
if hasattr(self, 'stratdescs'):
|
||||||
|
return self.stratdescs
|
||||||
|
|
||||||
|
self.sendcommand("SHOW STRAT")
|
||||||
|
self.stratdescs = self.get100dict()
|
||||||
|
return self.stratdescs
|
||||||
|
|
||||||
|
def getdbobj(self, dbname):
|
||||||
|
"""Gets a Database object corresponding to the database name passed
|
||||||
|
in. This function explicitly will *not* generate network traffic.
|
||||||
|
If you have not yet run getdbdescs(), it will fail."""
|
||||||
|
if not hasattr(self, 'dbobjs'):
|
||||||
|
self.dbobjs = {}
|
||||||
|
|
||||||
|
if self.dbobjs.has_key(dbname):
|
||||||
|
return self.dbobjs[dbname]
|
||||||
|
|
||||||
|
# We use self.dbdescs explicitly since we don't want to
|
||||||
|
# generate net traffic with this request!
|
||||||
|
|
||||||
|
if dbname != '*' and dbname != '!' and \
|
||||||
|
not dbname in self.dbdescs.keys():
|
||||||
|
raise Exception, "Invalid database name '%s'" % dbname
|
||||||
|
|
||||||
|
self.dbobjs[dbname] = Database(self, dbname)
|
||||||
|
return self.dbobjs[dbname]
|
||||||
|
|
||||||
|
def sendcommand(self, command):
|
||||||
|
"""Takes a command, without a newline character, and sends it to
|
||||||
|
the server."""
|
||||||
|
self.wfile.write(command + "\n")
|
||||||
|
|
||||||
|
def define(self, database, word):
|
||||||
|
"""Returns a list of Definition objects for each matching
|
||||||
|
definition. Parameters are the database name and the word
|
||||||
|
to look up. This is one of the main functions you will use
|
||||||
|
to interact with the server. Returns a list of Definition
|
||||||
|
objects. If there are no matches, an empty list is returned.
|
||||||
|
|
||||||
|
Note: database may be '*' which means to search all databases,
|
||||||
|
or '!' which means to return matches from the first database that
|
||||||
|
has a match."""
|
||||||
|
self.getdbdescs() # Prime the cache
|
||||||
|
|
||||||
|
if database != '*' and database != '!' and \
|
||||||
|
not database in self.getdbdescs():
|
||||||
|
raise Exception, "Invalid database '%s' specified" % database
|
||||||
|
|
||||||
|
self.sendcommand("DEFINE " + enquote(database) + " " + enquote(word))
|
||||||
|
code = self.getresultcode()[0]
|
||||||
|
|
||||||
|
retval = []
|
||||||
|
|
||||||
|
if code == 552:
|
||||||
|
# No definitions.
|
||||||
|
return []
|
||||||
|
if code != 150:
|
||||||
|
raise Exception, "Unknown code %d" % code
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
code, text = self.getresultcode()
|
||||||
|
if code != 151:
|
||||||
|
break
|
||||||
|
|
||||||
|
m = re.search('^"(.+)" (\S+)', text)
|
||||||
|
assert m is not None
|
||||||
|
resultword, resultdb = m.groups()
|
||||||
|
defstr = self.get100block()
|
||||||
|
retval.append(Definition(self, self.getdbobj(resultdb),
|
||||||
|
resultword, defstr))
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def match(self, database, strategy, word):
|
||||||
|
"""Gets matches for a query. Arguments are database name,
|
||||||
|
the strategy (see available ones in getstratdescs()), and the
|
||||||
|
pattern/word to look for. Returns a list of Definition objects.
|
||||||
|
If there is no match, an empty list is returned.
|
||||||
|
|
||||||
|
Note: database may be '*' which means to search all databases,
|
||||||
|
or '!' which means to return matches from the first database that
|
||||||
|
has a match."""
|
||||||
|
self.getstratdescs() # Prime the cache
|
||||||
|
self.getdbdescs() # Prime the cache
|
||||||
|
if not strategy in self.getstratdescs().keys():
|
||||||
|
raise Exception, "Invalid strategy '%s'" % strategy
|
||||||
|
if database != '*' and database != '!' and \
|
||||||
|
not database in self.getdbdescs().keys():
|
||||||
|
raise Exception, "Invalid database name '%s'" % database
|
||||||
|
|
||||||
|
self.sendcommand("MATCH %s %s %s" % (enquote(database),
|
||||||
|
enquote(strategy),
|
||||||
|
enquote(word)))
|
||||||
|
code = self.getresultcode()[0]
|
||||||
|
if code == 552:
|
||||||
|
# No Matches
|
||||||
|
return []
|
||||||
|
if code != 152:
|
||||||
|
raise Exception, "Unexpected code %d" % code
|
||||||
|
|
||||||
|
retval = []
|
||||||
|
|
||||||
|
for matchline in self.get100block().split("\n"):
|
||||||
|
matchdict, matchword = matchline.split(" ", 1)
|
||||||
|
retval.append(Definition(self, self.getdbobj(matchdict),
|
||||||
|
dequote(matchword)))
|
||||||
|
if self.getresultcode()[0] != 250:
|
||||||
|
raise Exception, "Unexpected end-of-list code %d" % code
|
||||||
|
return retval
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
"""An object corresponding to a particular database in a server."""
|
||||||
|
def __init__(self, dictconn, dbname):
|
||||||
|
"""Initialize the object -- requires a Connection object and
|
||||||
|
a database name."""
|
||||||
|
self.conn = dictconn
|
||||||
|
self.name = dbname
|
||||||
|
|
||||||
|
def getname(self):
|
||||||
|
"""Returns the short name for this database."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def getdescription(self):
|
||||||
|
if hasattr(self, 'description'):
|
||||||
|
return self.description
|
||||||
|
if self.getname() == '*':
|
||||||
|
self.description = 'All Databases'
|
||||||
|
elif self.getname() == '!':
|
||||||
|
self.description = 'First matching database'
|
||||||
|
else:
|
||||||
|
self.description = self.conn.getdbdescs()[self.getname()]
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
def getinfo(self):
|
||||||
|
"""Returns a string of info describing this database."""
|
||||||
|
if hasattr(self, 'info'):
|
||||||
|
return self.info
|
||||||
|
|
||||||
|
if self.getname() == '*':
|
||||||
|
self.info = "This special database will search all databases on the system."
|
||||||
|
elif self.getname() == '!':
|
||||||
|
self.info = "This special database will return matches from the first matching database."
|
||||||
|
else:
|
||||||
|
self.conn.sendcommand("SHOW INFO " + self.name)
|
||||||
|
self.info = "\n".join(self.conn.get100result()[1])
|
||||||
|
return self.info
|
||||||
|
|
||||||
|
def define(self, word):
|
||||||
|
"""Get a definition from within this database.
|
||||||
|
The argument, word, is the word to look up. The return value is the
|
||||||
|
same as from Connection.define()."""
|
||||||
|
return self.conn.define(self.getname(), word)
|
||||||
|
|
||||||
|
def match(self, strategy, word):
|
||||||
|
"""Get a match from within this database.
|
||||||
|
The argument, word, is the word to look up. The return value is
|
||||||
|
the same as from Connection.define()."""
|
||||||
|
return self.conn.match(self.getname(), strategy, word)
|
||||||
|
|
||||||
|
class Definition:
|
||||||
|
"""An object corresponding to a single definition."""
|
||||||
|
def __init__(self, dictconn, db, word, defstr = None):
|
||||||
|
"""Instantiate the object. Requires: a Connection object,
|
||||||
|
a Database object (NOT corresponding to '*' or '!' databases),
|
||||||
|
a word. Optional: a definition string. If not supplied,
|
||||||
|
it will be fetched if/when it is requested."""
|
||||||
|
self.conn = dictconn
|
||||||
|
self.db = db
|
||||||
|
self.word = word
|
||||||
|
self.defstr = defstr
|
||||||
|
|
||||||
|
def getdb(self):
|
||||||
|
"""Get the Database object corresponding to this definition."""
|
||||||
|
return self.db
|
||||||
|
|
||||||
|
def getdefstr(self):
|
||||||
|
"""Get the definition string (the actual content) of this
|
||||||
|
definition."""
|
||||||
|
if not self.defstr:
|
||||||
|
self.defstr = self.conn.define(self.getdb().getname(), self.word)[0].getdefstr()
|
||||||
|
return self.defstr
|
||||||
|
|
||||||
|
def getword(self):
|
||||||
|
"""Get the word this object describes."""
|
||||||
|
return self.word
|
129
plugins/Dict/plugin.py
Normal file
129
plugins/Dict/plugin.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
###
|
||||||
|
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
###
|
||||||
|
|
||||||
|
import sets
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import dictclient
|
||||||
|
|
||||||
|
import supybot.conf as conf
|
||||||
|
import supybot.utils as utils
|
||||||
|
from supybot.commands import *
|
||||||
|
import supybot.ircutils as ircutils
|
||||||
|
import supybot.registry as registry
|
||||||
|
import supybot.webutils as webutils
|
||||||
|
import supybot.callbacks as callbacks
|
||||||
|
|
||||||
|
class Dict(callbacks.Privmsg):
|
||||||
|
threaded = True
|
||||||
|
def dictionaries(self, irc, msg, args):
|
||||||
|
"""takes no arguments
|
||||||
|
|
||||||
|
Returns the dictionaries valid for the dict command.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
server = conf.supybot.plugins.Dict.server()
|
||||||
|
conn = dictclient.Connection(server)
|
||||||
|
dbs = conn.getdbdescs().keys()
|
||||||
|
dbs.sort()
|
||||||
|
irc.reply(utils.commaAndify(dbs))
|
||||||
|
except socket.error, e:
|
||||||
|
irc.error(webutils.strError(e))
|
||||||
|
dictionaries = wrap(dictionaries)
|
||||||
|
|
||||||
|
def random(self, irc, msg, args):
|
||||||
|
"""takes no arguments
|
||||||
|
|
||||||
|
Returns a random valid dictionary.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
server = conf.supybot.plugins.Dict.server()
|
||||||
|
conn = dictclient.Connection(server)
|
||||||
|
dbs = conn.getdbdescs().keys()
|
||||||
|
irc.reply(random.choice(dbs))
|
||||||
|
except socket.error, e:
|
||||||
|
irc.error(webutils.strError(e))
|
||||||
|
random = wrap(random)
|
||||||
|
|
||||||
|
def dict(self, irc, msg, args, words):
|
||||||
|
"""[<dictionary>] <word>
|
||||||
|
|
||||||
|
Looks up the definition of <word> on dict.org's dictd server.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
server = conf.supybot.plugins.Dict.server()
|
||||||
|
conn = dictclient.Connection(server)
|
||||||
|
except socket.error, e:
|
||||||
|
irc.error(webutils.strError(e), Raise=True)
|
||||||
|
dbs = sets.Set(conn.getdbdescs())
|
||||||
|
if words[0] in dbs:
|
||||||
|
dictionary = words.pop(0)
|
||||||
|
else:
|
||||||
|
default = self.registryValue('default', msg.args[0])
|
||||||
|
if default in dbs:
|
||||||
|
dictionary = default
|
||||||
|
else:
|
||||||
|
if default:
|
||||||
|
self.log.info('Default dict for %s is not a supported '
|
||||||
|
'dictionary: %s.', msg.args[0], default)
|
||||||
|
dictionary = '*'
|
||||||
|
if not words:
|
||||||
|
irc.error('You must give a word to define.', Raise=True)
|
||||||
|
word = ' '.join(words)
|
||||||
|
definitions = conn.define(dictionary, word)
|
||||||
|
dbs = sets.Set()
|
||||||
|
if not definitions:
|
||||||
|
if dictionary == '*':
|
||||||
|
irc.reply('No definition for %s could be found.' %
|
||||||
|
utils.quoted(word))
|
||||||
|
else:
|
||||||
|
irc.reply('No definition for %s could be found in %s' %
|
||||||
|
(utils.quoted(word), ircutils.bold(dictionary)))
|
||||||
|
return
|
||||||
|
L = []
|
||||||
|
for d in definitions:
|
||||||
|
dbs.add(ircutils.bold(d.getdb().getname()))
|
||||||
|
(db, s) = (d.getdb().getname(), d.getdefstr())
|
||||||
|
db = ircutils.bold(db)
|
||||||
|
s = utils.normalizeWhitespace(s).rstrip(';.,')
|
||||||
|
L.append('%s: %s' % (db, s))
|
||||||
|
utils.sortBy(len, L)
|
||||||
|
if dictionary == '*' and len(dbs) > 1:
|
||||||
|
s = '%s responded: %s' % (utils.commaAndify(dbs), '; '.join(L))
|
||||||
|
else:
|
||||||
|
s = '; '.join(L)
|
||||||
|
irc.reply(s)
|
||||||
|
dict = wrap(dict, [many('something')])
|
||||||
|
|
||||||
|
|
||||||
|
Class = Dict
|
||||||
|
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
47
plugins/Dict/test.py
Normal file
47
plugins/Dict/test.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
###
|
||||||
|
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions, and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of the author of this software nor the name of
|
||||||
|
# contributors to this software may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written consent.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
###
|
||||||
|
|
||||||
|
from supybot.test import *
|
||||||
|
|
||||||
|
class DictTestCase(PluginTestCase):
|
||||||
|
plugins = ('Dict',)
|
||||||
|
if network:
|
||||||
|
def testDict(self):
|
||||||
|
self.assertNotError('dict slash')
|
||||||
|
self.assertNotRegexp('dict web1913 slash', 'foldoc')
|
||||||
|
self.assertError('dict ""')
|
||||||
|
|
||||||
|
def testDictionaries(self):
|
||||||
|
self.assertNotError('dictionaries')
|
||||||
|
|
||||||
|
def testRandomDictionary(self):
|
||||||
|
self.assertNotError('random')
|
||||||
|
self.assertNotError('dict [random] moo')
|
||||||
|
|
||||||
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
Loading…
Reference in New Issue
Block a user