17757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Copyright (C) 1999--2002  Joel Rosdahl
27757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#
37757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# This library is free software; you can redistribute it and/or
47757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# modify it under the terms of the GNU Lesser General Public
57757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# License as published by the Free Software Foundation; either
67757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# version 2.1 of the License, or (at your option) any later version.
77757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#
87757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# This library is distributed in the hope that it will be useful,
97757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# but WITHOUT ANY WARRANTY; without even the implied warranty of
107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Lesser General Public License for more details.
127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#
137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# You should have received a copy of the GNU Lesser General Public
147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# License along with this library; if not, write to the Free Software
157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#
177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Joel Rosdahl <joel@rosdahl.net>
187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#
197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# $Id: ircbot.py,v 1.23 2008/09/11 07:38:30 keltus Exp $
207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch"""ircbot -- Simple IRC bot library.
227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochThis module contains a single-server IRC bot class that can be used to
247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochwrite simpler bots.
257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch"""
267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport sys
287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom UserDict import UserDict
297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom irclib import SimpleIRCClient
317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom irclib import nm_to_n, irc_lower, all_events
327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom irclib import parse_channel_modes, is_channel
337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom irclib import ServerConnectionError
347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass SingleServerIRCBot(SimpleIRCClient):
367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    """A single-server IRC bot class.
377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    The bot tries to reconnect if it is disconnected.
397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    The bot keeps track of the channels it has joined, the other
417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    clients that are present in the channels and which of those that
427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    have operator or voice modes.  The "database" is kept in the
437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    self.channels attribute, which is an IRCDict of Channels.
447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    """
457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __init__(self, server_list, nickname, realname, reconnection_interval=60):
467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Constructor for SingleServerIRCBot objects.
477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Arguments:
497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            server_list -- A list of tuples (server, port) that
517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                           defines which servers the bot should try to
527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                           connect to.
537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            nickname -- The bot's nickname.
557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            realname -- The bot's realname.
577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            reconnection_interval -- How long the bot should wait
597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                                     before trying to reconnect.
607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            dcc_connections -- A list of initiated/accepted DCC
627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            connections.
637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        SimpleIRCClient.__init__(self)
667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.channels = IRCDict()
677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.server_list = server_list
687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if not reconnection_interval or reconnection_interval < 0:
697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            reconnection_interval = 2**31
707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.reconnection_interval = reconnection_interval
717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self._nickname = nickname
737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self._realname = realname
747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        for i in ["disconnect", "join", "kick", "mode",
757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                  "namreply", "nick", "part", "quit"]:
767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.connection.add_global_handler(i,
777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                                               getattr(self, "_on_" + i),
787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                                               -10)
797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _connected_checker(self):
807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if not self.connection.is_connected():
827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.connection.execute_delayed(self.reconnection_interval,
837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                                            self._connected_checker)
847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.jump_server()
857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _connect(self):
877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        password = None
897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if len(self.server_list[0]) > 2:
907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            password = self.server_list[0][2]
917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        try:
927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.connect(self.server_list[0][0],
937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                         self.server_list[0][1],
947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                         self._nickname,
957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                         password,
967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                         ircname=self._realname)
977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        except ServerConnectionError:
987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            pass
997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_disconnect(self, c, e):
1017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.channels = IRCDict()
1037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.connection.execute_delayed(self.reconnection_interval,
1047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                                        self._connected_checker)
1057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_join(self, c, e):
1077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        ch = e.target()
1097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        nick = nm_to_n(e.source())
1107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if nick == c.get_nickname():
1117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.channels[ch] = Channel()
1127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.channels[ch].add_user(nick)
1137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_kick(self, c, e):
1157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        nick = e.arguments()[0]
1177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        channel = e.target()
1187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if nick == c.get_nickname():
1207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            del self.channels[channel]
1217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
1227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.channels[channel].remove_user(nick)
1237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_mode(self, c, e):
1257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        modes = parse_channel_modes(" ".join(e.arguments()))
1277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        t = e.target()
1287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if is_channel(t):
1297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            ch = self.channels[t]
1307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            for mode in modes:
1317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                if mode[0] == "+":
1327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                    f = ch.set_mode
1337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                else:
1347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                    f = ch.clear_mode
1357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                f(mode[1], mode[2])
1367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
1377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            # Mode on self... XXX
1387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            pass
1397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_namreply(self, c, e):
1417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # e.arguments()[0] == "@" for secret channels,
1447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #                     "*" for private channels,
1457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #                     "=" for others (public channels)
1467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # e.arguments()[1] == channel
1477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # e.arguments()[2] == nick list
1487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        ch = e.arguments()[1]
1507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        for nick in e.arguments()[2].split():
1517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            if nick[0] == "@":
1527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                nick = nick[1:]
1537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                self.channels[ch].set_mode("o", nick)
1547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            elif nick[0] == "+":
1557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                nick = nick[1:]
1567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                self.channels[ch].set_mode("v", nick)
1577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.channels[ch].add_user(nick)
1587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_nick(self, c, e):
1607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        before = nm_to_n(e.source())
1627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        after = e.target()
1637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        for ch in self.channels.values():
1647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            if ch.has_user(before):
1657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                ch.change_nick(before, after)
1667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_part(self, c, e):
1687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        nick = nm_to_n(e.source())
1707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        channel = e.target()
1717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if nick == c.get_nickname():
1737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            del self.channels[channel]
1747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
1757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.channels[channel].remove_user(nick)
1767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def _on_quit(self, c, e):
1787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """[Internal]"""
1797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        nick = nm_to_n(e.source())
1807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        for ch in self.channels.values():
1817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            if ch.has_user(nick):
1827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                ch.remove_user(nick)
1837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def die(self, msg="Bye, cruel world!"):
1857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Let the bot die.
1867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Arguments:
1887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            msg -- Quit message.
1907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
1917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.connection.disconnect(msg)
1937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        sys.exit(0)
1947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def disconnect(self, msg="I'll be back!"):
1967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Disconnect the bot.
1977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
1987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        The bot will try to reconnect after a while.
1997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Arguments:
2017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            msg -- Quit message.
2037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
2047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.connection.disconnect(msg)
2057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def get_version(self):
2077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Returns the bot version.
2087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Used when answering a CTCP VERSION request.
2107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
2117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return "ircbot.py by Joel Rosdahl <joel@rosdahl.net>"
2127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def jump_server(self, msg="Changing servers"):
2147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Connect to a new server, possibly disconnecting from the current.
2157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        The bot will skip to next server in the server_list each time
2177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        jump_server is called.
2187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
2197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if self.connection.is_connected():
2207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.connection.disconnect(msg)
2217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.server_list.append(self.server_list.pop(0))
2237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self._connect()
2247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def on_ctcp(self, c, e):
2267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Default handler for ctcp events.
2277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Replies to VERSION and PING requests and relays DCC requests
2297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        to the on_dccchat method.
2307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
2317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if e.arguments()[0] == "VERSION":
2327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            c.ctcp_reply(nm_to_n(e.source()),
2337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                         "VERSION " + self.get_version())
2347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        elif e.arguments()[0] == "PING":
2357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            if len(e.arguments()) > 1:
2367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                c.ctcp_reply(nm_to_n(e.source()),
2377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                             "PING " + e.arguments()[1])
2387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        elif e.arguments()[0] == "DCC" and e.arguments()[1].split(" ", 1)[0] == "CHAT":
2397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.on_dccchat(c, e)
2407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def on_dccchat(self, c, e):
2427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        pass
2437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def start(self):
2457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Start the bot."""
2467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self._connect()
2477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        SimpleIRCClient.start(self)
2487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass IRCDict:
2517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    """A dictionary suitable for storing IRC-related things.
2527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    Dictionary keys a and b are considered equal if and only if
2547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    irc_lower(a) == irc_lower(b)
2557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    Otherwise, it should behave exactly as a normal dictionary.
2577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    """
2587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
2597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __init__(self, dict=None):
2607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.data = {}
2617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.canon_keys = {}  # Canonical keys
2627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if dict is not None:
2637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.update(dict)
2647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __repr__(self):
2657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return repr(self.data)
2667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __cmp__(self, dict):
2677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if isinstance(dict, IRCDict):
2687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return cmp(self.data, dict.data)
2697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
2707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return cmp(self.data, dict)
2717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __len__(self):
2727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return len(self.data)
2737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __getitem__(self, key):
2747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.data[self.canon_keys[irc_lower(key)]]
2757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __setitem__(self, key, item):
2767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if key in self:
2777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            del self[key]
2787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.data[key] = item
2797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.canon_keys[irc_lower(key)] = key
2807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __delitem__(self, key):
2817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        ck = irc_lower(key)
2827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        del self.data[self.canon_keys[ck]]
2837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        del self.canon_keys[ck]
2847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __iter__(self):
2857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return iter(self.data)
2867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __contains__(self, key):
2877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_key(key)
2887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def clear(self):
2897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.data.clear()
2907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.canon_keys.clear()
2917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def copy(self):
2927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if self.__class__ is UserDict:
2937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return UserDict(self.data)
2947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        import copy
2957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return copy.copy(self)
2967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def keys(self):
2977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.data.keys()
2987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def items(self):
2997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.data.items()
3007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def values(self):
3017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.data.values()
3027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_key(self, key):
3037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return irc_lower(key) in self.canon_keys
3047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def update(self, dict):
3057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        for k, v in dict.items():
3067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.data[k] = v
3077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def get(self, key, failobj=None):
3087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.data.get(key, failobj)
3097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass Channel:
3127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    """A class for keeping information about an IRC channel.
3137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    This class can be improved a lot.
3157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    """
3167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __init__(self):
3187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.userdict = IRCDict()
3197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.operdict = IRCDict()
3207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.voiceddict = IRCDict()
3217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.modes = {}
3227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def users(self):
3247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Returns an unsorted list of the channel's users."""
3257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.userdict.keys()
3267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def opers(self):
3287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Returns an unsorted list of the channel's operators."""
3297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.operdict.keys()
3307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def voiced(self):
3327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Returns an unsorted list of the persons that have voice
3337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        mode set in the channel."""
3347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.voiceddict.keys()
3357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_user(self, nick):
3377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Check whether the channel has a user."""
3387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return nick in self.userdict
3397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def is_oper(self, nick):
3417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Check whether a user has operator status in the channel."""
3427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return nick in self.operdict
3437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def is_voiced(self, nick):
3457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Check whether a user has voice mode set in the channel."""
3467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return nick in self.voiceddict
3477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def add_user(self, nick):
3497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.userdict[nick] = 1
3507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def remove_user(self, nick):
3527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        for d in self.userdict, self.operdict, self.voiceddict:
3537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            if nick in d:
3547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                del d[nick]
3557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def change_nick(self, before, after):
3577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.userdict[after] = 1
3587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        del self.userdict[before]
3597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if before in self.operdict:
3607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.operdict[after] = 1
3617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            del self.operdict[before]
3627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if before in self.voiceddict:
3637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.voiceddict[after] = 1
3647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            del self.voiceddict[before]
3657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def set_mode(self, mode, value=None):
3677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Set mode on the channel.
3687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Arguments:
3707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            mode -- The mode (a single-character string).
3727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            value -- Value
3747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
3757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if mode == "o":
3767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.operdict[value] = 1
3777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        elif mode == "v":
3787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.voiceddict[value] = 1
3797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
3807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            self.modes[mode] = value
3817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def clear_mode(self, mode, value=None):
3837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """Clear mode on the channel.
3847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        Arguments:
3867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            mode -- The mode (a single-character string).
3887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
3897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            value -- Value
3907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        """
3917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        try:
3927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            if mode == "o":
3937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                del self.operdict[value]
3947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            elif mode == "v":
3957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                del self.voiceddict[value]
3967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            else:
3977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch                del self.modes[mode]
3987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        except KeyError:
3997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            pass
4007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_mode(self, mode):
4027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return mode in self.modes
4037757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4047757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def is_moderated(self):
4057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("m")
4067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def is_secret(self):
4087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("s")
4097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def is_protected(self):
4117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("p")
4127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_topic_lock(self):
4147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("t")
4157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def is_invite_only(self):
4177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("i")
4187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_allow_external_messages(self):
4207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("n")
4217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_limit(self):
4237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("l")
4247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def limit(self):
4267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if self.has_limit():
4277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return self.modes[l]
4287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
4297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return None
4307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def has_key(self):
4327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.has_mode("k")
4337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
4347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def key(self):
4357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        if self.has_key():
4367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return self.modes["k"]
4377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        else:
4387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch            return None
439