Do not warn about empty email address
[people/stefanha/gpxebot.git] / cmds.py
1 import re
2 import sys
3 import time
4
5 import config
6 import utils
7
8 # For development an errcode database may not be available
9 try:
10     import errcode
11 except ImportError:
12     sys.stderr.write('running without errcode support\n')
13
14 logs = {}
15 commands = {}
16 op_commands = {}
17 op_members = [config.NICK]
18 aliases = {}
19
20 class Who(object):
21     def __init__(self, mask, is_identified=False):
22         self.mask = mask
23         self.nick = mask.split('!', 1)[0]
24         self.is_identified = is_identified
25
26     def is_op(self):
27         return self.is_identified and self.nick in op_members
28
29 def readrc_command(who=None, target=None, replyto=None, words=None):
30     '''Source bot command file (readrc)'''
31     try:
32         f = open(config.RCFILE).readlines()
33     except IOError:
34         return 'PRIVMSG', (replyto, 'unable to open file')
35     cmd = []
36     for line in f:
37         response = do_command(Who(config.NICK, True), config.NICK, config.NICK, line.split())
38         if isinstance(response, list):
39             cmd += response
40         elif response:
41             cmd.append(response)
42     return cmd
43 op_commands['readrc'] = readrc_command
44
45 def alias_command(who, _, replyto, words):
46     '''Add or list email aliases (alias stefanha stefanha@gmail.com)'''
47     if len(words) == 2:
48         if words[1].lower() == 'list':
49             return [('PRIVMSG', (replyto, '%s    %s' % (user, email))) for (user, email) in aliases.items()]
50     if len(words) < 3:
51         return
52     user = words[1]
53     email = words[2]
54     aliases[user] = email
55 op_commands['alias'] = alias_command
56
57 def op_command(who, target, replyto, words):
58     '''Add or list bot operators (op stefanha, op list)'''
59     if len(words) == 2:
60         if words[1].lower() == 'list':
61             return [('PRIVMSG', (replyto, '%s' % nick)) for nick in op_members]
62     if len(words) < 2:
63         return
64     nick = words[1]
65     if not nick in op_members:
66         op_members.append(nick)
67 op_commands['op'] = op_command
68
69 def deop_command(who, target, replyto, words):
70     '''Remove bot operators (deop stefanha)'''
71     if len(words) < 2:
72         return
73     nick = words[1]
74     if nick in op_members:
75         op_members.remove(nick)
76 op_commands['deop'] = deop_command
77
78 ERRCODE_RE = re.compile(r'((?:0x)?[0-9a-fA-F]{8})')
79
80 def errcode_command(who, _, replyto, words):
81     '''Look up gPXE error code (errcode 0x12345678)'''
82     # errcode 0x12345678
83     msg = ' '.join(words)
84     m = ERRCODE_RE.search(msg)
85     if m:
86         try:
87             return 'PRIVMSG', (replyto, str(errcode.Errcode(int(m.group(1), 16))))
88         except ValueError:
89             pass
90 commands['errcode'] = errcode_command
91 commands['error'] = errcode_command
92
93 def help_command(who, _, replyto, msg):
94     '''List commands (help)'''
95     def do_help(vocabulary):
96         output = []
97         names = sorted(vocabulary.keys())
98         max_width = max(len(name) for name in names)
99         for name in names:
100             cmd = vocabulary[name]
101             if hasattr(cmd, '__doc__'):
102                 output.append(('PRIVMSG', (replyto, '%s    %s' % (name.ljust(max_width), cmd.__doc__))))
103         return output
104     output = do_help(commands)
105     if who.is_op():
106         output.extend(do_help(op_commands))
107     return output
108 commands['help'] = help_command
109
110 def log_command(who, target, replyto, words):
111     '''Control channel logging (log start, log stop, log list)'''
112     if len(words) < 2:
113         return
114
115     # Parse arguments
116     words.pop(0)
117     cmd     = words.pop(0)
118     channel = words and words[0].startswith('#') and words.pop(0) or target
119     emails  = words and words.pop(0) or ''
120     subject = ' '.join(words)
121
122     if cmd == 'start':
123         if channel in logs:
124             return 'PRIVMSG', (replyto, 'Logging %s already started' % channel)
125         output = []
126         if channel.startswith('#') and channel != target:
127             output.append(('CMD', ('JOIN %s' % channel,)))
128         filename = utils.make_log(channel)
129         logs[channel] = {
130             'filename': filename,
131             'fileobj': open(filename, 'w'),
132             'emails': emails,
133             'subject': subject,
134         }
135         output.append(('PRIVMSG', (replyto, 'Start logging %s...' % channel)))
136         return output
137
138     elif cmd == 'stop':
139         if not channel in logs:
140             return 'PRIVMSG', (replyto, 'Logging %s not started yet' % channel)
141         log = logs.pop(channel)
142         log['fileobj'].close()
143
144         if not emails:
145             emails = log['emails']
146         if not subject:
147             subject = log['subject']
148         if not subject:
149             subject = 'IRC logs for ' + channel
150
151         unrecognized = utils.email_log(emails, subject, log['filename'], aliases)
152         output = [('PRIVMSG', (replyto, 'Unrecognized alias %s' % user)) for user in unrecognized]
153         output.append(('PRIVMSG', (replyto, 'Stop logging %s.  Saved log file (%s)' % (channel, log['filename']))))
154         return output
155
156     elif cmd == 'list':
157         return [('PRIVMSG', (replyto, channel)) for channel in logs.keys()]
158 op_commands['log'] = log_command
159
160 def join_command(who, target, replyto, words):
161     '''Join a channel (join #etherboot)'''
162     if len(words) != 2:
163         return
164     channel = words[1]
165     return 'CMD', ('JOIN %s' % (channel.startswith('#') and channel or '#' + channel),)
166 op_commands['join'] = join_command
167
168 def part_command(who, target, replyto, words):
169     '''Leave a channel (part #etherboot)'''
170     if len(words) != 2:
171         return
172     channel = words[1]
173     return 'CMD', ('PART %s' % (channel.startswith('#') and channel or '#' + channel),)
174 op_commands['part'] = part_command
175
176 def privmsg_command(who, target, replyto, words):
177     '''Send a chat message (privmsg #etherboot Hello all!)'''
178     if len(words) < 3:
179         return
180     return 'PRIVMSG', (words[1], ' '.join(words[2:]))
181 op_commands['privmsg'] = privmsg_command
182
183 def restart_command(who, target, replyto, words):
184     '''Restart bot (restart)'''
185     return ('RESTART', ())
186 op_commands['restart'] = restart_command
187
188 def do_command(who, target, replyto, words):
189     if not words:
190         return
191     command = words[0].lower()
192
193     if who.is_op() and command in op_commands:
194         return op_commands[command](who, target, replyto, words)
195     if command in commands:
196         return commands[command](who, target, replyto, words)