1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package com.levelonelabs.aimbot.modules;
35
36
37 import java.io.BufferedReader;
38 import java.io.InputStreamReader;
39
40 import java.net.URL;
41 import java.net.URLConnection;
42
43 import java.util.ArrayList;
44 import java.util.StringTokenizer;
45 import java.util.logging.Logger;
46
47 import javax.swing.text.MutableAttributeSet;
48 import javax.swing.text.html.HTML;
49 import javax.swing.text.html.HTML.Tag;
50 import javax.swing.text.html.HTMLEditorKit.ParserCallback;
51 import javax.swing.text.html.parser.ParserDelegator;
52
53 import com.levelonelabs.aim.AIMBuddy;
54 import com.levelonelabs.aimbot.AIMBot;
55 import com.levelonelabs.aimbot.BotModule;
56
57
58 /***
59 * Handles requests to get movie times based on zip and/or theater and lists theaters by zip code.
60 *
61 * @author John G Ledo <A HREF="mailto:john@imssoft.com">john@imssoft.com</A>
62 *
63 * @created October 3, 2003
64 */
65 public class MoviesModule extends BotModule {
66 static Logger logger=Logger.getLogger(MoviesModule.class.getName());
67 private static ArrayList services;
68
69 /***
70 * Initialize the service commands.
71 */
72 static {
73 services=new ArrayList();
74 services.add("movies");
75 services.add("movieslegend");
76 services.add("theaters");
77 }
78
79 /***
80 * Constructor for MoviesModule.
81 *
82 * @param bot
83 */
84 public MoviesModule(AIMBot bot) {
85 super(bot);
86 }
87
88 /***
89 * @see com.levelonelabs.aimbot.BotModule#getName()
90 */
91 public String getName() {
92 return "Movies Module";
93 }
94
95
96 /***
97 * @see com.levelonelabs.aimbot.BotModule#getServices()
98 */
99 public ArrayList getServices() {
100 return services;
101 }
102
103
104 /***
105 * @see com.levelonelabs.aimbot.BotModule#help()
106 */
107 public String help() {
108 StringBuffer sb=new StringBuffer();
109 sb.append("<B>theaters <i>ZIPCODE<i></B> (displays a list of theaters in your area)\n");
110 sb.append(
111 "* If the preference \"zipcode\" is set, you can omit the zipcode to use your default.\n");
112 sb.append(
113 "<B>movies <i>ZIPCODE [Theater Number]</i></B> (displays movie times for the theater closest to your zipcode if a theater number is not provided)\n");
114 sb.append(
115 "* If the preference \"zipcode\" is set, you can omit the zipcode to use your default.\n");
116 sb.append("<B>movieslegend</B> (displays the legend for movie times)");
117 return sb.toString();
118 }
119
120
121 /***
122 * Grab the movie times (or list the theater not done)
123 *
124 * @param buddy the buddy
125 * @param query the request
126 */
127 public void performService(AIMBuddy buddy, String query) {
128 StringTokenizer st=new StringTokenizer(query, " ");
129 String zipPref=buddy.getPreference("zipcode");
130 String zipMovie=buddy.getPreference("movieszipcode");
131 String imcommand=st.nextToken();
132 String zipcode="";
133 String theatercode="";
134
135 if(imcommand.trim().equalsIgnoreCase("MOVIES")) {
136
137
138
139
140
141
142
143
144
145 if(st.countTokens() == 2) {
146 zipcode=((String) st.nextElement()).trim();
147 theatercode=((String) st.nextElement()).trim();
148 } else {
149 if(st.hasMoreElements()) {
150 zipcode=((String) st.nextElement()).trim();
151 if(zipcode.length() == 1) {
152 theatercode=zipcode;
153 zipcode=zipMovie;
154 if((zipcode == null) || (zipcode.length() != 5)) {
155 zipcode=zipPref;
156 }
157 }
158 } else if(zipPref != null) {
159 zipcode=zipPref;
160 }
161 }
162
163 if( zipcode!=null && zipcode.length() == 5) {
164 MovieTimesScraper scrapper;
165 if(theatercode.length() == 1) {
166 scrapper=new MovieTimesScraper(zipcode, theatercode);
167 } else {
168 scrapper=new MovieTimesScraper(zipcode);
169 }
170 if(zipPref == null) {
171
172 buddy.setPreference("zipcode", zipcode);
173 }
174 Object[] list=scrapper.getTimes().toArray();
175 if((list.length < 1) || (((String) list[0]).length() < 1)) {
176 super.sendMessage(buddy, "Could not find movie times.");
177 return;
178 }
179 for(int i=0; i < list.length; i++) {
180 logger.fine("Sending movie message #"+i);
181 super.sendMessage(buddy, ((String) list[i]));
182 }
183 } else {
184 super.sendMessage(buddy, "ERROR:\n"+help());
185 }
186 } else if(imcommand.trim().equalsIgnoreCase("THEATERS")) {
187 if(st.hasMoreElements()) {
188 zipcode=((String) st.nextElement()).trim();
189 } else if(zipPref != null) {
190 zipcode=zipPref;
191 }
192 if(zipcode.length() == 5) {
193 buddy.setPreference("movieszipcode", zipcode);
194 MovieTheaterScraper scrapper=new MovieTheaterScraper(zipcode);
195 if(zipPref == null) {
196
197 buddy.setPreference("zipcode", zipcode);
198 }
199 String list=scrapper.getTheaters();
200 if((list == null) || (list.length() < 1)) {
201 super.sendMessage(buddy, "Could not find theaters.");
202 } else {
203 list="<B>Theaters for "+zipcode+"</B>\n"+list;
204 list=list+
205 "\n\nSend <b>movies <i>theaternumber</i></b> to obtain movie times for that theater.\n";
206 super.sendMessage(buddy, list);
207 }
208 } else {
209 super.sendMessage(buddy, "ERROR:\n"+help());
210 }
211 } else if(imcommand.trim().equalsIgnoreCase("MOVIESLEGEND")) {
212 super.sendMessage(buddy,
213 "<b>Legend:</b>\n"+"( ) - discounted / bargain shows.\n"+
214 "* - Sony Digital Dynamic Sound (SDDS)\n"+"** - Digital Theater Systems (DTS)\n"+
215 "*** - Dolby Digital Surround EX\n"+"**** - Dolby Digital\n"+
216 "***** - Dolby Stereo\n");
217 }
218 }
219 }
220
221
222
223 class MovieTimesScraper extends ParserCallback {
224 static Logger logger=Logger.getLogger(MoviesModule.class.getName());
225 private String urlPart="http://movies.yahoo.com/showtimes/showtimes.html?m=&t=&a=&s=tm&r=sim&p=0&get=Get+Showtimes&z=";
226 private String zipcode;
227 private String line;
228 private int stage;
229 private String theater;
230 private String theaterNext;
231 private boolean areLinkTimes;
232
233
234
235 private ArrayList result;
236
237 /***
238 * Contructor for MovieTimesScraper
239 *
240 * @param zipcode
241 */
242 public MovieTimesScraper(String zipcode) {
243 this.zipcode=zipcode;
244 this.result=new ArrayList();
245 this.line="";
246 this.theater="0";
247 this.theaterNext="1";
248 this.stage=0;
249 this.areLinkTimes=false;
250 }
251
252
253 /***
254 * Contructor for MovieTimesScraper
255 *
256 * @param zipcode
257 * @param theater
258 */
259 public MovieTimesScraper(String zipcode, String theater) {
260 this.zipcode=zipcode;
261 this.result=new ArrayList();
262 this.line="";
263 this.stage=0;
264 this.areLinkTimes=false;
265 try {
266 int num=Integer.parseInt(theater);
267 this.theater=theater;
268 this.theaterNext=String.valueOf((num+1));
269 } catch(Exception ex) {
270 this.theater="0";
271 this.theaterNext="1";
272 }
273 }
274
275 /***
276 * Looks for link 'T0' for start of first theater. Then scrape text until T1 link.
277 *
278 * @param t the tag
279 * @param a unused
280 * @param pos unused
281 */
282 public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
283 if((t == HTML.Tag.A) && (a.getAttribute(HTML.Attribute.NAME) != null) &&
284 a.getAttribute(HTML.Attribute.NAME).equals("T"+theater)) {
285 stage=1;
286 } else if((t == HTML.Tag.A) && (stage == 1)) {
287 stage=2;
288 } else if((t == HTML.Tag.A) && (a.getAttribute(HTML.Attribute.NAME) != null) &&
289 a.getAttribute(HTML.Attribute.NAME).equals("T"+theaterNext)) {
290 stage=5;
291 }
292 }
293
294
295 /***
296 * Looks for links
297 *
298 * @param t A tag
299 * @param pos unused
300 */
301 public void handleEndTag(Tag t, int pos) {
302 if((t == HTML.Tag.A) && (stage == 2)) {
303 stage=3;
304 } else if((t == HTML.Tag.A) && (stage == 3)) {
305 stage=4;
306 } else if((t == HTML.Tag.HTML) && (line.length() > 0)) {
307
308 result.add(line);
309 line="";
310 } else if(t == HTML.Tag.TD) {
311 if(areLinkTimes) {
312 areLinkTimes=false;
313 line=line+"\n";
314 }
315 if(stage == 4) {
316 stage=3;
317 }
318 }
319 }
320
321
322 /***
323 * Grabs the theater text
324 *
325 * @param data the text
326 * @param pos unused
327 */
328 public void handleText(char[] data, int pos) {
329 String temp=new String(data);
330 if(stage == 2 && !temp.equals("Add to My Favorite Theaters")) {
331 line="\n<B>"+temp+"</B>\n";
332 } else if((stage == 3) && (temp.length() > 1) &&
333 (temp.toUpperCase().indexOf("CLICK") == -1) &&
334 (temp.toUpperCase().indexOf(" ]") == -1) &&
335 (temp.toUpperCase().indexOf(" |") == -1) &&
336 (temp.toUpperCase().indexOf("THEATER INFO") == -1) &&
337 (temp.toUpperCase().indexOf("MAP IT") == -1)) {
338 if(temp.toUpperCase().indexOf("BUY TICKETS") != -1) {
339
340
341
342
343 areLinkTimes=true;
344 } else {
345 if((line.length()+data.length) > 950) {
346 result.add(line);
347 line="<B>"+temp+"</B>";
348 } else {
349 line=line+"<B>"+temp+"</B>";
350 }
351 if(!areLinkTimes) {
352 line=line+"\n";
353 }
354 }
355 } else if((stage == 4) && (temp.length() > 1) &&
356 (temp.toUpperCase().indexOf("CLICK") == -1) &&
357 (temp.toUpperCase().indexOf(" |") == -1) &&
358 (temp.toUpperCase().indexOf("THEATER INFO") == -1) &&
359 (temp.toUpperCase().indexOf("MAP IT") == -1)) {
360 if(temp.toUpperCase().indexOf("BUY TICKETS") != -1) {
361
362
363
364
365 areLinkTimes=true;
366 } else {
367 if((line.length()+data.length) > 950) {
368 result.add(line);
369 line=temp;
370 } else {
371 line=line+temp;
372 }
373 if(!areLinkTimes) {
374 line=line+"\n";
375 }
376 }
377 }
378 }
379
380
381 /***
382 * Grabs the movie times
383 *
384 * @return The text
385 */
386 public ArrayList getTimes() {
387 try {
388 URL url=new URL(urlPart+zipcode);
389 logger.fine("Looking for movies for: "+zipcode+" using URL="+url);
390 URLConnection conn=url.openConnection();
391 BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));
392 ParserDelegator pd=new ParserDelegator();
393 pd.parse(br, this, true);
394 } catch(Exception e) {
395 e.printStackTrace();
396 }
397 return this.result;
398 }
399 }
400
401
402
403 class MovieTheaterScraper extends ParserCallback {
404 static Logger logger=Logger.getLogger(MoviesModule.class.getName());
405 private String urlPart="http://movies.yahoo.com/showtimes/showtimes.html?m=&t=&a=&s=tm&r=sim&p=0&get=Get+Showtimes&z=";
406 private String zipcode;
407 private String result;
408 private String line;
409 private boolean startA=false;
410 private boolean createList=true;
411
412 /***
413 * Contructor for MovieTimesScraper
414 *
415 * @param zipcode
416 */
417 public MovieTheaterScraper(String zipcode) {
418 this.zipcode=zipcode;
419 this.result="";
420 this.line="";
421 }
422
423 /***
424 * Looks for links 'T0' to 'T9' and creates a list
425 *
426 * @param t the tag
427 * @param a unused
428 * @param pos unused
429 */
430 public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
431 if((a.getAttribute(HTML.Attribute.HREF) != null) &&
432 a.getAttribute(HTML.Attribute.HREF).equals("#T10")) {
433 createList=false;
434 }
435
436 if((t == HTML.Tag.A) && (a.getAttribute(HTML.Attribute.HREF) != null) &&
437 (((String) a.getAttribute(HTML.Attribute.HREF)).startsWith("#T")) && createList) {
438 startA=true;
439 line=a.getAttribute(HTML.Attribute.HREF)+" ";
440 }
441 }
442
443
444 /***
445 * Looks for links
446 *
447 * @param t A tag
448 * @param pos unused
449 */
450 public void handleEndTag(Tag t, int pos) {
451 if((t == HTML.Tag.A) && startA) {
452 startA=false;
453 }
454 }
455
456
457 /***
458 * Grabs the theater text
459 *
460 * @param data the text
461 * @param pos unused
462 */
463 public void handleText(char[] data, int pos) {
464 if(createList && startA) {
465 result=result+line.substring(2)+": "+new String(data)+"\n";
466 }
467 }
468
469
470 /***
471 * Grabs a list of theaters
472 *
473 * @return The text
474 */
475 public String getTheaters() {
476 try {
477 URL url=new URL(urlPart+zipcode);
478 logger.fine("Looking for theaters for: "+zipcode+" using URL="+url);
479 URLConnection conn=url.openConnection();
480 BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));
481 ParserDelegator pd=new ParserDelegator();
482 pd.parse(br, this, true);
483 } catch(Exception e) {
484 e.printStackTrace();
485 return result;
486 }
487 return result;
488 }
489 }