b3d57e04034a6c747bc6295118c878a3f6945c28
[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         cmd = []
33         for line in open(config.RCFILE):
34             response = do_command(Who(config.NICK, True), config.NICK, config.NICK, line.split())
35             if isinstance(response, list):
36                 cmd += response
37             elif response:
38                 cmd.append(response)
39         return cmd
40     except IOError:
41         return 'PRIVMSG', (replyto, 'unable to open file')
42 op_commands['readrc'] = readrc_command
43
44 def alias_command(who, _, replyto, words):
45     '''Add or list email aliases (alias stefanha stefanha@gmail.com)'''
46     if len(words) == 2:
47         if words[1].lower() == 'list':
48             return [('PRIVMSG', (replyto, '%s    %s' % (user, email))) for (user, email) in aliases.items()]
49     if len(words) < 3:
50         return
51     user = words[1]
52     email = words[2]
53     aliases[user] = email
54 op_commands['alias'] = alias_command
55
56 def op_command(who, target, replyto, words):
57     '''Add or list bot operators (op stefanha, op list)'''
58     if len(words) == 2:
59         if words[1].lower() == 'list':
60             return [('PRIVMSG', (replyto, '%s' % nick)) for nick in op_members]
61     if len(words) < 2:
62         return
63     nick = words[1]
64     if not nick in op_members:
65         op_members.append(nick)
66 op_commands['op'] = op_command
67
68 def deop_command(who, target, replyto, words):
69     '''Remove bot operators (deop stefanha)'''
70     if len(words) < 2:
71         return
72     nick = words[1]
73     if nick in op_members:
74         op_members.remove(nick)
75 op_commands['deop'] = deop_command
76
77 ERRCODE_RE = re.compile(r'((?:0x)?[0-9a-fA-F]{8})')
78
79 def errcode_command(who, _, replyto, words):
80     '''Look up gPXE error code (errcode 0x12345678)'''
81     msg = ' '.join(words)
82     m = ERRCODE_RE.search(msg)
83     if m:
84         try:
85             return 'PRIVMSG', (replyto, str(errcode.Errcode(int(m.group(1), 16))))
86         except ValueError:
87             pass
88 commands['errcode'] = errcode_command
89 commands['error'] = errcode_command
90
91 PCI_ID_RE = re.compile(r'([0-9a-fA-F]{4}):([0-9a-fA-F]{4})')
92
93 def lspci_command(who, _, replyto, words):
94     '''Look up driver for PCI ID (lspci 10ec:8139)'''
95     if len(words) != 2:
96         return 'PRIVMSG', (replyto, 'Invalid arguments')
97
98     m = PCI_ID_RE.match(words[1])
99     if not m:
100         return 'PRIVMSG', (replyto, 'Invalid arguments')
101     pci_id = '%s,%s' % tuple(s.lower() for s in m.groups())
102
103     try:
104         for line in open(config.NICFILE):
105             line = line.strip()
106             if not line or line.startswith('#'):
107                 continue
108             fields = line.split()
109
110             if fields[0] == 'family':
111                 driver = fields[1].split('/')[-1]
112                 continue
113
114             if fields[1] == pci_id:
115                 return 'PRIVMSG', (replyto, driver)
116     except IndexError, e:
117         utils.dbg(str(e))
118     except IOError, e:
119         utils.dbg(str(e))
120     return 'PRIVMSG', (replyto, 'No driver found')
121 if config.NICFILE:
122     commands['lspci'] = lspci_command
123 else:
124     sys.stderr.write('running without lspci support (NICFILE not given)\n')
125
126 def help_command(who, _, replyto, msg):
127     '''List commands (help)'''
128     def do_help(vocabulary):
129         output = []
130         names = sorted(vocabulary.keys())
131         max_width = max(len(name) for name in names)
132         for name in names:
133             cmd = vocabulary[name]
134             if hasattr(cmd, '__doc__'):
135                 output.append(('PRIVMSG', (replyto, '%s    %s' % (name.ljust(max_width), cmd.__doc__))))
136         return output
137     output = do_help(commands)
138     if who.is_op():
139         output.extend(do_help(op_commands))
140     return output
141 commands['help'] = help_command
142
143 def log_command(who, target, replyto, words):
144     '''Control channel logging (log start, log stop, log list)'''
145     if len(words) < 2:
146         return
147
148     # Parse arguments
149     words.pop(0)
150     cmd     = words.pop(0)
151     channel = words and words[0].startswith('#') and words.pop(0) or target
152     emails  = words and words.pop(0) or ''
153     subject = ' '.join(words)
154
155     if cmd == 'start':
156         if channel in logs:
157             return 'PRIVMSG', (replyto, 'Logging %s already started' % channel)
158         output = []
159         if channel.startswith('#') and channel != target:
160             output.append(('CMD', ('JOIN %s' % channel,)))
161         filename = utils.make_log(channel)
162         logs[channel] = {
163             'filename': filename,
164             'fileobj': open(filename, 'w'),
165             'emails': emails,
166             'subject': subject,
167         }
168         output.append(('PRIVMSG', (replyto, 'Start logging %s...' % channel)))
169         return output
170
171     elif cmd == 'stop':
172         if not channel in logs:
173             return 'PRIVMSG', (replyto, 'Logging %s not started yet' % channel)
174         log = logs.pop(channel)
175         log['fileobj'].close()
176
177         if not emails:
178             emails = log['emails']
179         if not subject:
180             subject = log['subject']
181         if not subject:
182             subject = 'IRC logs for ' + channel
183
184         unrecognized = utils.email_log(emails, subject, log['filename'], aliases)
185         output = [('PRIVMSG', (replyto, 'Unrecognized alias %s' % user)) for user in unrecognized]
186         output.append(('PRIVMSG', (replyto, 'Stop logging %s.  Saved log file (%s)' % (channel, log['filename']))))
187         return output
188
189     elif cmd == 'list':
190         return [('PRIVMSG', (replyto, channel)) for channel in logs.keys()]
191 op_commands['log'] = log_command
192
193 def join_command(who, target, replyto, words):
194     '''Join a channel (join #etherboot)'''
195     if len(words) != 2:
196         return
197     channel = words[1]
198     return 'CMD', ('JOIN %s' % (channel.startswith('#') and channel or '#' + channel),)
199 op_commands['join'] = join_command
200
201 def part_command(who, target, replyto, words):
202     '''Leave a channel (part #etherboot)'''
203     if len(words) != 2:
204         return
205     channel = words[1]
206     return 'CMD', ('PART %s' % (channel.startswith('#') and channel or '#' + channel),)
207 op_commands['part'] = part_command
208
209 def privmsg_command(who, target, replyto, words):
210     '''Send a chat message (privmsg #etherboot Hello all!)'''
211     if len(words) < 3:
212         return
213     return 'PRIVMSG', (words[1], ' '.join(words[2:]))
214 op_commands['privmsg'] = privmsg_command
215
216 def restart_command(who, target, replyto, words):
217     '''Restart bot (restart)'''
218     return ('RESTART', ())
219 op_commands['restart'] = restart_command
220
221 def do_command(who, target, replyto, words):
222     if not words:
223         return
224     command = words[0].lower()
225
226     if who.is_op() and command in op_commands:
227         return op_commands[command](who, target, replyto, words)
228     if command in commands:
229         return commands[command](who, target, replyto, words)