Don't allow unknown currency amounts to get set as max bid. [#733 tagged:committed state:resolved]
- ~
- jbidwatcher
- trunk
- src
- com
- jbidwatcher
- auction
- server
- ebay
- ebayAuction.java
| ebayAuction.java |
|---|
cyberfox 1 package com.jbidwatcher.auction.server.ebay; cyberfox 2 mrs 3 import com.jbidwatcher.auction.AuctionEntry; cyberfox 4 import com.jbidwatcher.auction.SpecificAuction; mrs 5 import com.jbidwatcher.util.config.*; mrs 6 import com.jbidwatcher.util.Externalized; mrs 7 import com.jbidwatcher.util.queue.MQFactory; mrs 8 import com.jbidwatcher.util.queue.PlainMessageQueue; cyberfox 9 import com.jbidwatcher.util.*; cyberfox 10 import com.jbidwatcher.util.html.JHTML; cyberfox 11 import com.jbidwatcher.util.html.htmlToken; mrs 12 import com.jbidwatcher.util.Constants; cyberfox 13 mrs 14 import java.util.Date; mrs 15 import java.util.regex.Matcher; mrs 16 import java.util.regex.Pattern; cyberfox 17 cyberfox 18 /** mrs 19 * User: Morgan mrs 20 * Date: Feb 25, 2007 mrs 21 * Time: 4:08:52 PM mrs 22 * The core eBay auction parsing class. mrs 23 * Nearly everything about the item is set by code in this class. mrs 24 */ cyberfox 25 class ebayAuction extends SpecificAuction { mrs 26 private static Currency zeroDollars = new Currency("$0.00"); mrs 27 String mBidCountScript = null; mrs 28 String mStartComment = null; cyberfox 29 private static final int TITLE_LENGTH = 60; cyberfox 30 private static final int HIGH_BIT_SET = 0x80; cyberfox 31 private final Pattern p = Pattern.compile(Externalized.getString("ebayServer.thumbSearch"), Pattern.DOTALL | Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); cyberfox 32 private final Pattern p2 = Pattern.compile(Externalized.getString("ebayServer.thumbSearch2"), Pattern.DOTALL | Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); cyberfox 33 private String potentialThumbnail = null; mrs 34 private TT T; cyberfox 35 mrs 36 protected ebayAuction(TT countryProperties) { mrs 37 super(); mrs 38 T = countryProperties; mrs 39 } mrs 40 cyberfox 41 private void checkThumb(StringBuffer sb) { cyberfox 42 Matcher imgMatch = p.matcher(sb); cyberfox 43 if(imgMatch.find()) { cyberfox 44 potentialThumbnail = imgMatch.group(1); cyberfox 45 } else { cyberfox 46 imgMatch = p2.matcher(sb); cyberfox 47 if(imgMatch.find()) { cyberfox 48 potentialThumbnail = imgMatch.group(1); cyberfox 49 } cyberfox 50 } cyberfox 51 } cyberfox 52 cyberfox 53 /** cyberfox 54 * @brief Delete the 'description' portion of a page, all scripts, and comments. cyberfox 55 * cyberfox 56 * @param sb - The StringBuffer to clean of description and scripts. cyberfox 57 */ cyberfox 58 public void cleanup(StringBuffer sb) { cyberfox 59 checkThumb(sb); mrs 60 cyberfox 61 // We ignore the result of this, because it's just useful if it cyberfox 62 // works, it's not critical. cyberfox 63 StringTools.deleteFirstToLast(sb, Externalized.getString("ebayServer.description"), Externalized.getString("ebayServer.descriptionMotors"), Externalized.getString("ebayServer.descriptionEnd"), Externalized.getString("ebayServer.descriptionClosedEnd")); cyberfox 64 StringTools.deleteFirstToLast(sb, Externalized.getString("ebayServer.descStart"), Externalized.getString("ebayServer.descriptionMotors"), Externalized.getString("ebayServer.descEnd"), Externalized.getString("ebayServer.descriptionClosedEnd")); cyberfox 65 cyberfox 66 String skimOver = sb.toString(); cyberfox 67 cyberfox 68 Matcher startCommentSearch = Pattern.compile(Externalized.getString("ebayServer.startedRegex")).matcher(skimOver); mrs 69 if(startCommentSearch.find()) mrs 70 mStartComment = startCommentSearch.group(1); mrs 71 else mrs 72 mStartComment = ""; cyberfox 73 mrs 74 Matcher bidCountSearch = Pattern.compile(T.s("ebayServer.bidCountRegex")).matcher(skimOver); mrs 75 if(bidCountSearch.find()) mrs 76 mBidCountScript = bidCountSearch.group(1); mrs 77 else mrs 78 mBidCountScript = ""; cyberfox 79 cyberfox 80 // Use eBay's cleanup method to finish up with. cyberfox 81 new ebayCleaner().cleanup(sb); cyberfox 82 } cyberfox 83 mrs 84 boolean checkValidTitle(String auctionTitle) { cyberfox 85 String[] eBayTitles = new String[]{ mrs 86 T.s("ebayServer.titleEbay"), mrs 87 T.s("ebayServer.titleEbay2"), mrs 88 T.s("ebayServer.titleEbay3"), mrs 89 T.s("ebayServer.titleMotors"), mrs 90 T.s("ebayServer.titleMotors2"), mrs 91 T.s("ebayServer.titleDisney"), mrs 92 T.s("ebayServer.titleCollections")}; cyberfox 93 cyberfox 94 for (String eBayTitle : eBayTitles) { mrs 95 if(auctionTitle.matches(eBayTitle)) return true; cyberfox 96 } cyberfox 97 cyberfox 98 return false; cyberfox 99 } cyberfox 100 mrs 101 private Currency getUSCurrency(Currency val, JHTML htmlDoc) { mrs 102 Currency newCur = zeroDollars; cyberfox 103 cyberfox 104 if(val != null && !val.isNull()) { mrs 105 if (val.getCurrencyType() == Currency.US_DOLLAR) { cyberfox 106 newCur = val; cyberfox 107 } else { mrs 108 newCur = walkForUSCurrency(htmlDoc); mrs 109 } mrs 110 } mrs 111 mrs 112 return newCur; mrs 113 } mrs 114 mrs 115 /** mrs 116 * If the next text doesn't contain a USD amount, it's separated somehow. mrs 117 * Skim forward until we either find something, or give up. (6 steps for now.) mrs 118 * mrs 119 * @param html - The document to search. mrs 120 * @return - Either zeroDollars or the approximate USD equivalent of the value of the item. mrs 121 */ mrs 122 private Currency walkForUSCurrency(JHTML html) { mrs 123 Currency newCur = zeroDollars; mrs 124 int count = 0; mrs 125 mrs 126 String usdPattern = Externalized.getString("ebayServer.USD"); mrs 127 do { mrs 128 String approxAmount = html.getNextContent(); mrs 129 approxAmount = StringTools.stripHigh(approxAmount, ""); mrs 130 approxAmount = approxAmount.replaceAll("\\s+", " "); mrs 131 int matchAtIndex = approxAmount.indexOf(usdPattern); mrs 132 if (matchAtIndex != -1) { mrs 133 approxAmount = approxAmount.substring(matchAtIndex); //$NON-NLS-1$ mrs 134 newCur = Currency.getCurrency(approxAmount); mrs 135 if (newCur.getCurrencyType() != Currency.US_DOLLAR) newCur = zeroDollars; cyberfox 136 } mrs 137 } while (count++ < 6 && newCur == zeroDollars); cyberfox 138 cyberfox 139 return newCur; cyberfox 140 } cyberfox 141 cyberfox 142 private Pattern digits = Pattern.compile("([0-9]+)"); cyberfox 143 cyberfox 144 int getDigits(String digitsStarting) { cyberfox 145 Matcher m = digits.matcher(digitsStarting); mrs 146 if(m.find()) { mrs 147 String rawCount = m.group(); mrs 148 if (rawCount != null) { mrs 149 return Integer.parseInt(rawCount); mrs 150 } cyberfox 151 } cyberfox 152 return -1; cyberfox 153 } cyberfox 154 cyberfox 155 /** cyberfox 156 * @brief Check the title for unavailable or 'removed item' messages. cyberfox 157 * mrs 158 * @param title - The title from the web page, to check. cyberfox 159 */ mrs 160 private void handleBadTitle(String title) { mrs 161 if(title.indexOf(T.s("ebayServer.unavailable")) != -1) { cyberfox 162 MQFactory.getConcrete("Swing").enqueue("LINK DOWN eBay (or the link to eBay) appears to be down."); cyberfox 163 MQFactory.getConcrete("Swing").enqueue("eBay (or the link to eBay) appears to be down for the moment."); mrs 164 } else if(title.indexOf(T.s("ebayServer.invalidItem")) != -1) { mrs 165 JConfig.log().logDebug("Found bad/deleted item."); cyberfox 166 } else { mrs 167 JConfig.log().logDebug("Failed to load auction title from header: \"" + title + '\"'); cyberfox 168 } cyberfox 169 } cyberfox 170 cyberfox 171 /** cyberfox 172 * @brief Build the title from the data on the web page, pulling HTML tokens out as it goes. cyberfox 173 * cyberfox 174 * @param doc - The document to pull the title from. cyberfox 175 * cyberfox 176 * @return - A string consisting of just the title part of the page, with tags stripped. cyberfox 177 */ cyberfox 178 private String buildTitle(JHTML doc) { cyberfox 179 // This is an HTML title... Suck. cyberfox 180 doc.reset(); cyberfox 181 doc.getNextTag(); cyberfox 182 StringBuffer outTitle = new StringBuffer(TITLE_LENGTH); cyberfox 183 // Iterate over the tokens, adding all content to the cyberfox 184 // title tag until the end of the title. cyberfox 185 htmlToken jh; cyberfox 186 do { cyberfox 187 jh = doc.nextToken(); cyberfox 188 if(jh.getTokenType() == htmlToken.HTML_CONTENT) { cyberfox 189 outTitle.append(jh.getToken()); cyberfox 190 } cyberfox 191 } while(!(jh.getTokenType() == htmlToken.HTML_ENDTAG && cyberfox 192 jh.getToken().equalsIgnoreCase("/title"))); cyberfox 193 cyberfox 194 return outTitle.toString(); cyberfox 195 } cyberfox 196 mrs 197 private Pattern amountPat = Pattern.compile("([0-9]+\\.[0-9]+|(?i)free)"); cyberfox 198 mrs 199 private void loadShippingInsurance(Currency sampleAmount) { mrs 200 String shipString = mDocument.getNextContentAfterRegex(T.s("ebayServer.shipping")); cyberfox 201 // Sometimes the next content might not be the shipping amount, it might be the next-next. cyberfox 202 Matcher amount = null; cyberfox 203 boolean amountFound = false; cyberfox 204 if(shipString != null) { cyberfox 205 amount = amountPat.matcher(shipString); cyberfox 206 amountFound = amount.find(); cyberfox 207 if (!amountFound) { mrs 208 shipString = mDocument.getNextContent(); cyberfox 209 amount = amountPat.matcher(shipString); cyberfox 210 if (shipString != null) amountFound = amount.find(); cyberfox 211 } cyberfox 212 } cyberfox 213 // This will result in either 'null' or the amount. cyberfox 214 if(shipString != null && amountFound) shipString = amount.group(); cyberfox 215 cyberfox 216 // Step back two contents, to check if it's 'Payment cyberfox 217 // Instructions', in which case, the shipping and handling cyberfox 218 // came from their instructions box, not the cyberfox 219 // standard-formatted data. mrs 220 String shipStringCheck = mDocument.getPrevContent(2); cyberfox 221 mrs 222 String insureString = mDocument.getNextContentAfterRegex(T.s("ebayServer.shippingInsurance")); mrs 223 String insuranceOptionalCheck = mDocument.getNextContent(); cyberfox 224 cyberfox 225 // Default to thinking it's optional if the word 'required' isn't found. cyberfox 226 // You don't want to make people think it's required if it's not. mrs 227 setInsuranceOptional(insuranceOptionalCheck == null || (insuranceOptionalCheck.toLowerCase().indexOf(T.s("ebayServer.requiredInsurance")) == -1)); cyberfox 228 cyberfox 229 if(insureString != null) { cyberfox 230 if(insureString.equals("-") || insureString.equals("--")) { cyberfox 231 insureString = null; cyberfox 232 } else { cyberfox 233 insureString = insureString.trim(); cyberfox 234 } cyberfox 235 } cyberfox 236 mrs 237 if(shipStringCheck != null && !shipStringCheck.equals(T.s("ebayServer.paymentInstructions"))) { cyberfox 238 if(shipString != null) { cyberfox 239 if(shipString.equals("-")) { cyberfox 240 shipString = null; cyberfox 241 } else { cyberfox 242 shipString = shipString.trim(); cyberfox 243 } cyberfox 244 } cyberfox 245 } else { cyberfox 246 shipString = null; cyberfox 247 } cyberfox 248 cyberfox 249 if(shipString != null) { cyberfox 250 if(shipString.equalsIgnoreCase("free")) { mrs 251 setShipping(Currency.getCurrency(sampleAmount.fullCurrencyName(), "0.0")); cyberfox 252 } else { cyberfox 253 try { mrs 254 setShipping(Currency.getCurrency(sampleAmount.fullCurrencyName(), shipString)); cyberfox 255 } catch(NumberFormatException nfe) { mrs 256 setShipping(Currency.NoValue()); cyberfox 257 } cyberfox 258 } cyberfox 259 } else { mrs 260 setShipping(Currency.NoValue()); cyberfox 261 } cyberfox 262 try { mrs 263 setInsurance(Currency.getCurrency(insureString)); cyberfox 264 } catch(NumberFormatException nfe) { mrs 265 setInsurance(Currency.NoValue()); cyberfox 266 } cyberfox 267 } cyberfox 268 mrs 269 private void loadBuyNow() { mrs 270 setBuyNow(Currency.NoValue()); mrs 271 setBuyNowUS(zeroDollars); mrs 272 mrs 273 String altBuyNowString1 = mDocument.getNextContentAfterRegexIgnoring(T.s("ebayServer.price"), "([Ii]tem.[Nn]umber|^\\s*[0-9]+\\s*$)"); mrs 274 if(altBuyNowString1 != null) { mrs 275 altBuyNowString1 = altBuyNowString1.trim(); cyberfox 276 } mrs 277 if(altBuyNowString1 != null && altBuyNowString1.length() != 0) { mrs 278 setBuyNow(Currency.getCurrency(altBuyNowString1)); mrs 279 if(getBuyNow().isNull()) { mrs 280 altBuyNowString1 = mDocument.getNextContentAfterContent("Buy It Now:"); mrs 281 setBuyNow(Currency.getCurrency(altBuyNowString1)); mrs 282 } mrs 283 setBuyNowUS(getUSCurrency(getBuyNow(), mDocument)); cyberfox 284 } cyberfox 285 } cyberfox 286 cyberfox 287 private String getEndDate(String inTitle) { cyberfox 288 String result = null; cyberfox 289 mrs 290 String dateMatch = T.s("title.end_time"); mrs 291 Pattern datePat = Pattern.compile(dateMatch); mrs 292 mrs 293 Matcher when = datePat.matcher(inTitle); mrs 294 if(when.find()) result = when.group(2); cyberfox 295 cyberfox 296 return result; cyberfox 297 } cyberfox 298 cyberfox 299 /** cyberfox 300 * A utility function to check the provided preferred object against an arbitrary 'bad' value, cyberfox 301 * and return the preferred object if it's not bad, and an alternative object if it the preferred cyberfox 302 * object is bad. cyberfox 303 * cyberfox 304 * @param preferred - The preferred object (to be compared against the 'bad' value) cyberfox 305 * @param alternate - The alternative object, if the first object is bad. cyberfox 306 * @param bad - The bad object to validate the preferred object against. cyberfox 307 * cyberfox 308 * @return - preferred if it's not bad, alternate if the preferred object is bad. cyberfox 309 * @noinspection ObjectEquality cyberfox 310 **/ cyberfox 311 private Object ensureSafeValue(Object preferred, Object alternate, Currency bad) { cyberfox 312 return (preferred == bad)?alternate:preferred; cyberfox 313 } cyberfox 314 cyberfox 315 private String getResult(JHTML doc, String regex, int match) { cyberfox 316 String rval = doc.grep(regex); cyberfox 317 if(rval != null) { cyberfox 318 if(match == 0) return rval; cyberfox 319 Pattern searcher = Pattern.compile(regex); cyberfox 320 Matcher matcher = searcher.matcher(rval); cyberfox 321 if(matcher.matches()) return matcher.group(match); cyberfox 322 } cyberfox 323 cyberfox 324 return null; cyberfox 325 } cyberfox 326 mrs 327 private void loadOptionalInformation(JHTML doc) { cyberfox 328 try { mrs 329 loadFeedback(doc); cyberfox 330 mrs 331 String location = doc.getNextContentAfterRegex(T.s("ebayServer.itemLocationRegex")); cyberfox 332 if(location != null) { mrs 333 setItemLocation(StringTools.decode(location, doc.getCharset())); cyberfox 334 } cyberfox 335 mrs 336 loadPaypal(doc); cyberfox 337 } catch(Throwable t) { cyberfox 338 // I don't actually CARE about any of this data, or any errors that occur on loading it, so don't mess things up on errors. mrs 339 String msg = t.getMessage(); mrs 340 if(msg != null) { mrs 341 JConfig.log().logDebug(msg); mrs 342 } else { mrs 343 JConfig.log().handleException("A weird error occurred", t); mrs 344 } cyberfox 345 } cyberfox 346 } cyberfox 347 mrs 348 private void loadPaypal(JHTML doc) { mrs 349 String pbp = getResult(doc, T.s("ebayServer.paypalMatcherRegex"), 0); mrs 350 if(pbp != null) { mrs 351 setPaypal(true); mrs 352 } else { mrs 353 String preferred = doc.getNextContentAfterRegex("PayPal.?"); mrs 354 if(preferred != null) { mrs 355 if(preferred.indexOf("preferred") != -1) setPaypal(true); mrs 356 if(preferred.indexOf("accepted") != -1) setPaypal(true); mrs 357 } mrs 358 String methods = doc.getNextContentAfterRegex("Payment methods:?"); mrs 359 // If it's not the first payment method... mrs 360 // It might be the second. mrs 361 int i=0; mrs 362 while (i<3 && !hasPaypal()) { mrs 363 if (methods != null && methods.equalsIgnoreCase("paypal")) setPaypal(true); mrs 364 else methods = doc.getNextContent(); mrs 365 i++; mrs 366 } mrs 367 } mrs 368 } mrs 369 mrs 370 private void loadFeedback(JHTML doc) { mrs 371 String score = doc.getContentBeforeContent(T.s("ebayServer.feedback")); mrs 372 if(score != null && StringTools.isNumberOnly(score)) { mrs 373 mSeller.setFeedback(Integer.parseInt(score)); mrs 374 } mrs 375 mrs 376 String percentage = doc.getNextContentAfterContent(T.s("ebayServer.feedback")); mrs 377 if(percentage != null) mSeller.setPositivePercentage(percentage); mrs 378 } mrs 379 cyberfox 380 /** cyberfox 381 * @brief - Not brief at all; wow...this is way too long. cyberfox 382 * cyberfox 383 * @param ae - The auction entry to update or null if it's a new auction. cyberfox 384 * cyberfox 385 * @return - false if the parse failed, true if it succeeded. This needs cyberfox 386 * to be turned into a set of enums. cyberfox 387 */ mrs 388 public ParseErrors parseAuction(AuctionEntry ae) { cyberfox 389 // Verify the title (in case it's an invalid page, the site is cyberfox 390 // down for maintenance, etc). mrs 391 String prelimTitle; mrs 392 try { mrs 393 prelimTitle = checkTitle(); mrs 394 } catch (ParseException e) { mrs 395 finish(); mrs 396 return e.getError(); mrs 397 } mrs 398 mrs 399 if(prelimTitle == null) { mrs 400 finish(); mrs 401 return ParseErrors.BAD_TITLE; mrs 402 } mrs 403 mrs 404 if(getIdentifier() == null && (ae == null || ae.getIdentifier() == null)) { mrs 405 parseIdentifier(); mrs 406 } mrs 407 mrs 408 Integer quant = getNumberFromLabel(mDocument, T.s("ebayServer.quantity"), T.s("ebayServer.postTitleIgnore")); mrs 409 mrs 410 // Get the integer values (Quantity, Bidcount) mrs 411 setQuantity(quant == null ? 1 : quant); mrs 412 mrs 413 setFixedPrice(false); mrs 414 setNumBids(getBidCount(mDocument, getQuantity())); mrs 415 if(!isFixedPrice() && quant != null) setDutch(true); mrs 416 mrs 417 try { mrs 418 loadBuyNow(); mrs 419 } catch(Exception e) { mrs 420 JConfig.log().handleException("Buy It Now Loading error", e); mrs 421 } mrs 422 mrs 423 if (isFixedPrice()) { mrs 424 establishCurrentBidFixedPrice(ae); mrs 425 } else { mrs 426 Currency maxBid = establishCurrentBid(ae); mrs 427 setOutbid(mDocument.grep(T.s("ebayServer.outbid")) != null); mrs 428 setMaxBidFromServer(ae, maxBid); mrs 429 } mrs 430 mrs 431 if(getMinBid() == null && getBuyNow() != null && !getBuyNow().isNull()) { mrs 432 setMinBid(getBuyNow()); mrs 433 } mrs 434 try { mrs 435 Currency sample = getCurBid(); mrs 436 if(sample.isNull()) sample = getMinBid(); mrs 437 loadShippingInsurance(sample); mrs 438 } catch(Exception e) { mrs 439 JConfig.log().handleException("Shipping / Insurance Loading Failed", e); mrs 440 } mrs 441 mrs 442 if (checkSeller(ae)) return ParseErrors.SELLER_AWAY; mrs 443 mrs 444 checkDates(prelimTitle, ae); mrs 445 checkHighBidder(); mrs 446 checkReserve(); mrs 447 checkPrivate(); mrs 448 mrs 449 loadOptionalInformation(mDocument); mrs 450 checkThumbnail(); mrs 451 mrs 452 finish(); mrs 453 this.saveDB(); mrs 454 return ParseErrors.SUCCESS; mrs 455 } mrs 456 mrs 457 private void parseIdentifier() { mrs 458 String itemNumberContent = mDocument.grep(T.s("ebayServer.itemNumber")); mrs 459 if(itemNumberContent != null) { mrs 460 Pattern p = Pattern.compile(T.s("ebayServer.itemNumber")); mrs 461 Matcher m = p.matcher(itemNumberContent); mrs 462 if (m.matches()) { mrs 463 setIdentifier(m.group(1)); mrs 464 } mrs 465 } else { mrs 466 itemNumberContent = mDocument.getNextContentAfterRegex("(?i).*item.number.*"); mrs 467 if(itemNumberContent != null && StringTools.isNumberOnly(itemNumberContent)) { mrs 468 setIdentifier(itemNumberContent); mrs 469 } mrs 470 } mrs 471 } mrs 472 mrs 473 private static class ParseException extends Exception { mrs 474 private ParseErrors mError; mrs 475 public ParseException(ParseErrors error) { mrs 476 mError = error; mrs 477 } mrs 478 mrs 479 public ParseErrors getError() { mrs 480 return mError; mrs 481 } mrs 482 } mrs 483 mrs 484 /** mrs 485 * Sets title, and possibly end. mrs 486 * mrs 487 * @return - The preliminary extraction of the title, in its entirety, for later parsing. null if a failure occurred. mrs 488 * @throws com.jbidwatcher.auction.server.ebay.ebayAuction.ParseException - An exception that describes what's wrong with the title. mrs 489 */ mrs 490 private String checkTitle() throws ParseException { mrs 491 String prelimTitle = mDocument.getTitle(); cyberfox 492 if( prelimTitle == null) { mrs 493 prelimTitle = T.s("ebayServer.unavailable"); cyberfox 494 } mrs 495 if(prelimTitle.equals(T.s("ebayServer.adultPageTitle")) || prelimTitle.indexOf("Terms of Use") != -1) { mrs 496 throw new ParseException(ParseErrors.NOT_ADULT); cyberfox 497 } cyberfox 498 mrs 499 if(prelimTitle.equals(T.s("ebayServer.invalidItem"))) { mrs 500 String realListing = mDocument.getLinkForContent(T.s("view.original.listing")); mrs 501 if(realListing != null) { mrs 502 setURL(realListing); mrs 503 throw new ParseException(ParseErrors.WRONG_SITE); mrs 504 } mrs 505 throw new ParseException(ParseErrors.DELETED); mrs 506 } mrs 507 mrs 508 if(prelimTitle.equals("Security Measure")) { mrs 509 throw new ParseException(ParseErrors.CAPTCHA); mrs 510 } mrs 511 cyberfox 512 // Is this a valid eBay item page? mrs 513 if(prelimTitle != null && !checkValidTitle(prelimTitle)) { mrs 514 handleBadTitle(prelimTitle); mrs 515 if(getURL() != null) { mrs 516 throw new ParseException(ParseErrors.WRONG_SITE); mrs 517 } else { mrs 518 throw new ParseException(ParseErrors.BAD_TITLE); mrs 519 } cyberfox 520 } cyberfox 521 mrs 522 if(prelimTitle != null) { mrs 523 // If we got a valid title, mark the link as up, because it worked... mrs 524 MQFactory.getConcrete("Swing").enqueue("LINK UP"); cyberfox 525 mrs 526 boolean ebayMotors = false; mrs 527 if(prelimTitle.matches(T.s("ebayServer.ebayMotorsTitle"))) ebayMotors = true; mrs 528 // This is mostly a hope, not a guarantee, as eBay might start mrs 529 // cross-advertising eBay Motors in their normal pages, or mrs 530 // something. mrs 531 if(doesLabelExist(T.s("ebayServer.ebayMotorsTitle"))) ebayMotors = true; cyberfox 532 mrs 533 setEnd(null); mrs 534 setTitle(null); cyberfox 535 mrs 536 // This sucks. They changed to: eBay: {title} (item # end time {endtime}) mrs 537 if(prelimTitle.matches(T.s("ebayServer.titleEbay2")) || mrs 538 prelimTitle.matches(T.s("ebayServer.titleMotors2"))) { mrs 539 // Handle the new titles. mrs 540 Pattern newTitlePat = Pattern.compile(T.s("ebayServer.titleMatch")); mrs 541 Matcher newTitleMatch = newTitlePat.matcher(prelimTitle); mrs 542 if (newTitleMatch.find()) { mrs 543 setTitle(StringTools.decode(newTitleMatch.group(1), mDocument.getCharset())); mrs 544 String endDate = newTitleMatch.group(4); mrs 545 if(getEnd() != null) setEnd(StringTools.figureDate(endDate, T.s("ebayServer.dateFormat")).getDate()); mrs 546 } cyberfox 547 } cyberfox 548 mrs 549 if(getTitle() == null) { mrs 550 boolean htmlTitle = false; mrs 551 // The first element after the title is always the description. Unfortunately, it's in HTML-encoded format, mrs 552 // so there are <'s, and such. While I could translate that, that's something I can wait on. -- HACKHACK mrs 553 // title = (String)contentFields.get(1); mrs 554 // For now, just load from the title, everything after ') - '. mrs 555 int titleIndex = prelimTitle.indexOf(") - "); mrs 556 if(titleIndex == -1) { mrs 557 titleIndex = prelimTitle.indexOf(") -"); mrs 558 // This is an HTML title... Suck. mrs 559 htmlTitle = true; mrs 560 } cyberfox 561 mrs 562 // Always convert, at this point, from iso-8859-1 (iso latin-1) to UTF-8. mrs 563 if(htmlTitle) { mrs 564 setTitle(StringTools.decode(buildTitle(mDocument), mDocument.getCharset())); mrs 565 } else { mrs 566 setTitle(StringTools.decode(prelimTitle.substring(titleIndex+4).trim(), mDocument.getCharset())); mrs 567 } cyberfox 568 } cyberfox 569 mrs 570 if(getTitle().length() == 0) setTitle("(bad title)"); mrs 571 setTitle(JHTML.deAmpersand(getTitle())); cyberfox 572 mrs 573 // eBay Motors titles are really a combination of the make/model, mrs 574 // and the user's own text. Under BIBO, the user's own text is mrs 575 // below the 'description' fold. For now, we don't get the user mrs 576 // text. mrs 577 if(ebayMotors) { mrs 578 extractMotorsTitle(); mrs 579 } cyberfox 580 } mrs 581 return prelimTitle; mrs 582 } cyberfox 583 mrs 584 /** mrs 585 * Sets start and end. mrs 586 * mrs 587 * @param prelimTitle - The preliminary title block, because sometimes it has date information in it. mrs 588 * @param ae - The old auction, in case we need to fall back because we can't figure out the ending date. mrs 589 */ mrs 590 private void checkDates(String prelimTitle, AuctionEntry ae) { mrs 591 setStart(StringTools.figureDate(mDocument.getNextContentAfterRegexIgnoring(T.s("ebayServer.startTime"), T.s("ebayServer.postTitleIgnore")), T.s("ebayServer.dateFormat")).getDate()); mrs 592 if (getStart() == null) { mrs 593 setStart(StringTools.figureDate(mStartComment, T.s("ebayServer.dateFormat")).getDate()); cyberfox 594 } mrs 595 setStart((Date) ensureSafeValue(getStart(), ae != null ? ae.getStartDate() : null, null)); cyberfox 596 mrs 597 if (getEnd() == null) { cyberfox 598 String endDate = getEndDate(prelimTitle); mrs 599 setEnd(StringTools.figureDate(endDate, T.s("ebayServer.dateFormat")).getDate()); cyberfox 600 } cyberfox 601 mrs 602 // Handle odd case... mrs 603 if (getEnd() == null) { mrs 604 setEnd(StringTools.figureDate(mDocument.getNextContentAfterRegex(T.s("ebayServer.endsPrequel")), T.s("ebayServer.dateFormat")).getDate()); mrs 605 if (getEnd() == null) { mrs 606 String postContent = mDocument.getNextContent().replaceAll("[()]", ""); mrs 607 setEnd(StringTools.figureDate(postContent, T.s("ebayServer.dateFormat")).getDate()); cyberfox 608 } cyberfox 609 } cyberfox 610 mrs 611 setEnd((Date) ensureSafeValue(getEnd(), ae != null ? ae.getEndDate() : null, null)); mrs 612 if(getEnd() != null) { mrs 613 if (getEnd().getTime() > System.currentTimeMillis()) { mrs 614 // Item is not ended yet. mrs 615 if (ae != null) { mrs 616 ae.setComplete(false); mrs 617 ae.setSticky(false); mrs 618 } mrs 619 } mrs 620 } else { mrs 621 if(ae != null) setEnd(ae.getEndDate()); mrs 622 if(mDocument.grep(T.s("ebayServer.ended")) != null) { mrs 623 if(ae != null) ae.setComplete(true); mrs 624 setEnd(new Date()); mrs 625 } else { mrs 626 if(isFixedPrice()) { mrs 627 String durationRaw = mDocument.getNextContentAfterContent("Duration:"); mrs 628 if(durationRaw != null) { mrs 629 String duration = durationRaw.replaceAll("[^0-9]", ""); mrs 630 long days = Long.parseLong(duration); mrs 631 if(getStart() != null && !getStart().equals(Constants.LONG_AGO)) { mrs 632 long endTime = getStart().getTime() + Constants.ONE_DAY * days; mrs 633 setEnd(new Date(endTime)); mrs 634 } else { mrs 635 setEnd(Constants.FAR_FUTURE); mrs 636 } mrs 637 } else { mrs 638 JConfig.log().logMessage("Setting auction #" + getIdentifier() + " to be a 'Far Future' listing, as it has no date info."); mrs 639 setEnd(Constants.FAR_FUTURE); mrs 640 } mrs 641 } mrs 642 } cyberfox 643 } cyberfox 644 mrs 645 if (getStart() == null) setStart(Constants.LONG_AGO); mrs 646 if (getEnd() == null) setEnd(Constants.FAR_FUTURE); mrs 647 } cyberfox 648 mrs 649 /** mrs 650 * Sets the user's max bid, based on what eBay thinks it is, to catch out-of-JBidwatcher bidding. mrs 651 * mrs 652 * @param ae - The auction entry, so we can set the max bid value. mrs 653 * @param maxBid - The max bid extracted from eBay. mrs 654 */ mrs 655 private void setMaxBidFromServer(AuctionEntry ae, Currency maxBid) { mrs 656 // This is dangerously intimate with the AuctionEntry class, mrs 657 // and it won't work the first time, since the first time ae mrs 658 // is null. mrs 659 if(ae != null && !maxBid.isNull()) { mrs 660 try { mrs 661 if(!ae.isBidOn() || ae.getBid().less(maxBid)) ae.setBid(maxBid); mrs 662 } catch(Currency.CurrencyTypeException cte) { mrs 663 JConfig.log().handleException("eBay says my max bid is a different type of currency than I have stored!", cte); cyberfox 664 } mrs 665 } mrs 666 } cyberfox 667 mrs 668 private Currency establishCurrentBid(AuctionEntry ae) { mrs 669 // The set of tags that indicate the current/starting/lowest/winning mrs 670 // bid are 'Current bid', 'Starting bid', 'Lowest bid', mrs 671 // 'Winning bid' so far. mrs 672 String cvtCur = mDocument.getNextContentAfterRegex(T.s("ebayServer.currentBid")); mrs 673 setCurBid(Currency.getCurrency(cvtCur)); mrs 674 setUSCur(getUSCurrency(getCurBid(), mDocument)); cyberfox 675 mrs 676 if(getCurBid() == null || getCurBid().isNull()) { mrs 677 if(getQuantity() > 1) { mrs 678 setCurBid(Currency.getCurrency(mDocument.getNextContentAfterContent(T.s("ebayServer.lowestBid")))); mrs 679 setUSCur(getUSCurrency(getCurBid(), mDocument)); cyberfox 680 } mrs 681 } cyberfox 682 mrs 683 setMinBid(Currency.getCurrency(mDocument.getNextContentAfterContent(T.s("ebayServer.firstBid")))); mrs 684 Currency maxBid = Currency.getCurrency(mDocument.getNextContentAfterContent(T.s("ebayServer.yourMaxBid"))); mrs 685 if(maxBid != null && maxBid.getCurrencyType() == Currency.NONE) maxBid = Currency.NoValue(); cyberfox 686 mrs 687 setMinBid((Currency)ensureSafeValue(getMinBid(), ae!=null?ae.getMinBid() :Currency.NoValue(), Currency.NoValue())); mrs 688 setCurBid((Currency)ensureSafeValue(getCurBid(), ae!=null?ae.getCurBid() :Currency.NoValue(), Currency.NoValue())); mrs 689 setUSCur((Currency)ensureSafeValue(getUSCur(), ae!=null?ae.getUSCurBid():zeroDollars, Currency.NoValue())); cyberfox 690 mrs 691 if(getNumBids() == 0 && (getMinBid() == null || getMinBid().isNull())) setMinBid(getCurBid()); cyberfox 692 mrs 693 if(getMinBid() == null || getMinBid().isNull()) { mrs 694 String original = mDocument.grep(T.s("ebayServer.originalBid")); mrs 695 if(original != null) { mrs 696 Pattern bidPat = Pattern.compile(T.s("ebayServer.originalBid")); mrs 697 Matcher bidMatch = bidPat.matcher(original); mrs 698 if (bidMatch.find()) { mrs 699 setMinBid(Currency.getCurrency(bidMatch.group(1))); cyberfox 700 } cyberfox 701 } cyberfox 702 } mrs 703 return maxBid; mrs 704 } cyberfox 705 mrs 706 private void establishCurrentBidFixedPrice(AuctionEntry ae) { mrs 707 if(getBuyNow() != null && !getBuyNow().isNull()) { mrs 708 setMinBid(getBuyNow()); mrs 709 setCurBid(getBuyNow()); mrs 710 setUSCur(getBuyNowUS()); mrs 711 } else { mrs 712 // The set of tags that indicate the current/starting/lowest/winning mrs 713 // bid are 'Starts at', 'Current bid', 'Starting bid', 'Lowest bid', mrs 714 // 'Winning bid' so far. 'Starts at' is mainly for live auctions! mrs 715 String cvtCur = mDocument.getNextContentAfterRegex(T.s("ebayServer.currentBid")); mrs 716 setCurBid(Currency.getCurrency(cvtCur)); mrs 717 setUSCur(getUSCurrency(getCurBid(), mDocument)); mrs 718 mrs 719 setCurBid((Currency)ensureSafeValue(getCurBid(), ae!=null?ae.getCurBid() :Currency.NoValue(), Currency.NoValue())); mrs 720 setUSCur((Currency)ensureSafeValue(getUSCur(), ae!=null?ae.getUSCurBid():zeroDollars, Currency.NoValue())); cyberfox 721 } mrs 722 } cyberfox 723 mrs 724 private boolean checkSeller(AuctionEntry ae) { mrs 725 String sellerName = mDocument.getNextContentAfterRegex(T.s("ebayServer.seller")); mrs 726 if(sellerName == null) { mrs 727 sellerName = mDocument.getNextContentAfterRegex(T.s("ebayServer.sellerInfoPrequel")); cyberfox 728 } mrs 729 mrs 730 if(sellerName == null) { mrs 731 if(mDocument.grep(T.s("ebayServer.sellerAwayRegex")) != null) { cyberfox 732 if(ae != null) { cyberfox 733 ae.setLastStatus("Seller away - item unavailable."); cyberfox 734 } cyberfox 735 finish(); mrs 736 return true; cyberfox 737 } else { mrs 738 if(ae == null) mrs 739 sellerName = "(unknown)"; mrs 740 else mrs 741 sellerName = ae.getSeller(); cyberfox 742 } cyberfox 743 } mrs 744 setSellerName(sellerName); cyberfox 745 mrs 746 return false; mrs 747 } mrs 748 mrs 749 private void checkThumbnail() { mrs 750 try { mrs 751 if(JConfig.queryConfiguration("show.images", "true").equals("true")) { mrs 752 if(!hasNoThumbnail() && !hasThumbnail()) { mrs 753 ((PlainMessageQueue)MQFactory.getConcrete("thumbnail")).enqueueObject(this); mrs 754 } cyberfox 755 } mrs 756 } catch(Exception e) { mrs 757 JConfig.log().handleException("Error handling thumbnail loading", e); cyberfox 758 } mrs 759 } mrs 760 mrs 761 private void checkPrivate() { mrs 762 if(getHighBidder().indexOf(T.s("ebayServer.keptPrivate")) != -1) { //$NON-NLS-1$ mrs 763 setPrivate(true); mrs 764 setHighBidder("(private)"); //$NON-NLS-1$ mrs 765 } mrs 766 } mrs 767 mrs 768 private void checkReserve() { mrs 769 if(doesLabelExist(T.s("ebayServer.reserveNotMet1")) || //$NON-NLS-1$ mrs 770 doesLabelExist(T.s("ebayServer.reserveNotMet2"))) { //$NON-NLS-1$ mrs 771 setReserve(true); mrs 772 setReserveMet(false); mrs 773 } else { mrs 774 if(doesLabelExist(T.s("ebayServer.reserveMet1")) || //$NON-NLS-1$ mrs 775 doesLabelExist(T.s("ebayServer.reserveMet2"))) { //$NON-NLS-1$ mrs 776 setReserve(true); mrs 777 setReserveMet(true); mrs 778 } mrs 779 } mrs 780 } mrs 781 mrs 782 private void checkHighBidder() { mrs 783 String bidder = null; mrs 784 String email = null; mrs 785 mrs 786 if(mDocument.grep("This is a private listing.*") != null) { mrs 787 bidder = "(private)"; mrs 788 setPrivate(true); mrs 789 } else { mrs 790 if (isFixedPrice()) { mrs 791 bidder = mDocument.getNextContentAfterRegex(T.s("ebayServer.buyer")); mrs 792 if (bidder != null) { mrs 793 setNumBids(1); mrs 794 bidder = bidder.trim(); mrs 795 email = findHighBidderEmail(bidder); cyberfox 796 } cyberfox 797 } else { mrs 798 if (getQuantity() > 1 || isDutch()) { mrs 799 setDutch(true); mrs 800 bidder = "(dutch)"; mrs 801 } else { mrs 802 if (getNumBids() != 0) { mrs 803 bidder = mDocument.getNextContentAfterRegex(T.s("ebayServer.highBidder")); mrs 804 if (bidder != null) { mrs 805 bidder = bidder.trim(); mrs 806 email = findHighBidderEmail(bidder); mrs 807 } else { mrs 808 bidder = "(unknown)"; // ...but present. cyberfox 809 } cyberfox 810 } cyberfox 811 } cyberfox 812 } cyberfox 813 } mrs 814 mrs 815 setHighBidder(bidder == null ? "" : bidder); mrs 816 setHighBidderEmail(email == null ? "(unknown)" : email); cyberfox 817 } cyberfox 818 mrs 819 private String findHighBidderEmail(String bidder) { mrs 820 String email = null; mrs 821 mrs 822 if(bidder != null) { mrs 823 email = mDocument.getNextContentAfterContent(bidder, true, false); mrs 824 if (email != null) { mrs 825 email = email.trim(); mrs 826 if (email.charAt(0) == '(' && email.charAt(email.length() - 1) == ')' && email.indexOf('@') != -1) { mrs 827 email = (email.substring(1, email.length() - 1)); mrs 828 } mrs 829 if (email.equals("(")) email = null; mrs 830 } mrs 831 } mrs 832 mrs 833 return email; mrs 834 } mrs 835 cyberfox 836 private int getBidCount(JHTML doc, int quantity) { mrs 837 String rawBidCount = doc.getNextContentAfterRegex(T.s("ebayServer.bidCount")); mrs 838 if (rawBidCount == null) { mrs 839 rawBidCount = doc.getContentBeforeContent("See history"); mrs 840 if(rawBidCount != null && rawBidCount.matches("^(Purchased|Bid).*")) { mrs 841 if (rawBidCount.matches("^Purchased.*")) setFixedPrice(true); mrs 842 rawBidCount = doc.getPrevContent(); mrs 843 } mrs 844 if(rawBidCount != null && !StringTools.isNumberOnly(rawBidCount)) rawBidCount = null; mrs 845 } mrs 846 cyberfox 847 int bidCount = 0; cyberfox 848 if(rawBidCount != null) { mrs 849 if(rawBidCount.equals(T.s("ebayServer.purchasesBidCount")) || mrs 850 rawBidCount.matches(T.s("ebayServer.offerRecognition"))) { mrs 851 setFixedPrice(true); cyberfox 852 bidCount = -1; cyberfox 853 } else { mrs 854 if(rawBidCount.matches(T.s("ebayServer.bidderListCount"))) { mrs 855 bidCount = Integer.parseInt(mBidCountScript); mrs 856 mBidCountScript = null; cyberfox 857 } else { cyberfox 858 bidCount = getDigits(rawBidCount); cyberfox 859 } cyberfox 860 } cyberfox 861 } cyberfox 862 cyberfox 863 // If we can't match any digits in the bidcount, or there is no match for the eBayBidCount regex, then cyberfox 864 // this is a store or FP item. Still true under BIBO? mrs 865 if (rawBidCount == null || bidCount == -1) { mrs 866 setHighBidder(T.s("ebayServer.fixedPrice")); mrs 867 setFixedPrice(true); cyberfox 868 mrs 869 if (doesLabelExist(T.s("ebayServer.hasBeenPurchased")) || mrs 870 doesLabelPrefixExist(T.s("ebayServer.endedEarly"))) { cyberfox 871 bidCount = quantity; mrs 872 Date now = new Date(); mrs 873 setEnd(now); mrs 874 if(getStart() == null) setStart(now); cyberfox 875 } else { cyberfox 876 bidCount = 0; cyberfox 877 } cyberfox 878 } cyberfox 879 cyberfox 880 return bidCount; cyberfox 881 } cyberfox 882 mrs 883 public String getThumbnailURL() { mrs 884 if(potentialThumbnail != null) return potentialThumbnail; mrs 885 return getThumbnailById(getIdentifier()); cyberfox 886 } cyberfox 887 mrs 888 public String getAlternateSiteThumbnail() { cyberfox 889 return getThumbnailById(getIdentifier() + "6464"); cyberfox 890 } cyberfox 891 mrs 892 private String getThumbnailById(String id) { mrs 893 return "http://thumbs.ebaystatic.com/pict/" + id + ".jpg"; cyberfox 894 } cyberfox 895 mrs 896 private Integer getNumberFromLabel(JHTML doc, String label, String ignore) { cyberfox 897 String rawQuantity; cyberfox 898 if(ignore == null) { cyberfox 899 rawQuantity = doc.getNextContentAfterRegex(label); cyberfox 900 } else { cyberfox 901 rawQuantity = doc.getNextContentAfterRegexIgnoring(label, ignore); cyberfox 902 } mrs 903 Integer quant2; cyberfox 904 if(rawQuantity != null) { cyberfox 905 quant2 = getDigits(rawQuantity); cyberfox 906 } else { mrs 907 quant2 = null; cyberfox 908 } cyberfox 909 return quant2; cyberfox 910 } cyberfox 911 cyberfox 912 private void extractMotorsTitle() { mrs 913 String motorsTitle = mDocument.getContentBeforeContent(T.s("ebayServer.itemNum")); cyberfox 914 if(motorsTitle != null) { cyberfox 915 motorsTitle = motorsTitle.trim(); cyberfox 916 } mrs 917 if(motorsTitle != null && motorsTitle.length() != 0 && !getTitle().equals(motorsTitle)) { cyberfox 918 if(motorsTitle.length() != 1 || motorsTitle.charAt(0) < HIGH_BIT_SET) { mrs 919 if(getTitle().length() == 0) { mrs 920 setTitle(StringTools.decodeLatin(motorsTitle)); cyberfox 921 } else { mrs 922 setTitle(StringTools.decodeLatin(motorsTitle + " (" + getTitle() + ')')); cyberfox 923 } cyberfox 924 } cyberfox 925 } cyberfox 926 } cyberfox 927 }
Check out the code: svn co jbidwatcher/trunk/src/com/jbidwatcher/auction/server/ebay/ebayAuction.java
