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