2003-03-12 07:26:59 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
###
|
2004-08-23 15:14:06 +02:00
|
|
|
# Copyright (c) 2002-2004, Jeremiah Fincher
|
2003-03-12 07:26:59 +01:00
|
|
|
# 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.
|
|
|
|
###
|
|
|
|
|
2003-03-26 09:39:50 +01:00
|
|
|
"""
|
|
|
|
Handles relaying between networks.
|
|
|
|
"""
|
|
|
|
|
2003-11-25 09:23:47 +01:00
|
|
|
__revision__ = "$Id$"
|
2004-04-28 08:30:55 +02:00
|
|
|
__author__ = 'Jeremy Fincher (jemfinch) <jemfinch@users.sf.net>'
|
2003-11-25 09:23:47 +01:00
|
|
|
|
2004-07-24 07:18:26 +02:00
|
|
|
import supybot.plugins as plugins
|
2003-03-12 07:26:59 +01:00
|
|
|
|
2003-03-27 09:17:51 +01:00
|
|
|
import re
|
2003-10-22 07:15:19 +02:00
|
|
|
import sys
|
2004-02-04 09:19:28 +01:00
|
|
|
import copy
|
2003-06-22 18:17:33 +02:00
|
|
|
import time
|
2003-11-15 05:37:04 +01:00
|
|
|
from itertools import imap, ifilter
|
2003-03-27 09:17:51 +01:00
|
|
|
|
2004-07-24 07:18:26 +02:00
|
|
|
import supybot.conf as conf
|
|
|
|
import supybot.utils as utils
|
|
|
|
import supybot.world as world
|
|
|
|
import supybot.irclib as irclib
|
|
|
|
import supybot.drivers as drivers
|
|
|
|
import supybot.ircmsgs as ircmsgs
|
|
|
|
import supybot.ircutils as ircutils
|
|
|
|
import supybot.privmsgs as privmsgs
|
|
|
|
import supybot.registry as registry
|
|
|
|
import supybot.callbacks as callbacks
|
2004-08-21 10:43:47 +02:00
|
|
|
from supybot.structures import RingBuffer
|
2003-03-26 09:39:50 +01:00
|
|
|
|
2004-01-30 00:58:27 +01:00
|
|
|
def configure(advanced):
|
2004-07-25 20:24:51 +02:00
|
|
|
from supybot.questions import output, expect, anything, something, yn
|
2004-01-28 23:19:25 +01:00
|
|
|
conf.registerPlugin('Relay', True)
|
2004-02-14 01:56:17 +01:00
|
|
|
if yn('Would you like to relay between any channels?'):
|
|
|
|
channels = anything('What channels? Separated them by spaces.')
|
|
|
|
conf.supybot.plugins.Relay.channels.set(channels)
|
2004-01-31 23:24:43 +01:00
|
|
|
if yn('Would you like to use color to distinguish between nicks?'):
|
2004-02-14 01:56:17 +01:00
|
|
|
conf.supybot.plugins.Relay.color.setValue(True)
|
|
|
|
output("""Right now there's no way to configure the actual connection to
|
|
|
|
the server. What you'll need to do when the bot finishes starting up is
|
|
|
|
use the 'start' command followed by the 'connect' command. Use the 'help'
|
|
|
|
command to see how these two commands should be used.""")
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2004-07-27 07:19:14 +02:00
|
|
|
class Networks(registry.SpaceSeparatedListOf):
|
|
|
|
List = ircutils.IrcSet
|
|
|
|
Value = registry.String
|
|
|
|
|
2004-02-14 01:56:17 +01:00
|
|
|
conf.registerPlugin('Relay')
|
|
|
|
conf.registerChannelValue(conf.supybot.plugins.Relay, 'color',
|
|
|
|
registry.Boolean(False, """Determines whether the bot will color relayed
|
|
|
|
PRIVMSGs so as to make the messages easier to read."""))
|
|
|
|
conf.registerChannelValue(conf.supybot.plugins.Relay, 'topicSync',
|
|
|
|
registry.Boolean(True, """Determines whether the bot will synchronize
|
|
|
|
topics between networks in the channels it relays."""))
|
2004-08-05 06:01:52 +02:00
|
|
|
conf.registerChannelValue(conf.supybot.plugins.Relay, 'hostmasks',
|
|
|
|
registry.Boolean(False, """Determines whether the bot will relay the
|
|
|
|
hostmask of the person joining or parting the channel when he or she joins
|
|
|
|
or parts."""))
|
2004-08-10 17:40:21 +02:00
|
|
|
conf.registerChannelValue(conf.supybot.plugins.Relay, 'includeNetwork',
|
|
|
|
registry.Boolean(True, """Determines whether the bot will include the
|
|
|
|
network in relayed PRIVMSGs; if you're only relaying between two networks,
|
|
|
|
it's somewhat redundant, and you may wish to save the space."""))
|
2004-08-21 10:43:47 +02:00
|
|
|
conf.registerChannelValue(conf.supybot.plugins.Relay, 'detectOtherRelayBots',
|
|
|
|
registry.Boolean(False, """Determines whether the bot will detect other
|
|
|
|
bots relaying and respond by kickbanning them."""))
|
2004-02-14 01:56:17 +01:00
|
|
|
conf.registerGlobalValue(conf.supybot.plugins.Relay, 'channels',
|
|
|
|
conf.SpaceSeparatedSetOfChannels([], """Determines which channels the bot
|
|
|
|
will relay in."""))
|
2003-12-02 14:39:30 +01:00
|
|
|
|
2004-01-30 05:54:15 +01:00
|
|
|
class Relay(callbacks.Privmsg):
|
2003-11-04 18:34:48 +01:00
|
|
|
noIgnore = True
|
2003-10-22 07:15:19 +02:00
|
|
|
priority = sys.maxint
|
2003-03-26 09:39:50 +01:00
|
|
|
def __init__(self):
|
|
|
|
callbacks.Privmsg.__init__(self)
|
2004-04-17 19:14:59 +02:00
|
|
|
self._whois = {}
|
|
|
|
self.relayedMsgs = {}
|
2004-07-27 07:19:14 +02:00
|
|
|
self.lastmsg = {}
|
|
|
|
self.ircstates = {}
|
2004-08-21 10:43:47 +02:00
|
|
|
self.last20Privmsgs = ircutils.IrcDict()
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-09-22 13:17:10 +02:00
|
|
|
def __call__(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
try:
|
|
|
|
irc = self._getRealIrc(irc)
|
2004-08-23 14:34:16 +02:00
|
|
|
if irc not in self.ircstates:
|
2004-07-27 07:19:14 +02:00
|
|
|
self._addIrc(irc)
|
|
|
|
self.ircstates[irc].addMsg(irc, self.lastmsg[irc])
|
|
|
|
finally:
|
|
|
|
self.lastmsg[irc] = msg
|
2003-09-23 21:39:48 +02:00
|
|
|
callbacks.Privmsg.__call__(self, irc, msg)
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-08-23 14:12:04 +02:00
|
|
|
def do376(self, irc, msg):
|
2004-02-07 23:43:45 +01:00
|
|
|
L = []
|
2004-02-10 04:29:52 +01:00
|
|
|
for channel in self.registryValue('channels'):
|
2004-02-07 23:43:45 +01:00
|
|
|
if channel not in irc.state.channels:
|
|
|
|
L.append(channel)
|
|
|
|
if L:
|
|
|
|
irc.queueMsg(ircmsgs.joins(L))
|
2003-12-16 14:44:09 +01:00
|
|
|
do377 = do422 = do376
|
2003-08-23 14:12:04 +02:00
|
|
|
|
2003-12-17 14:16:42 +01:00
|
|
|
def _getRealIrc(self, irc):
|
|
|
|
if isinstance(irc, irclib.Irc):
|
|
|
|
return irc
|
|
|
|
else:
|
|
|
|
return irc.getRealIrc()
|
|
|
|
|
2004-07-27 07:19:14 +02:00
|
|
|
def _getIrc(self, name):
|
2004-08-23 14:34:16 +02:00
|
|
|
for irc in world.ircs:
|
2004-07-27 07:19:14 +02:00
|
|
|
if self._getIrcName(irc) == name:
|
|
|
|
return irc
|
|
|
|
raise KeyError, name
|
2003-04-06 11:17:38 +02:00
|
|
|
|
2004-07-27 07:19:14 +02:00
|
|
|
def _getIrcName(self, irc):
|
|
|
|
# We should allow abbreviations at some point.
|
|
|
|
return irc.network
|
2003-03-26 09:39:50 +01:00
|
|
|
|
2004-07-27 07:19:14 +02:00
|
|
|
def _addIrc(self, irc):
|
|
|
|
# Let's just be extra-special-careful here.
|
|
|
|
if irc not in self.ircstates:
|
|
|
|
self.ircstates[irc] = irclib.IrcState()
|
|
|
|
if irc not in self.lastmsg:
|
|
|
|
self.lastmsg[irc] = ircmsgs.ping('this is just a fake message')
|
2004-03-30 23:14:34 +02:00
|
|
|
|
2003-10-21 06:09:48 +02:00
|
|
|
def join(self, irc, msg, args):
|
2003-04-06 11:17:38 +02:00
|
|
|
"""<channel>
|
|
|
|
|
|
|
|
Starts relaying between the channel <channel> on all networks. If on a
|
|
|
|
network the bot isn't in <channel>, he'll join. This commands is
|
|
|
|
required even if the bot is in the channel on both networks; he won't
|
2004-05-11 00:17:45 +02:00
|
|
|
relay between those channels unless he's told to join both
|
2003-04-06 11:17:38 +02:00
|
|
|
channels.
|
|
|
|
"""
|
2003-03-27 09:24:22 +01:00
|
|
|
channel = privmsgs.getArgs(args)
|
2003-10-30 05:06:46 +01:00
|
|
|
if not ircutils.isChannel(channel):
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.error('%r is not a valid channel.' % channel)
|
2003-10-30 05:06:46 +01:00
|
|
|
return
|
2004-07-27 07:19:14 +02:00
|
|
|
self.registryValue('channels').add(channel)
|
2004-08-23 14:34:16 +02:00
|
|
|
for otherIrc in world.ircs: # Should we abstract this?
|
2003-03-27 09:24:22 +01:00
|
|
|
if channel not in otherIrc.state.channels:
|
|
|
|
otherIrc.queueMsg(ircmsgs.join(channel))
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.replySuccess()
|
2003-10-21 06:09:48 +02:00
|
|
|
join = privmsgs.checkCapability(join, 'owner')
|
2003-03-26 09:39:50 +01:00
|
|
|
|
2003-10-21 06:09:48 +02:00
|
|
|
def part(self, irc, msg, args):
|
2003-04-06 11:17:38 +02:00
|
|
|
"""<channel>
|
|
|
|
|
|
|
|
Ceases relaying between the channel <channel> on all networks. The bot
|
|
|
|
will part from the channel on all networks in which it is on the
|
|
|
|
channel.
|
|
|
|
"""
|
2003-03-27 09:24:22 +01:00
|
|
|
channel = privmsgs.getArgs(args)
|
2003-10-30 05:06:46 +01:00
|
|
|
if not ircutils.isChannel(channel):
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.error('%r is not a valid channel.' % channel)
|
2003-10-30 05:06:46 +01:00
|
|
|
return
|
2004-07-27 07:19:14 +02:00
|
|
|
self.registryValue('channels').remove(channel)
|
2004-08-23 14:34:16 +02:00
|
|
|
for otherIrc in world.ircs:
|
2003-03-27 09:24:22 +01:00
|
|
|
if channel in otherIrc.state.channels:
|
|
|
|
otherIrc.queueMsg(ircmsgs.part(channel))
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.replySuccess()
|
2003-10-21 06:09:48 +02:00
|
|
|
part = privmsgs.checkCapability(part, 'owner')
|
2003-03-26 09:39:50 +01:00
|
|
|
|
2003-12-16 14:44:09 +01:00
|
|
|
def command(self, irc, msg, args):
|
|
|
|
"""<network> <command> [<arg> ...]
|
|
|
|
|
|
|
|
Gives the bot <command> (with its associated <arg>s) on <network>.
|
|
|
|
"""
|
|
|
|
if len(args) < 2:
|
|
|
|
raise callbacks.ArgumentError
|
|
|
|
network = args.pop(0)
|
|
|
|
try:
|
2004-07-27 07:19:14 +02:00
|
|
|
otherIrc = self._getIrc(network)
|
2003-12-16 14:44:09 +01:00
|
|
|
except KeyError:
|
2004-04-05 11:57:36 +02:00
|
|
|
irc.error('I\'m not currently on the network %r.' % network)
|
2003-12-16 14:45:37 +01:00
|
|
|
return
|
2003-12-16 14:44:09 +01:00
|
|
|
Owner = irc.getCallback('Owner')
|
|
|
|
Owner.disambiguate(irc, args)
|
|
|
|
self.Proxy(otherIrc, msg, args)
|
|
|
|
command = privmsgs.checkCapability(command, 'admin')
|
2004-07-21 21:36:35 +02:00
|
|
|
|
2004-03-30 23:14:34 +02:00
|
|
|
def nicks(self, irc, msg, args):
|
2004-08-21 00:32:48 +02:00
|
|
|
"""[<channel>]
|
2003-04-06 11:17:38 +02:00
|
|
|
|
2004-08-21 00:32:48 +02:00
|
|
|
Returns the nicks of the people in the channel on the various networks
|
|
|
|
the bot is connected to. <channel> is only necessary if the message
|
|
|
|
isn't sent on the channel itself.
|
2003-04-06 11:17:38 +02:00
|
|
|
"""
|
2003-12-17 14:16:42 +01:00
|
|
|
realIrc = self._getRealIrc(irc)
|
2003-03-27 22:14:28 +01:00
|
|
|
channel = privmsgs.getChannel(msg, args)
|
2004-02-10 04:29:52 +01:00
|
|
|
if channel not in self.registryValue('channels'):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc.error('I\'m not relaying in %s.' % channel)
|
2003-03-27 22:14:28 +01:00
|
|
|
return
|
|
|
|
users = []
|
2004-08-23 14:34:16 +02:00
|
|
|
for otherIrc in world.ircs:
|
2004-07-28 05:08:03 +02:00
|
|
|
network = self._getIrcName(otherIrc)
|
2003-11-07 15:54:48 +01:00
|
|
|
ops = []
|
|
|
|
halfops = []
|
|
|
|
voices = []
|
|
|
|
usersS = []
|
2004-07-27 07:19:14 +02:00
|
|
|
if network != self._getIrcName(realIrc):
|
2003-11-11 12:37:17 +01:00
|
|
|
try:
|
|
|
|
Channel = otherIrc.state.channels[channel]
|
|
|
|
except KeyError:
|
2004-07-27 07:19:14 +02:00
|
|
|
s = 'Somehow I\'m not in %s on %s.'% (channel, network)
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.error(s)
|
2003-11-11 12:37:17 +01:00
|
|
|
return
|
2003-11-28 19:22:41 +01:00
|
|
|
numUsers = 0
|
2003-11-07 15:54:48 +01:00
|
|
|
for s in Channel.users:
|
|
|
|
s = s.strip()
|
|
|
|
if not s:
|
|
|
|
continue
|
2003-11-28 19:22:41 +01:00
|
|
|
numUsers += 1
|
2003-12-01 13:03:23 +01:00
|
|
|
if s in Channel.ops:
|
2003-11-07 15:54:48 +01:00
|
|
|
ops.append('@%s' % s)
|
|
|
|
elif s in Channel.halfops:
|
|
|
|
halfops.append('%%%s' % s)
|
|
|
|
elif s in Channel.voices:
|
|
|
|
voices.append('+%s' % s)
|
|
|
|
else:
|
|
|
|
usersS.append(s)
|
2003-11-21 12:45:43 +01:00
|
|
|
utils.sortBy(ircutils.toLower, ops)
|
|
|
|
utils.sortBy(ircutils.toLower, voices)
|
|
|
|
utils.sortBy(ircutils.toLower, halfops)
|
|
|
|
utils.sortBy(ircutils.toLower, usersS)
|
2003-11-15 05:37:04 +01:00
|
|
|
usersS = ', '.join(ifilter(None, imap(', '.join,
|
|
|
|
(ops,halfops,voices,usersS))))
|
2004-07-21 21:36:35 +02:00
|
|
|
users.append('%s (%s): %s' %
|
2004-07-27 07:19:14 +02:00
|
|
|
(ircutils.bold(network), numUsers, usersS))
|
2003-11-28 19:22:41 +01:00
|
|
|
users.sort()
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.reply('; '.join(users))
|
2003-06-22 18:17:33 +02:00
|
|
|
|
2003-10-21 06:09:48 +02:00
|
|
|
def whois(self, irc, msg, args):
|
2003-06-22 18:17:33 +02:00
|
|
|
"""<nick>@<network>
|
|
|
|
|
|
|
|
Returns the WHOIS response <network> gives for <nick>.
|
|
|
|
"""
|
|
|
|
nickAtNetwork = privmsgs.getArgs(args)
|
2003-12-17 14:16:42 +01:00
|
|
|
realIrc = self._getRealIrc(irc)
|
2003-08-12 10:48:16 +02:00
|
|
|
try:
|
|
|
|
(nick, network) = nickAtNetwork.split('@', 1)
|
2003-09-29 07:34:02 +02:00
|
|
|
if not ircutils.isNick(nick):
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.error('%s is not an IRC nick.' % nick)
|
2003-09-29 07:34:02 +02:00
|
|
|
return
|
2003-08-23 14:12:04 +02:00
|
|
|
nick = ircutils.toLower(nick)
|
2004-07-27 07:19:14 +02:00
|
|
|
except ValueError: # If split doesn't work, we get an unpack error.
|
2004-08-23 14:34:16 +02:00
|
|
|
if len(world.ircs) == 2:
|
2003-08-25 08:48:28 +02:00
|
|
|
# If there are only two networks being relayed, we can safely
|
|
|
|
# pick the *other* one.
|
|
|
|
nick = ircutils.toLower(nickAtNetwork)
|
2004-08-23 14:34:16 +02:00
|
|
|
for otherIrc in world.ircs:
|
2004-07-27 07:19:14 +02:00
|
|
|
if otherIrc != realIrc:
|
|
|
|
network = self._getIrcName(otherIrc)
|
2003-08-25 08:48:28 +02:00
|
|
|
else:
|
|
|
|
raise callbacks.ArgumentError
|
2004-07-27 07:19:14 +02:00
|
|
|
try:
|
|
|
|
otherIrc = self._getIrc(network)
|
|
|
|
except KeyError:
|
2004-01-08 04:12:14 +01:00
|
|
|
irc.error('I\'m not on that network.')
|
2003-06-22 18:17:33 +02:00
|
|
|
return
|
2003-08-21 18:31:37 +02:00
|
|
|
otherIrc.queueMsg(ircmsgs.whois(nick, nick))
|
2003-10-21 09:19:53 +02:00
|
|
|
self._whois[(otherIrc, nick)] = (irc, msg, {})
|
2003-06-22 18:17:33 +02:00
|
|
|
|
2004-08-10 17:40:21 +02:00
|
|
|
def ignore(self, irc, msg, args):
|
|
|
|
"""[<channel>] <nick|hostmask>
|
|
|
|
|
|
|
|
Ignores everything said or done by <nick|hostmask> in <channel>.
|
|
|
|
<channel> is only necessary if the message isn't sent in the channel
|
|
|
|
itself.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2003-06-22 18:17:33 +02:00
|
|
|
def do311(self, irc, msg):
|
2003-12-17 14:16:42 +01:00
|
|
|
irc = self._getRealIrc(irc)
|
2003-08-23 14:12:04 +02:00
|
|
|
nick = ircutils.toLower(msg.args[1])
|
2003-10-21 09:19:53 +02:00
|
|
|
if (irc, nick) not in self._whois:
|
2003-06-22 18:17:33 +02:00
|
|
|
return
|
|
|
|
else:
|
2003-10-21 09:19:53 +02:00
|
|
|
self._whois[(irc, nick)][-1][msg.command] = msg
|
2003-06-22 18:17:33 +02:00
|
|
|
|
2004-07-27 07:19:14 +02:00
|
|
|
# These are all sent by a WHOIS response.
|
2003-12-09 15:35:54 +01:00
|
|
|
do301 = do311
|
2003-06-22 18:17:33 +02:00
|
|
|
do312 = do311
|
|
|
|
do317 = do311
|
|
|
|
do319 = do311
|
2003-12-09 15:35:54 +01:00
|
|
|
do320 = do311
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-06-22 18:17:33 +02:00
|
|
|
def do318(self, irc, msg):
|
2003-12-17 14:16:42 +01:00
|
|
|
irc = self._getRealIrc(irc)
|
2004-01-02 21:59:14 +01:00
|
|
|
nick = msg.args[1]
|
|
|
|
loweredNick = ircutils.toLower(nick)
|
|
|
|
if (irc, loweredNick) not in self._whois:
|
2003-06-22 18:17:33 +02:00
|
|
|
return
|
2004-01-02 21:59:14 +01:00
|
|
|
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
2003-08-12 10:48:16 +02:00
|
|
|
hostmask = '@'.join(d['311'].args[2:4])
|
2003-08-21 18:31:37 +02:00
|
|
|
user = d['311'].args[-1]
|
2003-08-26 18:40:31 +02:00
|
|
|
if '319' in d:
|
|
|
|
channels = d['319'].args[-1].split()
|
2003-10-28 15:08:11 +01:00
|
|
|
ops = []
|
|
|
|
voices = []
|
|
|
|
normal = []
|
|
|
|
halfops = []
|
|
|
|
for channel in channels:
|
|
|
|
if channel.startswith('@'):
|
2003-11-04 09:50:10 +01:00
|
|
|
ops.append(channel[1:])
|
2003-10-28 15:08:11 +01:00
|
|
|
elif channel.startswith('%'):
|
2003-11-04 09:50:10 +01:00
|
|
|
halfops.append(channel[1:])
|
2003-10-28 15:08:11 +01:00
|
|
|
elif channel.startswith('+'):
|
2003-11-04 09:50:10 +01:00
|
|
|
voices.append(channel[1:])
|
2003-10-28 15:08:11 +01:00
|
|
|
else:
|
|
|
|
normal.append(channel)
|
|
|
|
L = []
|
|
|
|
if ops:
|
|
|
|
L.append('is an op on %s' % utils.commaAndify(ops))
|
|
|
|
if halfops:
|
|
|
|
L.append('is a halfop on %s' % utils.commaAndify(halfops))
|
|
|
|
if voices:
|
|
|
|
L.append('is voiced on %s' % utils.commaAndify(voices))
|
|
|
|
if L:
|
|
|
|
L.append('is also on %s' % utils.commaAndify(normal))
|
|
|
|
else:
|
|
|
|
L.append('is on %s' % utils.commaAndify(normal))
|
2003-08-12 10:48:16 +02:00
|
|
|
else:
|
2003-10-28 15:08:11 +01:00
|
|
|
L = ['isn\'t on any non-secret channels']
|
|
|
|
channels = utils.commaAndify(L)
|
2003-08-12 10:48:16 +02:00
|
|
|
if '317' in d:
|
|
|
|
idle = utils.timeElapsed(d['317'].args[2])
|
2004-01-18 08:58:26 +01:00
|
|
|
signon = time.strftime(conf.supybot.humanTimestampFormat(),
|
2003-08-26 13:15:15 +02:00
|
|
|
time.localtime(float(d['317'].args[3])))
|
2003-08-12 10:48:16 +02:00
|
|
|
else:
|
2003-08-21 18:31:37 +02:00
|
|
|
idle = '<unknown>'
|
|
|
|
signon = '<unknown>'
|
2003-10-16 12:58:31 +02:00
|
|
|
if '312' in d:
|
|
|
|
server = d['312'].args[2]
|
|
|
|
else:
|
|
|
|
server = '<unknown>'
|
2003-12-09 15:35:54 +01:00
|
|
|
if '301' in d:
|
2003-12-09 15:56:45 +01:00
|
|
|
away = ' %s is away: %s.' % (nick, d['301'].args[2])
|
2003-12-09 15:35:54 +01:00
|
|
|
else:
|
|
|
|
away = ''
|
|
|
|
if '320' in d:
|
|
|
|
if d['320'].args[2]:
|
|
|
|
identify = ' identified'
|
|
|
|
else:
|
|
|
|
identify = ''
|
|
|
|
else:
|
|
|
|
identify = ''
|
2004-01-09 00:03:48 +01:00
|
|
|
s = '%s (%s) has been%s on server %s since %s (idle for %s) and ' \
|
2003-12-09 15:35:54 +01:00
|
|
|
'%s.%s' % (user, hostmask, identify, server, signon, idle,
|
|
|
|
channels, away)
|
2004-01-09 01:03:59 +01:00
|
|
|
replyIrc.reply(s)
|
2004-01-02 21:59:14 +01:00
|
|
|
del self._whois[(irc, loweredNick)]
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-08-26 19:55:30 +02:00
|
|
|
def do402(self, irc, msg):
|
2003-12-17 14:16:42 +01:00
|
|
|
irc = self._getRealIrc(irc)
|
2004-01-02 21:59:14 +01:00
|
|
|
nick = msg.args[1]
|
|
|
|
loweredNick = ircutils.toLower(nick)
|
|
|
|
if (irc, loweredNick) not in self._whois:
|
2003-08-26 19:55:30 +02:00
|
|
|
return
|
2004-01-02 21:59:14 +01:00
|
|
|
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
|
|
|
del self._whois[(irc, loweredNick)]
|
2004-07-27 07:19:14 +02:00
|
|
|
s = 'There is no %s on %s.' % (nick, self._getIrcName(irc))
|
2004-01-09 01:03:59 +01:00
|
|
|
replyIrc.reply(s)
|
2003-08-26 19:55:30 +02:00
|
|
|
|
2003-08-28 18:31:56 +02:00
|
|
|
do401 = do402
|
|
|
|
|
2003-08-15 07:41:51 +02:00
|
|
|
def _formatPrivmsg(self, nick, network, msg):
|
2004-08-10 17:40:21 +02:00
|
|
|
channel = msg.args[0]
|
|
|
|
if self.registryValue('includeNetwork', channel):
|
|
|
|
network = '@' + network
|
|
|
|
else:
|
|
|
|
network = ''
|
2003-07-23 07:29:16 +02:00
|
|
|
# colorize nicks
|
2004-08-10 17:45:26 +02:00
|
|
|
color = self.registryValue('color', channel) # Also used further down.
|
|
|
|
if color:
|
2004-07-31 10:54:03 +02:00
|
|
|
nick = ircutils.IrcString(nick)
|
2004-08-02 20:03:27 +02:00
|
|
|
newnick = ircutils.mircColor(nick, *ircutils.canonicalColor(nick))
|
2003-08-21 13:19:32 +02:00
|
|
|
colors = ircutils.canonicalColor(nick, shift=4)
|
2004-08-02 20:03:27 +02:00
|
|
|
nick = newnick
|
2003-03-27 09:17:51 +01:00
|
|
|
if ircmsgs.isAction(msg):
|
2003-10-30 03:03:49 +01:00
|
|
|
if color:
|
2003-08-21 13:19:32 +02:00
|
|
|
t = ircutils.mircColor('*', *colors)
|
|
|
|
else:
|
|
|
|
t = '*'
|
2004-08-10 17:40:21 +02:00
|
|
|
s = '%s %s%s %s' % (t, nick, network, ircmsgs.unAction(msg))
|
2003-03-27 09:17:51 +01:00
|
|
|
else:
|
2003-10-30 03:03:49 +01:00
|
|
|
if color:
|
2003-08-21 13:19:32 +02:00
|
|
|
lt = ircutils.mircColor('<', *colors)
|
|
|
|
gt = ircutils.mircColor('>', *colors)
|
|
|
|
else:
|
|
|
|
lt = '<'
|
|
|
|
gt = '>'
|
2004-08-10 17:40:21 +02:00
|
|
|
s = '%s%s%s%s %s' % (lt, nick, network, gt, msg.args[1])
|
2003-08-12 10:48:16 +02:00
|
|
|
return s
|
2003-03-27 09:17:51 +01:00
|
|
|
|
2004-04-18 02:35:54 +02:00
|
|
|
def _addRelayedMsg(self, msg):
|
|
|
|
try:
|
|
|
|
self.relayedMsgs[msg] += 1
|
|
|
|
except KeyError:
|
|
|
|
self.relayedMsgs[msg] = 1
|
2004-07-21 21:36:35 +02:00
|
|
|
|
2003-10-10 08:10:50 +02:00
|
|
|
def _sendToOthers(self, irc, msg):
|
2004-01-19 21:21:30 +01:00
|
|
|
assert msg.command == 'PRIVMSG' or msg.command == 'TOPIC'
|
2004-08-23 14:34:16 +02:00
|
|
|
for otherIrc in world.ircs:
|
2003-10-10 08:10:50 +02:00
|
|
|
if otherIrc != irc:
|
|
|
|
if msg.args[0] in otherIrc.state.channels:
|
2004-04-18 02:35:54 +02:00
|
|
|
self._addRelayedMsg(msg)
|
2003-10-10 08:10:50 +02:00
|
|
|
otherIrc.queueMsg(msg)
|
|
|
|
|
2004-08-21 10:43:47 +02:00
|
|
|
def _detectRelays(self, irc, msg, channel):
|
|
|
|
def isRelayPrefix(s):
|
|
|
|
return s and s[0] == '<' and s[-1] == '>'
|
|
|
|
def punish():
|
|
|
|
punished = False
|
2004-08-23 14:34:16 +02:00
|
|
|
for irc in world.ircs:
|
2004-08-21 10:43:47 +02:00
|
|
|
if irc.nick in irc.state.channels[channel].ops:
|
|
|
|
self.log.info('Punishing %s in %s on %s for relaying.',
|
|
|
|
msg.prefix, channel, irc.network)
|
|
|
|
irc.sendMsg(ircmsgs.ban(channel, msg.prefix))
|
|
|
|
kmsg = 'You seem to be relaying, punk.'
|
|
|
|
irc.sendMsg(ircmsgs.kick(channel, msg.nick, kmsg))
|
|
|
|
punished = True
|
|
|
|
else:
|
|
|
|
self.log.warning('Can\'t punish %s in %s on %s; '
|
|
|
|
'I\'m not opped.',
|
|
|
|
msg.prefix, channel, irc.network)
|
|
|
|
return punished
|
|
|
|
if channel not in self.last20Privmsgs:
|
|
|
|
self.last20Privmsgs[channel] = RingBuffer(20)
|
|
|
|
s = ircutils.stripFormatting(msg.args[1])
|
|
|
|
s = utils.normalizeWhitespace(s)
|
|
|
|
try:
|
|
|
|
(prefix, suffix) = s.split(None, 1)
|
|
|
|
except ValueError, e:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if isRelayPrefix(prefix):
|
|
|
|
parts = suffix.split()
|
|
|
|
while parts and isRelayPrefix(parts[0]):
|
|
|
|
parts.pop(0)
|
|
|
|
suffix = ' '.join(parts)
|
|
|
|
for m in self.last20Privmsgs[channel]:
|
|
|
|
if suffix in m:
|
|
|
|
who = msg.prefix
|
|
|
|
self.log.info('%s seems to be relaying too.', who)
|
|
|
|
if punish():
|
|
|
|
self.log.info('Successfully punished %s.', who)
|
|
|
|
else:
|
|
|
|
self.log.info('Unsuccessfully attempted to '
|
|
|
|
'punish %s.', who)
|
|
|
|
break
|
|
|
|
self.last20Privmsgs[channel].append(s)
|
|
|
|
|
2003-03-26 09:39:50 +01:00
|
|
|
def doPrivmsg(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
(channel, text) = msg.args
|
|
|
|
if ircutils.isChannel(channel):
|
2003-12-17 14:16:42 +01:00
|
|
|
irc = self._getRealIrc(irc)
|
2004-02-10 04:29:52 +01:00
|
|
|
if channel not in self.registryValue('channels'):
|
2004-01-01 20:10:20 +01:00
|
|
|
return
|
2004-07-31 05:45:25 +02:00
|
|
|
if ircmsgs.isCtcp(msg) and \
|
2004-07-27 07:19:14 +02:00
|
|
|
'AWAY' not in text and 'ACTION' not in text:
|
2003-03-27 21:10:10 +01:00
|
|
|
return
|
2004-08-21 10:43:47 +02:00
|
|
|
# Let's try to detect other relay bots.
|
|
|
|
if self.registryValue('detectOtherRelayBots', channel):
|
|
|
|
self._detectRelays(irc, msg, channel)
|
2004-07-27 07:19:14 +02:00
|
|
|
network = self._getIrcName(irc)
|
|
|
|
s = self._formatPrivmsg(msg.nick, network, msg)
|
2003-10-10 08:10:50 +02:00
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2004-08-05 06:01:52 +02:00
|
|
|
|
2003-03-26 09:39:50 +01:00
|
|
|
def doJoin(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
channel = msg.args[0]
|
|
|
|
if channel not in self.registryValue('channels'):
|
|
|
|
return
|
|
|
|
network = self._getIrcName(irc)
|
2004-08-08 21:34:15 +02:00
|
|
|
if self.registryValue('hostmasks', channel):
|
|
|
|
hostmask = ' (%s)' % msg.prefix
|
2004-08-05 06:01:52 +02:00
|
|
|
else:
|
2004-08-08 21:34:15 +02:00
|
|
|
hostmask = ''
|
|
|
|
s = '%s%s has joined on %s' % (msg.nick, hostmask, network)
|
2004-07-27 07:19:14 +02:00
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-03-26 09:39:50 +01:00
|
|
|
|
|
|
|
def doPart(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
channel = msg.args[0]
|
|
|
|
if channel not in self.registryValue('channels'):
|
|
|
|
return
|
|
|
|
network = self._getIrcName(irc)
|
2004-08-08 21:34:15 +02:00
|
|
|
if self.registryValue('hostmasks', channel):
|
2004-08-05 07:57:53 +02:00
|
|
|
hostmask = ' (%s)' % msg.prefix
|
2004-08-08 21:34:15 +02:00
|
|
|
else:
|
|
|
|
hostmask = ''
|
2004-08-05 06:01:52 +02:00
|
|
|
s = '%s%s has left on %s' % (msg.nick, hostmask, network)
|
2004-07-27 07:19:14 +02:00
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-03-26 09:39:50 +01:00
|
|
|
|
2003-03-31 09:04:23 +02:00
|
|
|
def doMode(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
channel = msg.args[0]
|
|
|
|
if channel not in self.registryValue('channels'):
|
|
|
|
return
|
|
|
|
network = self._getIrcName(irc)
|
|
|
|
s = 'mode change by %s on %s: %s' % \
|
|
|
|
(msg.nick, network, ' '.join(msg.args[1:]))
|
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-09-03 20:42:52 +02:00
|
|
|
|
|
|
|
def doKick(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
channel = msg.args[0]
|
|
|
|
if channel not in self.registryValue('channels'):
|
|
|
|
return
|
|
|
|
network = self._getIrcName(irc)
|
|
|
|
if len(msg.args) == 3:
|
|
|
|
s = '%s was kicked by %s on %s (%s)' % \
|
|
|
|
(msg.args[1], msg.nick, network, msg.args[2])
|
|
|
|
else:
|
|
|
|
s = '%s was kicked by %s on %s' % \
|
|
|
|
(msg.args[1], msg.nick, network)
|
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-03-31 09:04:23 +02:00
|
|
|
def doNick(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
newNick = msg.args[0]
|
|
|
|
network = self._getIrcName(irc)
|
|
|
|
s = 'nick change by %s to %s on %s' % (msg.nick, newNick, network)
|
|
|
|
for channel in self.registryValue('channels'):
|
|
|
|
if newNick in irc.state.channels[channel].users:
|
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-10-10 08:10:50 +02:00
|
|
|
|
|
|
|
def doTopic(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
if msg.nick == irc.nick:
|
|
|
|
return
|
|
|
|
(channel, newTopic) = msg.args
|
|
|
|
if channel not in self.registryValue('channels'):
|
|
|
|
return
|
|
|
|
network = self._getIrcName(irc)
|
|
|
|
if self.registryValue('topicSync', channel):
|
|
|
|
m = ircmsgs.topic(channel, newTopic)
|
|
|
|
else:
|
|
|
|
s = 'topic change by %s on %s: %s' % (msg.nick, network, newTopic)
|
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-04-01 09:09:36 +02:00
|
|
|
|
|
|
|
def doQuit(self, irc, msg):
|
2004-07-27 07:19:14 +02:00
|
|
|
irc = self._getRealIrc(irc)
|
|
|
|
network = self._getIrcName(irc)
|
|
|
|
if msg.args:
|
|
|
|
s = '%s has quit %s (%s)' % (msg.nick, network, msg.args[0])
|
|
|
|
else:
|
|
|
|
s = '%s has quit %s.' % (msg.nick, network)
|
|
|
|
for channel in self.registryValue('channels'):
|
|
|
|
if msg.nick in self.ircstates[irc].channels[channel].users:
|
|
|
|
m = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, m)
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2004-04-18 02:35:54 +02:00
|
|
|
def _isRelayedPrivmsg(self, msg):
|
|
|
|
if msg in self.relayedMsgs:
|
|
|
|
self.relayedMsgs[msg] -= 1
|
|
|
|
if not self.relayedMsgs[msg]:
|
|
|
|
del self.relayedMsgs[msg]
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
2004-07-21 21:36:35 +02:00
|
|
|
|
2003-03-27 09:17:51 +01:00
|
|
|
def outFilter(self, irc, msg):
|
2003-12-17 14:16:42 +01:00
|
|
|
irc = self._getRealIrc(irc)
|
2003-03-27 09:17:51 +01:00
|
|
|
if msg.command == 'PRIVMSG':
|
2004-04-18 02:35:54 +02:00
|
|
|
if not self._isRelayedPrivmsg(msg):
|
2003-03-27 09:17:51 +01:00
|
|
|
channel = msg.args[0]
|
2004-02-10 04:29:52 +01:00
|
|
|
if channel in self.registryValue('channels'):
|
2004-07-27 07:19:14 +02:00
|
|
|
network = self._getIrcName(irc)
|
|
|
|
s = self._formatPrivmsg(irc.nick, network, msg)
|
2004-04-18 02:35:54 +02:00
|
|
|
relayMsg = ircmsgs.privmsg(channel, s)
|
|
|
|
self._sendToOthers(irc, relayMsg)
|
2003-12-10 09:27:30 +01:00
|
|
|
elif msg.command == 'TOPIC' and len(msg.args) > 1 and \
|
2004-01-30 05:54:15 +01:00
|
|
|
self.registryValue('topicSync', msg.args[0]):
|
2003-03-28 06:36:59 +01:00
|
|
|
(channel, topic) = msg.args
|
2004-02-10 04:29:52 +01:00
|
|
|
if channel in self.registryValue('channels'):
|
2004-08-23 14:34:16 +02:00
|
|
|
for otherIrc in world.ircs:
|
2003-03-27 09:17:51 +01:00
|
|
|
if otherIrc != irc:
|
2004-01-15 12:25:04 +01:00
|
|
|
try:
|
|
|
|
if otherIrc.state.getTopic(channel) != topic:
|
|
|
|
otherIrc.queueMsg(ircmsgs.topic(channel,topic))
|
|
|
|
except KeyError:
|
2004-08-23 14:34:16 +02:00
|
|
|
self.log.warning('Not on %s on %s -- '
|
2004-01-15 12:25:04 +01:00
|
|
|
'Can\'t synchronize topics.',
|
|
|
|
channel, otherIrc.server)
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-03-27 09:26:36 +01:00
|
|
|
return msg
|
2003-03-27 09:17:51 +01:00
|
|
|
|
2003-03-26 09:39:50 +01:00
|
|
|
Class = Relay
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-03-24 09:41:19 +01:00
|
|
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|