mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-01-30 14:14:37 +01:00
Updated to allow multiple servers.
This commit is contained in:
parent
544bfa35da
commit
2336af5525
5
RELNOTES
5
RELNOTES
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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():
|
||||
|
@ -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):
|
||||
|
33
src/conf.py
33
src/conf.py
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user