Allow multiple nicks, refactored irclib.Irc a bit.

This commit is contained in:
Jeremy Fincher 2004-07-26 06:05:39 +00:00
parent dc4d78774d
commit 7dbbf16dcc
5 changed files with 107 additions and 69 deletions

View File

@ -208,7 +208,6 @@ if __name__ == '__main__':
defaultNetwork = conf.supybot.networks.default()
network = conf.supybot.networks.get(defaultNetwork)
server = network.server()
password = network.password()
if ':' in server:
serverAndPort = server.split(':', 1)
serverAndPort[1] = int(serverAndPort[1])
@ -251,8 +250,7 @@ if __name__ == '__main__':
import supybot.callbacks as callbacks
import supybot.Owner as Owner
irc = irclib.Irc(nick, user=user, ident=ident,
network=defaultNetwork, password=password)
irc = irclib.Irc(network=defaultNetwork)
callback = Owner.Class()
irc.addCallback(callback)
driver = drivers.newDriver(server, irc)

View File

@ -129,6 +129,9 @@ class ValidNick(registry.String):
else:
registry.String.setValue(self, v)
class ValidNicks(registry.SpaceSeparatedListOf):
Value = ValidNick
class ValidChannel(registry.String):
"""Value must be a valid IRC channel name."""
def setValue(self, v):
@ -142,7 +145,12 @@ class ValidChannel(registry.String):
registry.String.setValue(self, v)
registerGlobalValue(supybot, 'nick',
ValidNick('supybot', """Determines the bot's nick."""))
ValidNick('supybot', """Determines the bot's default nick."""))
registerGlobalValue(supybot.nick, 'alternates', ValidNicks([], """Determines
what alternative nicks will be used if the primary nick (supybot.nick)
isn't available. If none are given, or if all are taken, the primary nick
will be perturbed appropriately until an unused nick is found."""))
registerGlobalValue(supybot, 'ident',
ValidNick('supybot', """Determines the bot's ident string, if the server
@ -450,13 +458,6 @@ registerChannelValue(supybot.replies, 'possibleBug',
# End supybot.replies.
###
# XXX: This should be SpaceSeparated, if it survives.
supybot.register('nickmods', registry.CommaSeparatedListOfStrings(
'__%s__,%s^,%s`,%s_,%s__,_%s,__%s,[%s]'.split(','),
"""A list of modifications to be made to a nick when the nick the bot tries
to get from the server is in use. There should be one %s in each string;
this will get replaced with the original nick."""))
registerGlobalValue(supybot, 'snarfThrottle',
registry.Float(10.0, """A floating point number of seconds to throttle
snarfed URLs, in order to prevent loops between two bots snarfing the same

View File

@ -36,6 +36,7 @@ import supybot.fix as fix
import copy
import sets
import time
import operator
from itertools import imap, chain, cycle
import supybot.log as log
@ -332,7 +333,8 @@ class IrcState(IrcCommandDispatcher):
chan = ChannelState()
chan.addUser(msg.nick)
self.channels[channel] = chan
assert msg.nick == irc.nick, msg
# I don't know why this assert was here.
#assert msg.nick == irc.nick, msg
def doMode(self, irc, msg):
channel = msg.args[0]
@ -441,65 +443,86 @@ class Irc(IrcCommandDispatcher):
_nickSetters = sets.Set(['001', '002', '003', '004', '250', '251', '252',
'254', '255', '265', '266', '372', '375', '376',
'333', '353', '332', '366', '005'])
def __init__(self, nick, user='', ident='',
network='unset', password='', callbacks=None):
def __init__(self, network, callbacks=None):
world.ircs.append(self)
self.originalNick = intern(nick)
self.originalNetwork = intern(network)
self.nick = self.originalNick
self.network = self.originalNetwork
self.nickmods = cycle(conf.supybot.nickmods())
self.password = password
self.user = intern(user or nick) # Default to nick
self.ident = intern(ident or nick) # Ditto.
self.prefix = '%s!%s@%s' % (nick, ident, 'unset.domain')
self.network = network
if callbacks is None:
self.callbacks = []
else:
self.callbacks = callbacks
self.state = IrcState()
self.queue = IrcMsgQueue()
self.lastTake = 0
self.server = 'unset'
self.afterConnect = False
self.fastqueue = smallqueue()
self.lastping = time.time()
self.outstandingPing = False
self.driver = None # The driver should set this later.
if self.password:
self.queue.enqueue(ircmsgs.password(self.password))
log.info('Sending NICK command, nick is %s.', self.nick)
self.queue.enqueue(ircmsgs.nick(self.nick))
log.info('Sending USER command, ident is %s, user is %s.',
self.nick, self.user)
self.queue.enqueue(ircmsgs.user(self.ident, self.user))
self._setNonResettingVariables()
self._queueConnectMessages()
def reset(self):
"""Resets the Irc object. Called when the driver reconnects."""
self.nick = self.originalNick
self.network = self.originalNetwork
self.prefix = '%s!%s@%s' % (self.nick, self.ident, 'unset.domain')
self._setNonResettingVariables()
self._queueConnectMessages()
self.state.reset()
self.queue.reset()
self.fastqueue.reset()
for callback in self.callbacks:
callback.reset()
def _setNonResettingVariables(self):
# Configuration stuff.
self.nick = conf.supybot.nick()
self.user = conf.supybot.user()
self.ident = conf.supybot.ident()
self.alternateNicks = conf.supybot.nick.alternates()[:]
self.password = conf.supybot.networks.get(self.network).password()
self.prefix = '%s!%s@%s' % (self.nick, self.ident, 'unset.domain')
# The rest.
self.lastTake = 0
self.server = 'unset'
self.afterConnect = False
self.lastping = time.time()
self.outstandingPing = False
self.fastqueue = queue()
def _queueConnectMessages(self):
if self.password:
self.queue.enqueue(ircmsgs.password(self.password))
self.queue.enqueue(ircmsgs.nick(self.nick))
self.queue.enqueue(ircmsgs.user(self.ident, self.user))
for callback in self.callbacks:
callback.reset()
log.info('Sending PASS command, not logging the password.')
self.queueMsg(ircmsgs.password(self.password))
log.info('Sending NICK command, nick is %s.', self.nick)
self.queueMsg(ircmsgs.nick(self.nick))
log.info('Sending USER command, ident is %s, user is %s.',
self.ident, self.user)
self.queueMsg(ircmsgs.user(self.ident, self.user))
def _getNextNick(self):
if self.alternateNicks:
return self.alternateNicks.pop(0)
else:
nick = conf.supybot.nick()
for c in '`_^':
if nick.endswith(c):
if len(nick) >= 9: # The max length on many servers.
nick = nick.rstrip(c)
continue
else:
return nick + c
else:
return nick + c
for c in '`_^':
if nick.startswith(c):
if len(nick) >= 9:
nick = nick.lstrip(c)
continue
else:
return c + nick
else:
return c + nick
def __repr__(self):
return '<irclib.Irc object for %s>' % self.server
return '<irclib.Irc object for %s>' % self.network
def addCallback(self, callback):
"""Adds a callback to the callbacks list."""
self.callbacks.append(callback)
utils.sortBy(lambda cb: cb.priority, self.callbacks)
utils.sortBy(operator.attrgetter('priority'), self.callbacks)
def getCallback(self, name):
"""Gets a given callback by name."""
@ -600,15 +623,16 @@ class Irc(IrcCommandDispatcher):
def do376(self, msg):
log.info('Got end of MOTD from %s', self.server)
self.afterConnect = True
# Let's reset nicks in case we had to use a weird one.
self.alternateNicks = conf.supybot.nick.alternates()[:]
do377 = do422 = do376
def do433(self, msg):
"""Handles 'nickname already in use' messages."""
if self.nick != self.originalNick or not self.afterConnect:
newNick = self.nickmods.next() % self.originalNick
log.info('Got 433: %s is in use. Trying %s.', self.nick, newNick)
self.sendMsg(ircmsgs.nick(newNick))
do432 = do433
newNick = self._getNextNick()
log.info('Got 433: %s is in use. Trying %s.', self.nick, newNick)
self.sendMsg(ircmsgs.nick(newNick))
do432 = do433 # 432: Erroneous nickname.
def doJoin(self, msg):
if msg.nick == self.nick:

View File

@ -52,6 +52,7 @@ supybot.log.detailedTracebacks: False
supybot.throttleTime: 0
supybot.prefixChars: @
supybot.protocols.irc.throttleTime: -1
supybot.networks.test.server: should.not.need.this
""")
fd.close()

View File

@ -304,7 +304,7 @@ class IrcStateTestCase(SupyTestCase):
class IrcTestCase(SupyTestCase):
def setUp(self):
self.irc = irclib.Irc('nick')
self.irc = irclib.Irc('test')
_ = self.irc.takeMsg() # NICK
_ = self.irc.takeMsg() # USER
@ -325,6 +325,8 @@ class IrcTestCase(SupyTestCase):
self.failUnless(msg.command == 'NICK' and msg.args[0] != self.irc.nick)
def testSendBeforeQueue(self):
while self.irc.takeMsg() is not None:
self.irc.takeMsg()
self.irc.queueMsg(ircmsgs.IrcMsg('NOTICE #foo bar'))
self.irc.sendMsg(ircmsgs.IrcMsg('PRIVMSG #foo yeah!'))
msg = self.irc.takeMsg()
@ -392,23 +394,35 @@ class IrcCallbackTestCase(SupyTestCase):
self.assertEqual(doCommandCatcher.L, commands)
def testFirstCommands(self):
nick = 'nick'
user = 'user any user'
password = 'password'
expected = [ircmsgs.nick(nick), ircmsgs.user(nick, user)]
irc = irclib.Irc(nick, user)
msgs = [irc.takeMsg()]
while msgs[-1] != None:
msgs.append(irc.takeMsg())
msgs.pop()
self.assertEqual(msgs, expected)
irc = irclib.Irc(nick, user, password=password)
msgs = [irc.takeMsg()]
while msgs[-1] != None:
msgs.append(irc.takeMsg())
msgs.pop()
expected.insert(0, ircmsgs.password(password))
self.assertEqual(msgs, expected)
try:
originalNick = conf.supybot.nick()
originalUser = conf.supybot.user()
originalPassword = conf.supybot.networks.test.password()
nick = 'nick'
conf.supybot.nick.setValue(nick)
user = 'user any user'
conf.supybot.user.setValue(user)
expected = [ircmsgs.nick(nick), ircmsgs.user('supybot', user)]
irc = irclib.Irc('test')
msgs = [irc.takeMsg()]
while msgs[-1] != None:
msgs.append(irc.takeMsg())
msgs.pop()
self.assertEqual(msgs, expected)
password = 'password'
conf.supybot.networks.test.password.setValue(password)
irc = irclib.Irc('test')
msgs = [irc.takeMsg()]
while msgs[-1] != None:
msgs.append(irc.takeMsg())
msgs.pop()
expected.insert(0, ircmsgs.password(password))
self.assertEqual(msgs, expected)
finally:
conf.supybot.nick.setValue(nick)
conf.supybot.user.setValue(user)
conf.supybot.networks.test.password.setValue(password)
conf.supybot.nick.setValue(nick)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: