1
### This file is part of KoFooBot and is licensed under BSD-license according to the   ###
2
### LICENSE file in the base directory.                                                ###
3
### Code in this file is contributed by:                                               ###
4
###    Krister Svanlund <krister.svanlund gmail.com>                                   ###
5
6
import re, irclib
7
8
#####
9
# Module interface
10
##
11
12
services_settings = { 'channel_passwords': {} }
13
14
do_register_command = { 'description': "",
15
                        'long help': """\
16
""",
17
                        'arguments': [("nick [<email>]", "Register with nickserv."),
18
                                      ("channel <channel> <password> <description>", "Register channel with chanserv.")],
19
                        'public': False,
20
                        'level': 100}
21
22
owned_channels = []
23
getting_alist_from = None
24
waiting_for_pass_for = None
25
26
def init_services(bot, server, sender = None):
27
    if type(bot.settings.bot_channel_list) is dict:
28
        bot.settings.services_channel_passwords.extend(bot.settings.bot_channel_list)
29
        bot.settings.bot_channel_list = bot.settings.bot_channel_list.keys()
30
        
31
    identify_with_nickserv(bot, server)
32
    
33
    for channel in bot.channel_list:
34
        identify_with_chanserv(bot, server, channel)
35
        request_access_list(bot, server, channel)
36
        
37
    return True
38
39
def services_private_notice_handler(bot, server, sender, msg):
40
    sender_nick = irclib.nm_to_n(sender)
41
    if sender_nick.lower() == "nickserv":
42
        return handle_nickserv_notice(bot, server, msg)
43
    elif sender_nick.lower() == "chanserv":
44
        return handle_chanserv_notice(bot, server, msg)
45
46
def services_join_part_handler(bot, server, channel, join):
47
    if join:
48
        ### Handle join event ###
49
        identify_with_chanserv(bot, server, channel)
50
        request_access_list(bot, server, channel)
51
    else:
52
        ### Handle part event ###
53
        if channel in owned_channels:
54
            owned_channels.remove(channel)
55
56
def do_register(bot, server, sender, target, args):
57
    """Register either nick och channel with respective service."""
58
    if args:
59
        if args[0] == 'nick':
60
            email = bot.settings.bot_nick_email
61
            if args[1:]:
62
                email = args[1]
63
            register_with_nickserv(bot, server, email, irclib.nm_to_n(sender))
64
            return True
65
        elif args[0] == 'channel':
66
            if len(args) > 4: ### Thats 'channel', '<channel>', '<password>', '<Channel description>'
67
                channel = args[1]
68
                password = args[2]
69
                description = ' '.join(args[3:])
70
                if not channel in bot.channel_list:
71
                    bot.respond(server, sender, target, "Can not register channel without joining it.")
72
                    print " - Bot not in channel '%s'." % channel
73
                if password == '*':
74
                    if channel in bot.settings.services_channel_passwords:
75
                        password = bot.settings.services_channel_passwords[channel]
76
                        print " + Registering %s with the password '%s'." (channel, password)
77
                    else:
78
                        bot.respond(server, sender, target, "No password to use. Could not register.")
79
                        print " - No password used, could not register channel."
80
                        return False
81
                register_with_chanserv(bot, server, channel, password, description)
82
            return True
83
    else:
84
        bot.respond(server, sender, target, "Not enough arguments.")
85
        return False
86
        
87
            
88
##
89
# End of module interface
90
#####
91
92
def identify_with_nickserv(bot, server):
93
    print "Identifying with nickserv..."
94
95
    server.privmsg("nickserv", "identify %s" % bot.settings.bot_nick_password)
96
97
def identify_with_chanserv(bot, server, channel):
98
    print "Identifying with chanserv for channel '%s'..." % channel
99
    chanpass = bot.settings.services_channel_passwords.get(channel, "")
100
    if chanpass:
101
        server.privmsg("chanserv", "identify %s %s" % (channel, chanpass))
102
    else:
103
        print " - Has no password for '%s'." % channel
104
105
def register_with_nickserv(bot, server, email, sender_nick = None):
106
    print "Register %s with nickserv." % bot.settings.bot_nick_name
107
108
    if bot.settings.bot_nick_password:
109
        server.privmsg("nickserv", "register %s %s" % (bot.settings.bot_nick_password, email))
110
        if sender:
111
            bot.privmsg(sender_nick, "Register nickname '%s' with nickserv using e-mail '%s'." % (bot.settings.bot_nick_name, email))
112
        print " + Register nickname '%s' with nickserv using e-mail '%s'." % (bot.settings.bot_nick_name, email)
113
    else:
114
        if sender:
115
            bot.privmsg(sender_nick, "Nick password is not set. Could not register.")
116
        print " - No nick password, could not register."
117
118
def register_with_chanserv(bot, server, channel, password, description):
119
    print "Register %s with chanserv using description '%s'." % (channel, description)
120
    server.privmsg("chanserv", "register %s %s %s" % (channel, password, description))
121
122
def make_op(bot, server, channel, user):
123
    """Ask chanserv to op a user."""
124
    print "Make %s an operator on %s." % (user, channel)
125
126
    server.privmsg("chanserv", "op %s %s" % (channel, user))
127
128
def request_access_list(bot, server, channel):
129
    """Ask chanserv for access list for channel."""
130
    print "Request access list for '%s'." % channel
131
132
    server.privmsg("chanserv", "access %s list" % channel)
133
134
def handle_nickserv_notice(bot, server, msg):
135
    print "Notice from nickserv."
136
    if msg == "Your nickname isn't registered.":
137
        print " - Nickname not registered."
138
        bot.tell_users(server, "Nickname is not registered.", level=100, notice=True)
139
        register_with_nickserv(bot, server, bot.settings.bot_nick_email)
140
        return True
141
    
142
    if msg[:17] == "Password accepted":
143
        print " + Successfully identified with nickserv."
144
        bot.tell_users(server, "Successfully identified with nickserv.", level=100, notice=True)
145
        return True
146
147
    return False
148
149
def handle_chanserv_notice(bot, server, msg):
150
    """On private notice from chanserv, similar to nickserv."""
151
    print "Notice from chanserv."
152
153
    global waiting_for_pass_for
154
    global getting_alist_from
155
156
    res = re.match('\s([0-9]{1,3})\s{2}(\S+)', msg)
157
    if res is not None:
158
        try:
159
            level = int(res.group(1))
160
            user = res.group(2)
161
            users = bot.settings.bot_user_list
162
            old_level = users.get(user, level)
163
            level = max(level, old_level)
164
            users[user] = level
165
            print " + '%s' has been given the level '%d'." % (user, level)
166
            return True
167
        except Exception, e:
168
            print " - Could not parse string '%s': %s" % (msg, str(e))
169
            return False
170
    
171
    res = re.match("Channel \\x02(\S*)\\x02 isn't registered.", msg)
172
    if res is not None:
173
        channel = res.group(1)
174
        print " + Channel %s is not registered." % channel
175
        bot.tell_users(server, "Channel %s is not registered." % channel, level=100, notice=True)
176
        return True
177
178
    res = re.match("Password accepted -- .* \\x02(.*)\\x02.", msg)
179
    if res is not None:
180
        channel = res.group(1)
181
        print " + Is taking control over %s." % channel
182
        owned_channels.append(channel)
183
        make_op(bot, server, channel, bot.settings.bot_nick_name)
184
        bot.tell_users(server, "Is taking control over %s." % channel, level=100, notice=True)
185
        return True
186
187
    res = re.match("Opped \\x02(\S*)\\x02 on channel \\x02(\S*)\\x02.", msg)
188
    if res is not None:
189
        user = res.group(1)
190
        channel = res.group(2)
191
        if user == bot.settings.bot_nick_name:
192
            print " + Control over %s established." % channel
193
        else:
194
            print " + %s has been opped on %s." % (user, channel)
195
        return True
196
197
    res = re.match("\\x02(\S*)\\x02 is already opped on channel \\x02(\S*)\\x02.", msg)
198
    if res is not None:
199
        user = res.group(1)
200
        channel = res.group(2)
201
        print " + %s is already opped in %s." % (user, channel)
202
        return True
203
204
    if waiting_for_pass_for:
205
        res = re.match("Your channel password is \\x02(\S*)\\x02 -- remember it for later use.", msg)
206
        if res is not None:
207
            channel = waiting_for_pass_for
208
            password = res.group(1)
209
            waiting_for_pass_for = None
210
            bot.settings.services_channel_passwords[channel] = password
211
            print " + Got password for %s: %s" % (channel, password)
212
            identify_with_chanserv(bot, server, channel)
213
            return True
214
215
    res = re.match("Channel \\x02(\S*)\\x02 registered under your nickname: (\S*)", msg)
216
    if res is not None:
217
        channel = res.group(1)
218
        user = res.group(2)
219
        if user == bot.settings.bot_nick_name:
220
            print " + Channel '%s' is registered." % channel
221
        else:
222
            print " + Channel '%s' is registered for '%s'." % (channel, user)
223
        bot.tell_users(server, "Registered channel '%s'." % channel)
224
        waiting_for_pass_for = channel
225
        return True
226
227
    res = re.match("Channel \\x02(\S*)\\x02 is already registerd!", msg)
228
    if res is not None:
229
        channel = res.group(1)
230
        print " - %s is already registered." % channel
231
        return True
232
233
    res = re.match('(\S*) access list is empty.', msg)
234
    if res is not None:
235
        channel = res.group(1)
236
        print " + Got empty access list from '%s'." % channel
237
        getting_alist_from = None
238
        return True
239
    
240
    res = re.match('Access list for (\S*):', msg)
241
    if res is not None:
242
        channel = res.group(1)
243
        if channel in owned_channels:
244
            getting_alist_from = channel
245
        else:
246
            print " - Got accesslist from unknown channel '%s'." % channel
247
        return True
248
249
    res = re.match('End of list; ([0-9]+)/([0-9]+) matches shown.', msg)
250
    if res is not None:
251
        print " + Got complete access list from '%s'." % str(getting_alist_from)
252
        getting_alist_from = None
253
        return True
254
255
    return False