4 # Joel Rosdahl <joel@rosdahl.net>
5 # Andrew Walkingshaw <andrew@lexical.org.uk>
6 # Peter Corbett <ptc24@cam.ac.uk>
7 # Matthew Vernon <matthew@debian.org>
9 # This file is part of Acrobat.
11 # Acrobat is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published
13 # by the Free Software Foundation; either version 2 of the License,
14 # or (at your option) any later version.
16 # Acrobat is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with Acrobat; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 disconnect -- Disconnect the bot. The bot will try to reconnect
30 die -- Let the bot cease to exist.
32 google -- search, 'I'm Feeling Lucky', and notice the user who searches
36 import string, urllib, sys, cPickle, os, random, re, time
37 from ircbot import SingleServerIRCBot
38 from irclib import nm_to_n, irc_lower
46 class Acrobat(SingleServerIRCBot):
47 def __init__(self, channel, nickname, server, owner, port=6667):
48 SingleServerIRCBot.__init__(self,
49 [(server, port)], nickname, nickname)
50 self.channel = channel
52 self.quotatime = time.time()
53 #Configurable stuff - how often do we add how many fish?
55 self.max_fish=5 #Maximum of 5 fish
56 self.fish_time_inc=60 #Add fish with 20s granularity
57 self.fish_inc=2 #Rate of increase is 2 fish per 60s
58 self.DoS=0 #Have we been told to shut up?
59 self.Boring_Git='Nobody' #Who told us to shut up?
62 f = open("karmadump", "r")
63 self.karma = cPickle.load(f)
68 f = open("trouts", "r")
69 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
72 self.trouts = [ "hits %s with a wet trout.", "thwaps %s.", "questions %s's parentage.", "pokes its tounge out at %s.", "bites its thumb at %s."]
76 def on_welcome(self, conn, evt):
77 conn.join(self.channel)
79 def on_privmsg(self, conn, evt):
80 self.do_command(nm_to_n(evt.source()), evt.arguments()[0])
82 def on_pubmsg(self, conn, evt):
83 payload = evt.arguments()[0]
84 a = string.split(evt.arguments()[0], " ", 1)
86 and (irc_lower(a[0]) == irc_lower(self.connection.get_nickname())
87 or irc_lower(a[0])[:-1] == irc_lower(self.connection.get_nickname())):
89 self.do_command(nm_to_n(evt.source()), string.strip(a[1]), public = 1)
90 if a[0].endswith("++"):
92 if a[0].endswith("--"):
94 if payload[0] == "!" and len(payload)>1:
95 self.do_command(nm_to_n(evt.source()), string.strip(payload[1:]), public=1)
96 if payload[0] == "~" and len(payload)>1:
97 self.do_command(nm_to_n(evt.source()), string.strip(payload[1:]), public=1)
99 # And now bot commands;
102 def karmaup(self, cmd):
103 if self.karma.dict.has_key(cmd.split()[0][:-2]):
104 self.karma.dict[cmd.split()[0][:-2]] += 1
106 self.karma.dict[cmd.split()[0][:-2]] = 1
109 def karmadown(self, cmd):
110 if self.karma.dict.has_key(cmd.split()[0][:-2]):
111 self.karma.dict[cmd.split()[0][:-2]] -= 1
113 self.karma.dict[cmd.split()[0][:-2]] = -1
116 def karmaq(self, cmd, conn, nick, public):
120 if self.karma.dict.has_key(cmd.split()[1]):
121 conn.privmsg(self.channel, "%s has karma %s."
123 self.karma.dict[cmd.split()[1]]))
125 conn.privmsg(self.channel, "%s has no karma set." %
128 conn.privmsg(self.channel, "I have karma on %s items." %
129 len(self.karma.dict.keys()))
133 if self.karma.dict.has_key(cmd.split()[1]):
134 conn.notice(nick, "%s has karma %s." %
136 self.karma.dict[cmd.split()[1]]))
138 conn.notice(nick, "I have karma on %s items." %
139 len(self.karma.dict.keys()))
141 conn.notice(nick, "I have karma on %s items." %
142 len(self.karma.dict.keys()))
144 def infoq(self, cmd, nick, conn, public):
145 # version control magic
146 acrorevision="$Revision$"
147 acrorev1=re.sub(r'\$Revision: (.*)',r'\1',acrorevision)
148 acroversion=re.sub(r'(.*) \$',r'\1',acrorev1)
150 conn.privmsg(self.channel,
151 "I am Acrobat %s, on %s, as nick %s." %
152 (acroversion, self.channel, self.connection.get_nickname()))
153 conn.privmsg(self.channel,
154 "My owner is %s; I have karma on %s items." %
155 (self.owner, len(self.karma.dict.keys())))
157 conn.notice(nick, "I am Acrobat %s, on %s, as nick %s." %
158 (acroversion, self.channel, self.connection.get_nickname()))
159 conn.notice(nick, "My owner is %s; I have karma on %s items." %
160 (self.owner, len(self.karma.dict.keys())))
163 def troutq(self, cmd, nick, conn, public):
166 conn.notice(nick, "Sorry, but %s is being a spoilsport." %
169 if self.cur_fish == 0:
170 conn.notice(nick, "Fish stocks exhausted.")
174 target = string.join(cmd.split()[1:])
175 me = self.connection.get_nickname()
176 trout_msg = random.choice(self.trouts)
177 # # The bot is loyal(ish)...
178 # if target.lower() == self.owner.lower():
181 if me.lower() == target.lower():
183 conn.action(self.channel, trout_msg % target)
185 if random.random() <= 0.1:
186 conn.action(self.channel, "notes %s is conducting a whispering campaign" % nick)
188 conn.notice(nick, "Who do you wish me to trout?")
191 def reloadq(self, cmd, nick, conn, public):
192 if irc_lower(nick) == irc_lower(self.owner):
195 f = open("trouts", "r")
196 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
198 conn.notice(nick, "I am re-armed!")
200 conn.notice(nick, "Trout re-arming failed!")
203 conn.notice(nick, "This command can only be invoked by my owner.")
204 # Shut up trouting for a minute
205 def nofish(self, cmd, nick, conn, public):
209 self.quotatime=time.time()
210 self.quotatime+=60 #60 seconds of no fishing
211 conn.notice(nick, "Fish stocks depleted, as you wish.")
212 # Check on fish stocks
213 def fish_quota(self):
215 if time.time()>=self.quotatime:
220 if (time.time()-self.quotatime)>self.fish_time_inc:
221 self.cur_fish+=(((time.time()-self.quotatime)/self.fish_time_inc)*self.fish_inc)
222 if self.cur_fish>self.max_fish:
223 self.cur_fish=self.max_fish
224 self.quotatime=time.time()
227 def quit(self, cmd, nick, conn, public):
228 if irc_lower(nick) == irc_lower(self.owner):
229 f = open("karmadump", "w")
230 cPickle.dump(self.karma, f)
232 self.die(msg="I have been chosen!")
234 conn.privmsg(nick, "Such aggression in public!")
236 conn.notice(nick, "You're not my owner.")
238 # google for something
239 def googleq(self, cmd, nick, conn, public):
240 cmdrest = string.join(cmd.split()[1:])
241 # "I'm Feeling Lucky" rather than try and parse the html
242 targ = ("http://www.google.com/search?q=%s&btnI=I'm+Feeling+Lucky"
243 % urllib.quote_plus(cmdrest))
245 # get redirected and grab the resulting url for returning
246 gsearch = urllib.urlopen(targ).geturl()
247 if gsearch != targ: # we've found something
249 conn.notice(nick, str(gsearch))
250 else: # we haven't found anything.
251 conn.privmsg(nick, str(gsearch))
254 conn.notice(nick, "No pages found.")
256 conn.privmsg(nick, "No pages found.")
257 except IOError: # if the connection times out. This blocks. :(
259 conn,notice(nick, "The web's broken. Waah!")
261 conn.privmsg(nick, "The web's broken. Waah!")
263 # General query handler
264 def do_command(self, nick, cmd, public=0):
265 conn = self.connection
267 if cmd.split()[0].endswith("++"):
271 if cmd.split()[0].endswith("--"):
275 if cmd.split()[0] == "karma" or cmd.split()[0] == "Karma":
276 self.karmaq(cmd, conn, nick, public)
278 # bot's vital statistics
280 self.infoq(cmd, nick, conn, public)
283 if cmd.split()[0] == "trout":
284 self.troutq(cmd, nick, conn, public)
286 self.reloadq(cmd, nick, conn, public)
288 #Disconnect disabled 'cos people hated it
290 # if cmd == "disconnect": # hop off for 60s
291 # self.disconnect(msg="Be right back.")
294 if cmd.split()[0] == "quiet":
295 self.nofish(cmd,nick,conn, public)
298 elif cmd.split()[0] == "say" \
299 and irc_lower(nick) == irc_lower(self.owner):
300 conn.privmsg(self.channel, string.join(cmd.split()[1:]))
302 # action to msg/channel
303 elif cmd.split()[0] == "do" \
304 and irc_lower(nick) == irc_lower(self.owner):
305 conn.action(self.channel, string.join(cmd.split()[1:]))
309 self.quit(cmd, nick, conn, public)
312 elif (cmd.split()[0] == "google" or cmd.split()[0] == "Google"):
313 self.googleq(cmd, nick, conn, public)
316 if len(sys.argv) != 5: # insufficient arguments
317 print "Usage: acrobat <server[:port]> <channel> <nickname> owner"
319 sv_port = string.split(sys.argv[1], ":", 1) # tuple; (server, port)
321 if len(sv_port) == 2:
323 port = int(sv_port[1])
325 print "Error: Erroneous port."
328 port = 6667 # default irc port
329 channel = sys.argv[2]
330 nickname = sys.argv[3]
333 bot = Acrobat(channel, nickname, server, owner, port)
334 sys.stderr.write("Trying to connect...\n")
338 if __name__ == "__main__":