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