Updated to allow multiple servers.

This commit is contained in:
Jeremy Fincher 2004-07-30 06:52:21 +00:00
parent 544bfa35da
commit 2336af5525
8 changed files with 126 additions and 78 deletions

View File

@ -9,12 +9,13 @@ configuration variable won't work. To fix this, add these lines to
your configuration:
supybot.networks.default: <network>
supybot.networks.<network>.server: <server>
supybot.networks.<network>.servers: <server>
supybot.networks.<network>.password: <password>
Where <network> is the name of the network you're connecting to,
<server> is the old supybot.server, and <password> is the old
supybot.password.
supybot.password. Also note the "servers" rather than "server": we
now allow the configuration of multiple servers per network.
Since we support multiple networks and multiple servers now, the
--server option has been removed from scripts/supybot, as has the

View File

@ -183,13 +183,13 @@ class Relay(callbacks.Privmsg):
if serverPort is None:
raise ValueError, '_connect requires a (server, port) if ' \
'the network is not registered.'
conf.registerNetwork(network, server=('%s:%s' % serverPort))
conf.registerNetwork(network, servers=('%s:%s' % serverPort,))
if makeNew:
self.log.info('Creating new Irc for relaying to %s.', network)
newIrc = irclib.Irc(network)
newIrc.state.history = realIrc.state.history
newIrc.callbacks = realIrc.callbacks
driver = drivers.newDriver(serverPort, newIrc)
driver = drivers.newDriver(newIrc)
else:
newIrc = realIrc
self._addIrc(newIrc)

View File

@ -109,7 +109,6 @@ if __name__ == '__main__':
# -p (profiling)
# -O (optimizing)
# -n, --nick (nick)
# -s, --server (server)
# --startup (commands to run onStart)
# --connect (commands to run afterConnect)
# --config (configuration values)
@ -216,14 +215,6 @@ if __name__ == '__main__':
sys.stderr.write('default when the bot starts.')
sys.exit(-1)
server = network.server()
if ':' in server:
serverAndPort = server.split(':', 1)
serverAndPort[1] = int(serverAndPort[1])
server = tuple(serverAndPort)
else:
server = (server, 6667)
if options.optimize:
# This doesn't work anymore.
__builtins__.__debug__ = False
@ -262,7 +253,7 @@ if __name__ == '__main__':
irc = irclib.Irc(network=defaultNetwork)
callback = Owner.Class()
irc.addCallback(callback)
driver = drivers.newDriver(server, irc)
driver = drivers.newDriver(irc)
if options.debug:
for (name, module) in sys.modules.iteritems():

View File

@ -63,28 +63,45 @@ class AsyncoreRunnerDriver(drivers.IrcDriver):
class AsyncoreDriver(asynchat.async_chat, object):
def __init__(self, (server, port), irc):
def __init__(self, irc):
asynchat.async_chat.__init__(self)
self.server = (server, port)
self.irc = irc
self.irc.driver = self
self.buffer = ''
self.servers = ()
self.networkGroup = conf.supybot.networks.get(self.irc.network)
self.set_terminator('\n')
# XXX: Use utils.getSocket.
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.connect(self.server)
self.connect(self._getNextServer())
except socket.error, e:
log.warning('Error connecting to %s: %s', self.server[0], e)
log.warning('Error connecting to %s: %s', self.currentServer, e)
self.reconnect(wait=True)
def _getServers(self):
# We do this, rather than itertools.cycle the servers in __init__,
# because otherwise registry updates given as setValues or sets
# wouldn't be visible until a restart.
return self.networkGroup.servers()[:] # Be sure to copy!
def _getNextServer(self):
if not self.servers:
self.servers = self._getServers()
assert self.servers, 'Servers value for %s is empty.' % \
self.networkGroup.name
server = self.servers.pop(0)
self.currentServer = '%s:%s' % server
return server
def _scheduleReconnect(self, at=60):
when = time.time() + at
if not world.dying:
whenS = log.timestamp(when)
log.info('Scheduling reconnect to %s at %s', self.server[0], whenS)
log.info('Scheduling reconnect to %s at %s',
self.currentServer, whenS)
def makeNewDriver():
self.irc.reset()
driver = self.__class__(self.server, self.irc)
driver = self.__class__(self.irc)
schedule.addEvent(makeNewDriver, when)
def writable(self):

View File

