chiark / gitweb /
a8b6cc8421ff93e2c5c1565c0609b49b39a3352b
[irc.git] / acrobat-chiark-0.2.py
1 #!/usr/bin/env python2
2 # $Id$
3 #
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>
8
9 # This file is part of Acrobat.
10 #
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.
15 #
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.
20
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
24 # USA.
25
26 """
27     disconnect -- Disconnect the bot.  The bot will try to reconnect
28                   after 60 seconds.
29
30     die -- Let the bot cease to exist.
31
32     google -- search, 'I'm Feeling Lucky', and notice the user who searches
33               back with the url.
34 """
35
36 import string, urllib, sys, cPickle, os, random
37 from ircbot import SingleServerIRCBot
38 from irclib import nm_to_n, irc_lower
39
40
41
42 class Karma:
43     def __init__(self):
44         self.dict = {}
45     
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
51         self.owner = owner
52         # load the karma db
53         try:
54             f = open("karmadump", "r")
55             self.karma = cPickle.load(f)
56             f.close()
57         except IOError:
58             self.karma = Karma()
59         try:
60             f = open("trouts", "r")
61             self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
62             f.close()
63         except IOError:
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."]
65
66     ## EVENT HANDLERS
67             
68     def on_welcome(self, conn, evt):
69         conn.join(self.channel)
70
71     def on_privmsg(self, conn, evt):
72         self.do_command(nm_to_n(evt.source()), evt.arguments()[0])
73         
74     def on_pubmsg(self, conn, evt):
75         payload = evt.arguments()[0]
76         a = string.split(evt.arguments()[0], " ", 1)
77         if len(a) > 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())):
80
81             self.do_command(self.channel, string.strip(a[1]), public = 1)
82         if a[0].endswith("++"):
83             self.karmaup(a[0])
84         if a[0].endswith("--"):
85             self.karmadown(a[0])
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)
90
91     # And now bot commands;
92
93     # increment karma
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
97         else:
98             self.karma.dict[cmd.split()[0][:-2]] = 1
99
100     #decrement karma
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
104         else:
105             self.karma.dict[cmd.split()[0][:-2]] = -1
106
107     # query karma
108     def karmaq(self, cmd, conn, nick, public):
109         # in public
110         if public == 1:
111             try:
112                 if self.karma.dict.has_key(cmd.split()[1]):
113                     conn.privmsg(self.channel, "%s has karma %s."
114                                  %(cmd.split()[1],
115                                        self.karma.dict[cmd.split()[1]]))
116                 else:
117                     conn.privmsg(self.channel, "%s has no karma set." %
118                                  cmd.split()[1])
119             except IndexError:
120                 conn.privmsg(self.channel, "I have karma on %s items." %
121                              len(self.karma.dict.keys()))
122         # in private
123         else:
124             try:
125                 if self.karma.dict.has_key(cmd.split()[1]):
126                     conn.notice(nick, "%s has karma %s." %
127                                 (cmd.split()[1],
128                                  self.karma.dict[cmd.split()[1]]))
129                 else:
130                     conn.notice(nick, "I have karma on %s items." %
131                                 len(self.karma.dict.keys()))
132             except IndexError:
133                 conn.notice(nick, "I have karma on %s items." %
134                             len(self.karma.dict.keys()))
135     # query bot status
136     def infoq(self, cmd, nick, conn, public):
137         if public == 1:
138             conn.privmsg(self.channel,
139                          "I am Acrobat 0.2.1chiark, on %s, as nick %s." %
140                          (self.channel, self.connection.get_nickname()))
141             conn.privmsg(self.channel,
142                          "My owner is %s; I have karma on %s items." %
143                          (self.owner, len(self.karma.dict.keys())))
144         else:
145             conn.notice(nick, "I am Acrobat 0.2.1chiark, on %s, as nick %s." %
146                         (self.channel, self.connection.get_nickname()))
147             conn.notice(nick, "My owner is %s; I have karma on %s items." %
148                         (self.owner, len(self.karma.dict.keys())))
149
150     # trout someone
151     def troutq(self, cmd, nick, conn, public):
152             try:
153                 target = string.join(cmd.split()[1:])
154                 me = self.connection.get_nickname()
155                 trout_msg = random.choice(self.trouts)
156 #                # The bot is loyal(ish)...
157 #                if target.lower() == self.owner.lower():
158 #                    target = nick
159                 # ...and touchy.
160                 if me.lower() == target.lower():
161                     target = nick
162                 conn.action(self.channel, trout_msg % target)
163                 if public == 0:
164                     if random.random() <= 0.1:
165                         conn.action(self.channel, "notes %s is conducting a whispering campaign" % nick)
166             except IndexError:
167                 conn.notice(nick, "Who do you wish me to trout?")
168
169     # stock up on trouts
170     def reloadq(self, cmd, nick, conn, public):
171         if irc_lower(nick) == irc_lower(self.owner):
172             tback = self.trouts
173             try:
174                 f = open("trouts", "r")
175                 self.trouts = [l.strip() for l in f.readlines() if l.find("%s") != -1]
176                 f.close()
177                 conn.notice(nick, "I am re-armed!")
178             except IOError:
179                 conn.notice(nick, "Trout re-arming failed!")
180                 self.trouts = tback
181         else:
182             conn.notice(nick, "This command can only be invoked by my owner.")
183
184     # quit irc
185     def quit(self, cmd, nick, conn, public):
186         if irc_lower(nick) == irc_lower(self.owner):
187             f = open("karmadump", "w")
188             cPickle.dump(self.karma, f)
189             f.close()
190             self.die(msg="I have been chosen!")
191         elif public == 1:
192             conn.privmsg(nick, "Such aggression in public!")
193         else:
194             conn.notice(nick, "You're not my owner.")
195             
196     # google for something
197     def googleq(self, cmd, nick, conn, public):
198         cmdrest = string.join(cmd.split()[1:])
199         # "I'm Feeling Lucky" rather than try and parse the html
200         targ = ("http://www.google.com/search?q=%s&btnI=I'm+Feeling+Lucky"
201                 % urllib.quote_plus(cmdrest))
202         try:
203             # get redirected and grab the resulting url for returning
204             gsearch = urllib.urlopen(targ).geturl()
205             if gsearch != targ: # we've found something
206                 if public == 0:
207                     conn.notice(nick, str(gsearch))
208                 else: # we haven't found anything.
209                     conn.privmsg(nick, str(gsearch))
210             else:
211                 if public == 0:
212                     conn.notice(nick, "No pages found.")
213                 else:
214                     conn.privmsg(nick, "No pages found.")
215         except IOError: # if the connection times out. This blocks. :(
216             if public == 0:
217                 conn,notice(nick, "The web's broken. Waah!")
218             else:
219                 conn.privmsg(nick, "The web's broken. Waah!")
220                 
221     # General query handler
222     def do_command(self, nick, cmd, public=0):
223         conn = self.connection
224         # karma: up
225         if cmd.split()[0].endswith("++"):
226             self.karmaup(cmd)
227         
228         # karma: down
229         if cmd.split()[0].endswith("--"):
230             self.karmadown(cmd)
231             
232         # karma: query
233         if cmd.split()[0] == "karma" or cmd.split()[0] == "Karma":
234             self.karmaq(cmd, conn, nick, public)
235             
236         # bot's vital statistics
237         if cmd == "info":
238             self.infoq(cmd, nick, conn, public)
239
240         # weaponry
241         if cmd.split()[0] == "trout":
242             self.troutq(cmd, nick, conn, public)
243         if cmd == "reload":
244             self.reloadq(cmd, nick, conn, public)
245         
246         #disconnect
247         if cmd == "disconnect": # hop off for 60s
248             self.disconnect(msg="Be right back.")
249
250         # say to msg/channel
251         elif cmd.split()[0] == "say" \
252              and irc_lower(nick) == irc_lower(self.owner):
253             conn.privmsg(self.channel, string.join(cmd.split()[1:]))
254
255         # action to msg/channel
256         elif cmd.split()[0] == "do" \
257              and irc_lower(nick) == irc_lower(self.owner):
258             conn.action(self.channel, string.join(cmd.split()[1:]))
259
260         # quit IRC
261         elif cmd == "die":
262             self.quit(cmd, nick, conn, public)
263
264         # Google!
265         elif (cmd.split()[0] == "google" or cmd.split()[0] == "Google"):
266             self.googleq(cmd, nick, conn, public)            
267
268 def main():
269     if len(sys.argv) != 5: # insufficient arguments
270         print "Usage: acrobat <server[:port]> <channel> <nickname> owner"
271         sys.exit(1)
272     sv_port = string.split(sys.argv[1], ":", 1) # tuple; (server, port)
273     server = sv_port[0]
274     if len(sv_port) == 2:
275         try:
276             port = int(sv_port[1])
277         except ValueError:
278             print "Error: Erroneous port."
279             sys.exit(1)
280     else:
281         port = 6667 # default irc port
282     channel = sys.argv[2]
283     nickname = sys.argv[3]
284     owner = sys.argv[4]
285     # initialize the bot
286     bot = Acrobat(channel, nickname, server, owner, port)
287     sys.stderr.write("Trying to connect...\n")
288     # and the event loop
289     bot.start()
290
291 if __name__ == "__main__":
292     main()