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 #List of known !commands we respond to
54 self.known =['karma','trout','info','die','quiet','list','google','say','do','reload','flirt']
55 #Configurable stuff - how often do we add how many fish?
57 self.max_fish=5 #Maximum of 5 fish
58 self.fish_time_inc=60 #Add fish with 20s granularity
59 self.fish_inc=2 #Rate of increase is 2 fish per 60s
60 self.DoS=0 #Have we been told to shut up?
61 self.Boring_Git='Nobody' #Who told us to shut up?
64 f = open("karmadump", "r")
65 self.karma = cPickle.load(f)
70 f = open("trouts", "r")
71 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
74 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 f = open("flirts", "r")
77 self.flirts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
80 self.flirts = [ "falls madly in love with %s", "blows kisses at %s"]
84 def on_welcome(self, conn, evt):
85 conn.join(self.channel)
87 def on_privmsg(self, conn, evt):
88 self.do_command(nm_to_n(evt.source()), evt.arguments()[0])
90 def on_pubmsg(self, conn, evt):
91 payload = evt.arguments()[0]
92 a = string.split(evt.arguments()[0], " ", 1)
94 and (irc_lower(a[0]) == irc_lower(self.connection.get_nickname())
95 or irc_lower(a[0])[:-1] == irc_lower(self.connection.get_nickname())):
97 self.do_command(nm_to_n(evt.source()), string.strip(a[1]), public = 1)
98 if a[0].endswith("++"):
100 if a[0].endswith("--"):
102 if payload[0] == "!" and len(payload)>1:
103 self.do_command(nm_to_n(evt.source()), string.strip(payload[1:]), public=1)
104 if payload[0] == "~" and len(payload)>1:
105 self.do_command(nm_to_n(evt.source()), string.strip(payload[1:]), public=1)
107 # And now bot commands;
110 def karmaup(self, cmd):
111 if self.karma.dict.has_key(cmd.split()[0][:-2]):
112 self.karma.dict[cmd.split()[0][:-2]] += 1
114 self.karma.dict[cmd.split()[0][:-2]] = 1
117 def karmadown(self, cmd):
118 if self.karma.dict.has_key(cmd.split()[0][:-2]):
119 self.karma.dict[cmd.split()[0][:-2]] -= 1
121 self.karma.dict[cmd.split()[0][:-2]] = -1
124 def karmaq(self, cmd, conn, nick, public):
128 if self.karma.dict.has_key(cmd.split()[1]):
129 conn.privmsg(self.channel, "%s has karma %s."
131 self.karma.dict[cmd.split()[1]]))
133 conn.privmsg(self.channel, "%s has no karma set." %
136 conn.privmsg(self.channel, "I have karma on %s items." %
137 len(self.karma.dict.keys()))
141 if self.karma.dict.has_key(cmd.split()[1]):
142 conn.notice(nick, "%s has karma %s." %
144 self.karma.dict[cmd.split()[1]]))
146 conn.notice(nick, "I have karma on %s items." %
147 len(self.karma.dict.keys()))
149 conn.notice(nick, "I have karma on %s items." %
150 len(self.karma.dict.keys()))
152 def infoq(self, cmd, nick, conn, public):
153 # version control magic
154 acrorevision="$Revision$"
155 acrorev1=re.sub(r'\$Revision: (.*)',r'\1',acrorevision)
156 acroversion=re.sub(r'(.*) \$',r'\1',acrorev1)
158 conn.privmsg(self.channel,
159 "I am Acrobat %s, on %s, as nick %s." %
160 (acroversion, self.channel, self.connection.get_nickname()))
161 conn.privmsg(self.channel,
162 "My owner is %s; I have karma on %s items." %
163 (self.owner, len(self.karma.dict.keys())))
165 conn.notice(nick, "I am Acrobat %s, on %s, as nick %s." %
166 (acroversion, self.channel, self.connection.get_nickname()))
167 conn.notice(nick, "My owner is %s; I have karma on %s items." %
168 (self.owner, len(self.karma.dict.keys())))
171 def listq(self, cmd, nick, conn, public):
172 conn.notice(nick, "%s" % string.join(self.known))
174 def flirtq(self, cmd, nick, conn, public):
177 conn.notice(nick, "Sorry, but %s made me take Holy Orders." %
180 if self.cur_fish <= 0:
181 conn.notice(nick, "My libido is over-used!")
185 target = string.join(cmd.split()[1:])
186 me = self.connection.get_nickname()
187 trout_msg = random.choice(self.flirts)
189 if me.lower() == target.lower():
192 if random.random() <= 0.1:
193 trout_msg+= ' (but %s is their secret admirer)' % nick
194 conn.action(self.channel, trout_msg % target)
196 conn.notice(nick, "Who do you wish me to flirt with?")
199 def troutq(self, cmd, nick, conn, public):
202 conn.notice(nick, "Sorry, but %s is being a spoilsport." %
205 if self.cur_fish <= 0:
206 conn.notice(nick, "Fish stocks exhausted.")
210 target = string.join(cmd.split()[1:])
211 me = self.connection.get_nickname()
212 trout_msg = random.choice(self.trouts)
213 # # The bot is loyal(ish)...
214 # if target.lower() == self.owner.lower():
217 if me.lower() == target.lower():
220 if random.random() <= 0.1:
221 trout_msg+= ' (at the instigation of %s)' % nick
222 conn.action(self.channel, trout_msg % target)
224 conn.notice(nick, "Who do you wish me to trout?")
227 def reloadq(self, cmd, nick, conn, public):
228 if irc_lower(nick) == irc_lower(self.owner):
232 f = open("trouts", "r")
233 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
235 f = open("flirts", "r")
236 self.flirts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
237 conn.notice(nick, "I am re-armed!")
239 conn.notice(nick, "Trout re-arming failed!")
243 conn.notice(nick, "This command can only be invoked by my owner.")
244 # Shut up trouting for a minute
245 def nofish(self, cmd, nick, conn, public):
249 self.quotatime=time.time()
250 self.quotatime+=60 #60 seconds of no fishing
251 conn.notice(nick, "Fish stocks depleted, as you wish.")
252 # Check on fish stocks
253 def fish_quota(self):
255 if time.time()>=self.quotatime:
260 if (time.time()-self.quotatime)>self.fish_time_inc:
261 self.cur_fish+=(((time.time()-self.quotatime)/self.fish_time_inc)*self.fish_inc)
262 if self.cur_fish>self.max_fish:
263 self.cur_fish=self.max_fish
264 self.quotatime=time.time()
267 def quit(self, cmd, nick, conn, public):
268 if irc_lower(nick) == irc_lower(self.owner):
269 f = open("karmadump", "w")
270 cPickle.dump(self.karma, f)
272 self.die(msg="I have been chosen!")
274 conn.privmsg(nick, "Such aggression in public!")
276 conn.notice(nick, "You're not my owner.")
278 # google for something
279 def googleq(self, cmd, nick, conn, public):
280 cmdrest = string.join(cmd.split()[1:])
281 # "I'm Feeling Lucky" rather than try and parse the html
282 targ = ("http://www.google.com/search?q=%s&btnI=I'm+Feeling+Lucky"
283 % urllib.quote_plus(cmdrest))
285 # get redirected and grab the resulting url for returning
286 gsearch = urllib.urlopen(targ).geturl()
287 if gsearch != targ: # we've found something
289 conn.notice(nick, str(gsearch))
290 else: # we haven't found anything.
291 conn.privmsg(self.channel, str(gsearch))
294 conn.notice(nick, "No pages found.")
296 conn.privmsg(self.channel, "No pages found.")
297 except IOError: # if the connection times out. This blocks. :(
299 conn,notice(nick, "The web's broken. Waah!")
301 conn.privmsg(self.channel, "The web's broken. Waah!")
303 # General query handler
304 def do_command(self, nick, cmd, public=0):
305 conn = self.connection
307 if cmd.split()[0].endswith("++"):
311 if cmd.split()[0].endswith("--"):
315 if cmd.split()[0] == "karma" or cmd.split()[0] == "Karma":
316 self.karmaq(cmd, conn, nick, public)
318 # bot's vital statistics
320 self.infoq(cmd, nick, conn, public)
324 self.listq(cmd, nick, conn, public)
326 if cmd.split()[0] == "trout":
327 self.troutq(cmd, nick, conn, public)
328 if cmd.split()[0] == "flirt":
329 self.flirtq(cmd, nick, conn, public)
331 self.reloadq(cmd, nick, conn, public)
333 #Disconnect disabled 'cos people hated it
335 # if cmd == "disconnect": # hop off for 60s
336 # self.disconnect(msg="Be right back.")
339 if cmd.split()[0] == "quiet":
340 self.nofish(cmd,nick,conn, public)
343 elif cmd.split()[0] == "say" \
344 and irc_lower(nick) == irc_lower(self.owner):
345 conn.privmsg(self.channel, string.join(cmd.split()[1:]))
347 # action to msg/channel
348 elif cmd.split()[0] == "do" \
349 and irc_lower(nick) == irc_lower(self.owner):
350 conn.action(self.channel, string.join(cmd.split()[1:]))
354 self.quit(cmd, nick, conn, public)
357 elif (cmd.split()[0] == "google" or cmd.split()[0] == "Google"):
358 self.googleq(cmd, nick, conn, public)
361 if len(sys.argv) != 5: # insufficient arguments
362 print "Usage: acrobat <server[:port]> <channel> <nickname> owner"
364 sv_port = string.split(sys.argv[1], ":", 1) # tuple; (server, port)
366 if len(sv_port) == 2:
368 port = int(sv_port[1])
370 print "Error: Erroneous port."
373 port = 6667 # default irc port
374 channel = sys.argv[2]
375 nickname = sys.argv[3]
378 bot = Acrobat(channel, nickname, server, owner, port)
379 sys.stderr.write("Trying to connect...\n")
383 if __name__ == "__main__":