@ -163,20 +163,41 @@ registerGlobalValue(supybot, 'user',
registry.String('Supybot %s' % version, """Determines the user the bot
sends to the server."""))
# TODO: Make this check for validity.
registerGroup(supybot, 'networks')
registerGlobalValue(supybot.networks, 'default', registry.String('',
"""Determines what the default network joined by the bot will be."""))
registerGlobalValue(supybot.networks, 'default',
registry.String('', """Determines what the default network joined by the
bot will be."""))
def registerNetwork(name, password='', server=''):
class Servers(registry.SpaceSeparatedListOfStrings):
def normalize(self, s):
if ':' not in s:
s += ':6667'
return s
def convert(self, s):
s = self.normalize(s)
(server, port) = s.split(':')
port = int(port)
return (server, port)
def __call__(self):
L = registry.SpaceSeparatedListOfStrings.__call__(self)
return map(self.convert, L)
def __str__(self):
return ' '.join(registry.SpaceSeparatedListOfStrings.__call__(self))
def registerNetwork(name, password='', servers=()):
name = intern(name)
network = registerGroup(supybot.networks, name)
registerGlobalValue(network, 'password', registry.String(password,
"""Determines what password will be used on %s. Yes, we know that
technically passwords are server-specific and not network-specific,
but this is the best we can do right now.""" % name))
registerGlobalValue(network, 'server', registry.String(server,
"""Determines what server the bot will connect to for %s.""" % name))
registerGlobalValue(network, 'servers', Servers(servers,
"""Determines what servers the bot will connect to for %s. Each will
be tried in order, wrapping back to the first when the cycle is
completed.""" % name))
return network
# Let's fill our networks.

View File

@ -54,8 +54,6 @@ class IrcDriver(object):
"""Base class for drivers."""
def __init__(self):
add(self.name(), self)
if not hasattr(self, 'irc'):
self.irc = None # This is to satisfy PyChecker.
def run(self):
raise NotImplementedError
@ -113,7 +111,7 @@ def run():
del _drivers[name]
_drivers[name] = driver
def newDriver(server, irc, moduleName=None):
def newDriver(irc, moduleName=None):
"""Returns a new driver for the given server using the irc given and using
conf.supybot.driverModule to determine what driver to pick."""
if moduleName is None:
@ -128,9 +126,8 @@ def newDriver(server, irc, moduleName=None):
elif not moduleName.startswith('supybot.'):
moduleName = 'supybot.' + moduleName
driverModule = __import__(moduleName, {}, {}, ['not empty'])
log.debug('Creating new driver for %s:%s (%s)',
server[0], server[1], moduleName)
driver = driverModule.Driver(server, irc)
log.debug('Creating new driver for %s.', irc)
driver = driverModule.Driver(irc)
irc.driver = driver
return driver

View File

