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
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
54 f = open("karmadump", "r")
55 self.karma = cPickle.load(f)
60 f = open("trouts", "r")
61 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
64 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."]
68 def on_welcome(self, conn, evt):
69 conn.join(self.channel)
71 def on_privmsg(self, conn, evt):
72 self.do_command(nm_to_n(evt.source()), evt.arguments()[0])
74 def on_pubmsg(self, conn, evt):
75 payload = evt.arguments()[0]
76 a = string.split(evt.arguments()[0], " ", 1)
78 and (irc_lower(a[0]) == irc_lower(self.connection.get_nickname())
79 or irc_lower(a[0])[:-1] == irc_lower(self.connection.get_nickname())):
81 self.do_command(self.channel, string.strip(a[1]), public = 1)
82 if a[0].endswith("++"):
84 if a[0].endswith("--"):
86 if payload[0] == "!" and len(payload)>1:
87 self.do_command(self.channel, string.strip(payload[1:]), public=1)
88 if payload[0] == "~" and len(payload)>1:
89 self.do_command(self.channel, string.strip(payload[1:]), public=1)
91 # And now bot commands;
94 def karmaup(self, cmd):
95 if self.karma.dict.has_key(cmd.split()[0][:-2]):
96 self.karma.dict[cmd.split()[0][:-2]] += 1
98 self.karma.dict[cmd.split()[0][:-2]] = 1
101 def karmadown(self, cmd):
102 if self.karma.dict.has_key(cmd.split()[0][:-2]):
103 self.karma.dict[cmd.split()[0][:-2]] -= 1
105 self.karma.dict[cmd.split()[0][:-2]] = -1
108 def karmaq(self, cmd, conn, nick, public):
112 if self.karma.dict.has_key(cmd.split()[1]):
113 conn.privmsg(self.channel, "%s has karma %s."
115 self.karma.dict[cmd.split()[1]]))
117 conn.privmsg(self.channel, "%s has no karma set." %
120 conn.privmsg(self.channel, "I have karma on %s items." %
121 len(self.karma.dict.keys()))
125 if self.karma.dict.has_key(cmd.split()[1]):
126 conn.notice(nick, "%s has karma %s." %
128 self.karma.dict[cmd.split()[1]]))
130 conn.notice(nick, "I have karma on %s items." %
131 len(self.karma.dict.keys()))
133 conn.notice(nick, "I have karma on %s items." %
134 len(self.karma.dict.keys()))
136 def infoq(self, cmd, nick, conn, public):
137 # version control magic
138 acrorevision="$Revision$"
139 acrorev1=re.sub(r'\$Revision: (.*)',r'\1',acrorevision)
140 acroversion=re.sub(r'(.*) \$',r'\1',acrorev1)
142 conn.privmsg(self.channel,
143 "I am Acrobat %s, on %s, as nick %s." %
144 (acroversion, self.channel, self.connection.get_nickname()))
145 conn.privmsg(self.channel,
146 "My owner is %s; I have karma on %s items." %
147 (self.owner, len(self.karma.dict.keys())))
149 conn.notice(nick, "I am Acrobat %s, on %s, as nick %s." %
150 (acroversion, self.channel, self.connection.get_nickname()))
151 conn.notice(nick, "My owner is %s; I have karma on %s items." %
152 (self.owner, len(self.karma.dict.keys())))
155 def troutq(self, cmd, nick, conn, public):
157 target = string.join(cmd.split()[1:])
158 me = self.connection.get_nickname()
159 trout_msg = random.choice(self.trouts)
160 # # The bot is loyal(ish)...
161 # if target.lower() == self.owner.lower():
164 if me.lower() == target.lower():
166 conn.action(self.channel, trout_msg % target)
168 if random.random() <= 0.1:
169 conn.action(self.channel, "notes %s is conducting a whispering campaign" % nick)
171 conn.notice(nick, "Who do you wish me to trout?")
174 def reloadq(self, cmd, nick, conn, public):
175 if irc_lower(nick) == irc_lower(self.owner):
178 f = open("trouts", "r")
179 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
181 conn.notice(nick, "I am re-armed!")
183 conn.notice(nick, "Trout re-arming failed!")
186 conn.notice(nick, "This command can only be invoked by my owner.")
189 def quit(self, cmd, nick, conn, public):
190 if irc_lower(nick) == irc_lower(self.owner):
191 f = open("karmadump", "w")
192 cPickle.dump(self.karma, f)
194 self.die(msg="I have been chosen!")
196 conn.privmsg(nick, "Such aggression in public!")
198 conn.notice(nick, "You're not my owner.")
200 # google for something
201 def googleq(self, cmd, nick, conn, public):
202 cmdrest = string.join(cmd.split()[1:])
203 # "I'm Feeling Lucky" rather than try and parse the html
204 targ = ("http://www.google.com/search?q=%s&btnI=I'm+Feeling+Lucky"
205 % urllib.quote_plus(cmdrest))
207 # get redirected and grab the resulting url for returning
208 gsearch = urllib.urlopen(targ).geturl()
209 if gsearch != targ: # we've found something
211 conn.notice(nick, str(gsearch))
212 else: # we haven't found anything.
213 conn.privmsg(nick, str(gsearch))
216 conn.notice(nick, "No pages found.")
218 conn.privmsg(nick, "No pages found.")
219 except IOError: # if the connection times out. This blocks. :(
221 conn,notice(nick, "The web's broken. Waah!")
223 conn.privmsg(nick, "The web's broken. Waah!")
225 # General query handler
226 def do_command(self, nick, cmd, public=0):
227 conn = self.connection
229 if cmd.split()[0].endswith("++"):
233 if cmd.split()[0].endswith("--"):
237 if cmd.split()[0] == "karma" or cmd.split()[0] == "Karma":
238 self.karmaq(cmd, conn, nick, public)
240 # bot's vital statistics
242 self.infoq(cmd, nick, conn, public)
245 if cmd.split()[0] == "trout":
246 self.troutq(cmd, nick, conn, public)
248 self.reloadq(cmd, nick, conn, public)
251 if cmd == "disconnect": # hop off for 60s
252 self.disconnect(msg="Be right back.")
255 elif cmd.split()[0] == "say" \
256 and irc_lower(nick) == irc_lower(self.owner):
257 conn.privmsg(self.channel, string.join(cmd.split()[1:]))
259 # action to msg/channel
260 elif cmd.split()[0] == "do" \
261 and irc_lower(nick) == irc_lower(self.owner):
262 conn.action(self.channel, string.join(cmd.split()[1:]))
266 self.quit(cmd, nick, conn, public)
269 elif (cmd.split()[0] == "google" or cmd.split()[0] == "Google"):
270 self.googleq(cmd, nick, conn, public)
273 if len(sys.argv) != 5: # insufficient arguments
274 print "Usage: acrobat <server[:port]> <channel> <nickname> owner"
276 sv_port = string.split(sys.argv[1], ":", 1) # tuple; (server, port)
278 if len(sv_port) == 2:
280 port = int(sv_port[1])
282 print "Error: Erroneous port."
285 port = 6667 # default irc port
286 channel = sys.argv[2]
287 nickname = sys.argv[3]
290 bot = Acrobat(channel, nickname, server, owner, port)
291 sys.stderr.write("Trying to connect...\n")
295 if __name__ == "__main__":