/[shmookey]/killbot/killbot.py


UCC Code Repository

Contents of /killbot/killbot.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 123 - (show annotations) (download) (as text)
Wed Nov 11 02:41:36 2009 UTC (12 years ago) by shmookey
File MIME type: text/x-python
File size: 5460 byte(s)
Unicode support from [DJA]

1 #!/usr/bin/python
2 ''' killbot.py
3 Kicks users from an IRC channel when they send banned expressions.
4
5 Written by Luke Williams <[email protected]>
6 Distributed under an MIT-style license - see the LICENSE file for details.
7 '''
8
9 import re, os, sys
10 import conf, easybot
11 import unicodedata
12
13 BOT_NAME = conf.username
14
15 patterns = {}
16
17 def load_channel (channel):
18 patterns[channel] = []
19 if os.access ("channels/" + channel, os.F_OK | os.R_OK):
20 pattern_file = open ("channels/" + channel, "r")
21 for line in pattern_file.read().split("\n"):
22 if (not line == "") and (not line[0] == ";"):
23 patterns[channel].append( (line,re.compile(line, re.IGNORECASE | re.UNICODE)) )
24 pattern_file.close ()
25 else:
26 os.mknod ("channels/" + channel)
27 pattern_file = open ("channels/" + channel, "w+")
28 pattern_file.write ("; Autogenerated by killbot")
29 pattern_file.close ()
30
31 def msg (chobj, channel, msg_from, msg, is_oper):
32 words = msg.split (":")
33 if len (words) > 1:
34 if words [0] == BOT_NAME:
35 responses = process_command (words[1], channel, msg_from, is_oper, chobj)
36 for response in responses:
37 chobj.privmsg (channel, response)
38 return
39
40 # strip out all but alphanumeric (Ll, Lu, Nd) and spacing (Zs) characters.
41 # punctuation is deliberately stripped out so as people cannot evade kicks by padding their message
42 safe_chars = [ 'Lu', 'Ll', 'Zs', 'Nd' ]
43 clean_msg = ''.join(c for c in msg.decode('utf-8') if unicodedata.category(c) in safe_chars)
44
45 for pattern in patterns[channel]:
46 if re.search (pattern[1], clean_msg):
47 print "Kicking " + msg_from + " for violating kickable expression (\"" + pattern[0] + "\")"
48 chobj.kick (channel, msg_from, "Violating kickable expression (\"" + pattern[0] + "\")")
49
50 def process_command (cmd, channel, msg_from, is_oper, connection):
51 words = cmd [1:].split (" ", 1)
52 try:
53 if words[0] == "help":
54 responses = ["I kick people when they act like idiots! Channel operators can define what idiotic behaviour looks like with Python-style regex by telling me:"]
55 responses.append (BOT_NAME + ": add kickable_pattern_here")
56 responses.append ("I ignore everything except letters, numbers and whitespace when processing the regex.")
57 responses.append ("Anyone can get the list of kickable expressions by asking me with \"" + BOT_NAME + ": show\"")
58 responses.append ("Channel operators can remove expressions from the kicklist with \"" + BOT_NAME + ": remove list_index\"")
59 responses.append ("I was originally written in 2009 by shmookey for #ucc on irc.ucc.asn.au")
60 return responses
61 if words[0] == "show":
62 responses = ["Kickable Expressions:"]
63 for i, pattern in enumerate (patterns[channel]):
64 responses.append (str(i) + ". " + pattern[0])
65 return responses
66 elif words[0] == "add" and len (words) > 1:
67 if not is_oper:
68 responses = [msg_from + ": I only obey channel operators."]
69 return responses
70 pattern = words[1]
71 try:
72 patterns[channel].append ( (pattern,re.compile (pattern, re.IGNORECASE)) )
73 except:
74 responses = ["There was a problem compiling your expression."]
75 return responses
76 pattern_file = open ("channels/" + channel, "a+")
77 pattern_file.write ("\n" + pattern)
78 pattern_file.close ()
79 responses = ["Added \"" + pattern + "\" to the list of kickable expressions."]
80 responses.append ("Use \"" + BOT_NAME + ": remove " + str (len (patterns[channel])-1) + "\" to remove.")
81 return responses
82 elif words[0] == "remove" and len(words) > 1:
83 if not is_oper:
84 responses = [msg_from + ": I only obey channel operators."]
85 return responses
86 pnum = int (words[1])
87 pattern = patterns[channel] [pnum]
88 patterns[channel].pop (pnum)
89 pattern_file = open ("channels/" + channel, "r")
90 new_content = "\n".join ([line for line in pattern_file.read().split("\n") if not line == pattern[0]])
91 pattern_file = open ("channels/" + channel, "w")
92 pattern_file.write (new_content)
93 pattern_file.close ()
94 responses = ["Removed pattern " + str (pnum) + " (\"" + pattern[0] + "\") from kicklist."]
95 return responses
96 elif words[0] == "join" and len(words) > 1:
97 if not is_oper:
98 responses = [msg_from + ": I only obey channel operators."]
99 return responses
100 new_channel = words[1]
101 load_channel (new_channel)
102 channel_file = open ("channels.conf", "a+")
103 channel_file.write ("\n" + new_channel)
104 channel_file.close ()
105 connection.join (new_channel)
106 responses = ["OK!"]
107 return responses
108 elif words[0] == "leave":
109 if not is_oper:
110 responses = [msg_from + ": I only obey channel operators."]
111 return responses
112 channel_file = open ("channels.conf", "r")
113 new_content = "\n".join([line for line in channel_file.read().split("\n") if not line == channel])
114 print new_content
115 channel_file.close ()
116 channel_file = open ("channels.conf", "w")
117 channel_file.write (new_content)
118 channel_file.close ()
119 connection.part (channel)
120 return []
121 else:
122 responses = [msg_from + ": I don't understand."]
123 return responses
124 except:
125 responses = [msg_from + ": What?"]
126 return responses
127
128 def on_welcome (c):
129 for channel in patterns:
130 c.join (channel)
131
132 channel_file = open ("channels.conf", "r")
133 for line in channel_file.read().split("\n"):
134 if (not line == "") and (not line[0] == ";"):
135 load_channel (line)
136 channel_file.close ()
137 bot = easybot.EasyBot (BOT_NAME, conf.server, port=conf.port)
138 bot.message_hook = msg
139 bot.welcome_hook = on_welcome
140 bot.start ()

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26