@ -53,30 +53,47 @@ import supybot.drivers as drivers
import supybot.ircmsgs as ircmsgs
import supybot.schedule as schedule
instances = 0
originalPoll = conf.supybot.drivers.poll()
def resetPoll():
log.info('Resetting supybot.drivers.poll to %s', originalPoll)
conf.supybot.drivers.poll.setValue(originalPoll)
atexit.register(resetPoll)
reconnectWaits = (0, 60, 300)
class SocketDriver(drivers.IrcDriver):
def __init__(self, (server, port), irc, reconnectWaits=(0, 60, 300)):
global instances
instances += 1
conf.supybot.drivers.poll.setValue(originalPoll / instances)
self.server = (server, port)
drivers.IrcDriver.__init__(self) # Must come after server is set.
def __init__(self, irc):
self.irc = irc
self.irc.driver = self
drivers.IrcDriver.__init__(self) # Must come after setting irc.
self.networkGroup = conf.supybot.networks.get(self.irc.network)
self.servers = ()
self.eagains = 0
self.inbuffer = ''
self.outbuffer = ''
self.connected = False
self.eagains = 0
self.reconnectWaitsIndex = 0
self.reconnectWaits = reconnectWaits
self.connect()
def _getServers(self):
# We do this, rather than itertools.cycle the servers in __init__,
# because otherwise registry updates given as setValues or sets
# wouldn't be visible until a restart.
return self.networkGroup.servers()[:] # Be sure to copy!
def _getNextServer(self):
if not self.servers:
self.servers = self._getServers()
assert self.servers, 'Servers value for %s is empty.' % \
self.networkGroup.name
server = self.servers.pop(0)
self.currentServer = '%s:%s' % server
return server
def _handleSocketError(self, e):
# (11, 'Resource temporarily unavailable') raised if connect
# hasn't finished yet. We'll keep track of how many we get.
if e.args[0] != 11 and self.eagains > 120:
log.warning('Disconnect from %s: %s.',
self.currentServer, e.args[1])
self.reconnect(wait=True)
else:
log.debug('Got EAGAIN, current count: %s.', self.eagains)
self.eagains += 1
def _sendIfMsgs(self):
msgs = [self.irc.takeMsg()]
while msgs[-1] is not None:
@ -89,15 +106,7 @@ class SocketDriver(drivers.IrcDriver):
self.outbuffer = self.outbuffer[sent:]
self.eagains = 0
except socket.error, e:
# (11, 'Resource temporarily unavailable') raised if connect
# hasn't finished yet.
if e.args[0] != 11 and self.eagains > 120:
server = '%s:%s' % self.server
log.warning('Disconnect from %s: %s', server, e.args[1])
self.reconnect(wait=True)
else:
log.debug('Got EAGAIN, current count: %s', self.eagains)
self.eagains += 1
self._handleSocketError(e)
def run(self):
if not self.connected:
@ -119,13 +128,7 @@ class SocketDriver(drivers.IrcDriver):
except socket.timeout:
pass
except socket.error, e:
# Same as with _sendIfMsgs.
if e.args[0] != 11 or self.eagains > 120:
log.warning('Disconnect from %s: %s', self.server, e)
self.reconnect(wait=True)
else:
log.debug('Got EAGAIN, current count: %s', self.eagains)
self.eagains += 1
self._handleSocketError(e)
return
self._sendIfMsgs()
@ -133,7 +136,7 @@ class SocketDriver(drivers.IrcDriver):
self.reconnect(reset=False, **kwargs)
def reconnect(self, wait=False, reset=True):
server = '%s:%s' % self.server
server = self._getNextServer()
if reset:
log.debug('Resetting %s.', self.irc)
self.irc.reset()
@ -143,16 +146,17 @@ class SocketDriver(drivers.IrcDriver):
log.info('Reconnect called on driver for %s.', self.irc)
self.conn.close()
elif not wait:
log.info('Connecting to %s.', server)
log.info('Connecting to %s.', self.currentServer)
self.connected = False
if wait:
log.info('Reconnect to %s waiting.', server)
log.info('Reconnect to %s waiting.', self.currentServer)
self._scheduleReconnect()
return
try:
self.conn = utils.getSocket(self.server[0])
self.conn = utils.getSocket(server[0])
except socket.error, e:
log.warning('Error connecting to %s: %s', server, e.args[1])
log.warning('Error connecting to %s: %s',
self.currentServer, e.args[1])
self.reconnect(wait=True)
return
# We allow more time for the connect here, since it might take longer.
@ -161,7 +165,7 @@ class SocketDriver(drivers.IrcDriver):
if self.reconnectWaitsIndex < len(self.reconnectWaits)-1:
self.reconnectWaitsIndex += 1
try:
self.conn.connect(self.server)
self.conn.connect(server)
self.conn.settimeout(conf.supybot.drivers.poll())
except socket.error, e:
if e.args[0] == 115:
@ -172,7 +176,7 @@ class SocketDriver(drivers.IrcDriver):
'check for %s', whenS)
schedule.addEvent(self._checkAndWriteOrReconnect, when)
else:
log.warning('Error connecting to %s: %s', self.server[0], e)
log.warning('Error connecting to %s: %s', self.currentServer,e)
self.reconnect(wait=True)
return
self.connected = True
@ -186,15 +190,15 @@ class SocketDriver(drivers.IrcDriver):
self.connected = True
self.reconnectWaitPeriodsIndex = 0
else:
log.warning('Error connecting to %s: Timed out.', self.server[0])
log.warning('Error connecting to %s: Timed out.',self.currentServer)
self.reconnect()
def _scheduleReconnect(self):
when = time.time() + self.reconnectWaits[self.reconnectWaitsIndex]
if not world.dying:
whenS = log.timestamp(when)
server = '%s:%s' % self.server
log.info('Scheduling reconnect to %s at %s', server, whenS)
log.info('Scheduling reconnect to %s at %s',
self.irc.network, whenS)
schedule.addEvent(self.reconnect, when)
def die(self):
@ -203,7 +207,7 @@ class SocketDriver(drivers.IrcDriver):
# self.irc.die() Kill off the ircs yourself, jerk!
def name(self):
return '%s%s' % (self.__class__.__name__, self.server)
return '%s(%s)' % (self.__class__.__name__, self.irc)
Driver = SocketDriver

View File

@ -92,12 +92,29 @@ class SupyIrcProtocol(LineReceiver):
class SupyReconnectingFactory(ReconnectingClientFactory):
maxDelay = 300
protocol = SupyIrcProtocol
def __init__(self, (server, port), irc):
def __init__(self, irc):
self.irc = irc
self.server = (server, port)
reactor.connectTCP(server, port, self)
self.networkGroup = conf.supybot.networks.get(self.irc.network)
self.servers = ()
reactor.connectTCP('', 0, self)
def _getServers(self):
# We do this, rather than itertools.cycle the servers in __init__,
# because otherwise registry updates given as setValues or sets
# wouldn't be visible until a restart.
return self.networkGroup.servers()[:] # Be sure to copy!
def _getNextServer(self):
if not self.servers:
self.servers = self._getServers()
assert self.servers, 'Servers value for %s is empty.' % \
self.networkGroup.name
server = self.servers.pop(0)
self.currentServer = '%s:%s' % server
return server
def buildProtocol(self, addr):
addr = self._getNextServer()
protocol = ReconnectingClientFactory.buildProtocol(self, addr)
protocol.irc = self.irc
return protocol