1 """
2 @Author: code@darkc0de.org
3 @Date: June 6th, 2007
4 @Filename: bot.py
5 @Description:
6
7 This small piece of code (bot.py) is a
8 fairly functional IRC bot designed to deliver
9 headlines from various RSS feeds (CNN, Slashdot,
10 Rotten Tomatoes, Shacknews) to a pre-defined IRC
11 channel upon command.
12
13 Features include spam protection; more specifically,
14 the bot will not re-download RSS feeds until five
15 minutes have passed, in an attempt to saturate less
16 bandwidth (and avoid channel abusers). As another
17 abuse prevention method, the bot will only respond
18 in channel every five seconds.
19
20 Torrent searching (from torrent conglomerate btjunkie.org
21 was added in in version 1.1; although the user can
22 search for torrents (!torrents search term) in either
23 the channel or a private message, the response will
24 only be sent via private message.
25
26 TO DO:
27 I'm going to add in a function to link to the specific
28 stories themselves (much like a real RSS feed) in the
29 next version. This will probably function something like
30 !news 4 (for a link to story number 4 in the news feed.
31
32 POSSIBLE BUGS:
33 bot.py was designed to work on the IRC server
34 chingShih.2600.net. The "hack" mentality of the code
35 involved here may prevent it from connecting successfully
36 to other servers. This should be able to be solved fairly
37 easily by looking at connect messages.
38
39 SOURCE CODE is free and open source under the GPL.
40
41 """
42
43 # gotta import a buncha stuff. you know how i do.
44 import sys
45 import socket
46 import string
47 import time
48 import os
49 import urllib
50
51 # stuff we need to connect our socket, etc.
52 HOST = "chingShih.2600net.org"
53 PORT = 6667
54 NICK = "NewsBot"
55 IDENT = "newsbot"
56 REALNAME = "News Bot"
57 global CHANNEL # bah... those damn global values annoy me.
58 CHANNEL = "#ca2600"
59 readbuffer = ""
60
61 # here's our block of update times... it's messy, i know.
62 # we have to declare them as globals here (and elsewhere!)
63 # to allow other functions to use them. sucks, i know.
64 global newsUPDATE
65 global moviesUPDATE
66 global techUPDATE
67 global gamesUPDATE
68 global UPDATE
69 global torrentUPDATE
70 global VERSION
71
72 newsUPDATE = 0
73 moviesUPDATE = 0
74 techUPDATE = 0
75 gamesUPDATE = 0
76 UPDATE = 0
77 torrentUPDATE = 0
78 VERSION = "1.2"
79
80 # i wrote this up as a simple method to not have to
81 # parse everything to RFC standards. it's awesome.
82 def message(msg, dest):
83 s.send("PRIVMSG " + dest + " :" + msg+"\r\n")
84
85
86 # getheadlines and grabnews code borrowed and
87 # modified from news.py (SMS relay), of which
88 # i am the author.
89
90 def getheadlines(loc, feed, tor):
91 heads = ""
92 num = 0
93 lnum = 0
94 if tor == 0:
95 for line in open(feed, "r").readlines():
96 if line.find( "<title>" ) != -1:
97 num = num + 1
98 if num >= loc and len(heads) + len(line[7:len(line)-8]) <= 200:
99 heads += line[7:len(line)-10] + ", "
100
101 # you know what? the code might be messy, but it's functional!
102
103 elif tor == 1:
104 for line in open(feed, "r").readlines():
105 if line.find("<title>") != -1:
106 num = num + 1
107 if num >= loc and len(heads) + len(line[7:len(line)-8]) <= 200:
108 heads += line[7:len(line)-10] + " --- "
109 elif line.find("<link>") != -1:
110 lnum = lnum + 1
111 if lnum >= loc and len(heads) + len(line[6:len(line)-7]) <= 200:
112 heads += line[6:len(line)-7] + " ; "
113
114 return heads
115
116
117 # we throw our query to this to see if we can grab headlines.
118 # if we don't find anything, it obviously won't give us anything.
119 # it might be more efficient to check *before* throwing around
120 # CPU cycles, but whatever.
121 def grabnews(sub):
122
123 # again, we had to define these as globals so we don't
124 # get an undefined error... python can be silly somtimes,
125 # i guess...
126
127 global newsUPDATE
128 global moviesUPDATE
129 global techUPDATE
130 global gamesUPDATE
131
132 if sub == "help" or sub == "version":
133 message("This bot will send headlines to the channel.", CHANNEL)
134 message("Current commands: !news, !tech, !movies, !games", CHANNEL)
135 message("New: Torrent search! !torrents search terms",CHANNEL)
136 message("NewsBot v"+VERSION+" -- written by code", CHANNEL)
137
138 body = "" # obviously nothing in the body yet!
139 # also: 'body' is a throwback from news.py,
140 # in which this was the body of an email/SMS
141
142 # grab latest headlines and put it in a file.
143 if sub == "news":
144 print newsUPDATE
145 if(time.time()-newsUPDATE > 300):
146 newsUPDATE = time.time()
147 print "--- grabbing the CNN feed"
148 text = urllib.urlopen("http://rss.cnn.com/rss/cnn_topstories.rss").read()
149 fout = open("newsFeed", "w")
150 fout.write(text)
151 fout.close()
152 print "--- CNN feed written"
153 body = getheadlines(3, "newsFeed", 0)
154
155 # needs better parsing -- currently not functional.
156 elif sub == "sports":
157 if(time.time()-sportsUPDATE > 300):
158 sportUPDATE = time.time()
159 print "--- grabbing the sports feed from Google"
160 text = urllib.urlopen("http://news.google.com/?ned=us&topic=s&output=rss").read()
161 fout = open("feed", "w")
162 fout.write(text)
163 fout.close()
164 print "sports feed written"
165 body = getheadlines(2, "techFeed", 0)
166
167
168 elif sub == "games":
169 if(time.time()-gamesUPDATE > 300):
170 gamesUPDATE = time.time()
171 print "--- grabbing the shacknews feed"
172 text = urllib.urlopen("http://www.shacknews.com/headlines.rdf").read()
173 fout = open("gamesFeed", "w", 0)
174 fout.write(text)
175 fout.close()
176 print "Shacknews feed written"
177 body = getheadlines(2, "gamesFeed", 0)
178
179
180 elif sub == "movies":
181 if(time.time()-moviesUPDATE > 300):
182 moviesUPDATE = time.time()
183 print "--- getting the rotten tomatoes feed"
184 text = urllib.urlopen("http://i.rottentomatoes.com/syndication/rss/complete_movies.xml").read()
185 fout = open("moviesFeed", "w")
186 fout.write(text)
187 fout.close()
188 print "rotten tomatoes feed written"
189 body = getheadlines(3, "moviesFeed", 0)
190
191
192 elif sub == "tech":
193 if(time.time()-techUPDATE > 300):
194 techUPDATE = time.time()
195 print "--- grabbing the slashdot feed"
196 text = urllib.urlopen("http://rss.slashdot.org/Slashdot/slashdot").read()
197 fout = open("techFeed", "w")
198 fout.write(text)
199 fout.close()
200 print "slashdot feed written"
201 body = getheadlines(3, "techFeed", 0)
202
203 elif sub == "test" or sub == "ping":
204 body = "I am alive!! ... NewsBot v"+VERSION+" by code"
205
206 message(body, CHANNEL)
207
208 ### end borrowed/modified code
209
210 # this method will grab torrent titles and
211 # links from BTJunkie, which searches other
212 # trackers...
213
214 # grabbing torrents has to be different than the
215 # rest because it alone includes more parameters
216 # (after all, we *are* searching...
217 def grabtorrent(search, user):
218 global torrentUPDATE
219 # we're nicer to torrent searchers...
220 if(time.time()-torrentUPDATE > 30):
221 torrentUPDATE = time.time()
222 print "--- searching btjunkie for " + search
223 text = urllib.urlopen("http://btjunkie.org/rss.xml?query="+search).read()
224 fout = open("torrentsFeed", "w")
225 fout.write(text)
226 fout.close()
227 print "torrents feed writen"
228 body = getheadlines(2, "torrentsFeed", 1)
229
230 message(body, user)
231
232
233 print """
234 _ _ ____ _
235 | \ | | _____ _____| __ ) ___ | |_
236 | \| |/ _ \ \ /\ / / __| _ \ / _ \| __|
237 | |\ | __/\ V V /\__ \ |_) | (_) | |_
238 |_| \_|\___| \_/\_/ |___/____/ \___/ \__|
239
240 -------------------> darkc0de industries
241 """
242
243 print "--- creating socket"
244 s = socket.socket( )
245 print "--- connecting to %s, port %s" % (HOST, PORT)
246 s.connect((HOST, PORT))
247 s.send("NICK %s\r\n" % NICK)
248 s.send("USER %s %s bla :%s\r\n" % (IDENT, HOST, REALNAME))
249 s.send("JOIN %s\r\n" % CHANNEL)
250 print "--- joined %s as %s" % (CHANNEL, NICK)
251
252
253 # code borrowed from O'Reilly's tutorial on
254 # python irc bots. ... then modified to all
255 # hell. it was still helpful, though!
256 while 1:
257
258 readbuffer=readbuffer+s.recv(1024)
259 temp=string.split(readbuffer, "\n")
260 readbuffer=temp.pop()
261
262 for line in temp:
263 line=string.rstrip(line)
264 line=string.split(line)
265
266 if(line[1] == "PRIVMSG" and str(line[0]) == ":code!code@judecca.aculei.net"):
267 if(str(line[3]) == ":!shutdown" or str(line[3]) == ":!die"):
268 message("Aww... my master is euthanizing me...", CHANNEL)
269 sys.exit(0)
270
271 if(line[1] == "PRIVMSG" and time.time()-UPDATE > 5):
272 if(str(line[2]) == CHANNEL and str(line[3])[:2] == ":!"):
273 print "--- trying " + str(line[3])[1:]
274 grabnews(str(line[3])[2:])
275 UPDATE = time.time()
276
277 if(str(line[3])[1:] == "!torrents"):
278 print "--- trying to get torrent files"
279
280 if(len(line) > 4):
281 query = ""
282 x = 4
283 while x < len(line):
284 query += line[x]
285 x = x + 1
286 user = str(line[0])[1:str(line[0]).find('!')]
287 print "--- trying to find "+query+" and send to "+ user
288 grabtorrent(query, user)
289
290
291 # gotta love IRC's PING? PONG! feature...
292 if(line[0]=="PING"):
293 s.send("PONG %s\r\n" % line[1])
294
295 ### the end! ###