#!/usr/bin/python # pywoot version 0.0.2 created by Alex Brown # based off of, and ideas taken from, and idea gotten from: wooty, a perl script that does the like, but without the awsome s**t I added (http://freshmeat.net/projects/wooty/?branch_id=65979) # 11/23/06 00:05 EDT -0400 (One month to my birthday!) # Made by Alex Brown ("uh oh spaghetti-o") # Licensed under the GPL version 2.0 # TODO: # %* Finish adding timeouts # ^* Fix for how many left: 85% : $69.99 : Logitech MX5000 Bluetooth Keyboard and Laser Mouse # + then add like status = "Highish (85%)" # + 1% : $69.99 : Logitech MX5000 Bluetooth Keyboard and Laser Mouse: SOLD OUT # * Control if summary = "Server too busy" # * Add egg.trayicon interface # * Add gnome panel interface # * Update comments heftly # * Create preferences dialog/window # * Move useful functions to pywoot.py as a library, and have woot-notify as just the app __program__ = "PyWoot" __version__ = "0.0.2" __filecall__ = "pywoot.py" __author__ = "Alex Brown (\"uh oh spaghetti-o\") " __contributors__ = ["mikm (#ubuntu on irc.freenode.net)", "John Ehresman "] __usage__ = __filecall__ + " [OPTIONS]\n" + __program__ + " is mainly used as a module to retrieve" __doc__ = __program__ + ", by " + __author__ + ", Version " + __version__ + " is a python module to retrieve information on the current woot at http://www.woot.com" __copyright__ = __program__ + " - A python module to retrieve information on the current woot at http://www.woot.com/\nCopyright (C) 2006 Alex Brown" __license__ = """ PyWoot - A python module to retrieve information on the current woot at http://www.woot.com/ Copyright (C) 2006 Alex Brown This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ def debug(*arg): """If debug is turned on, then print debug messages""" global deb; if deb == True: a = "" for i in arg: a += str(i) print a import sys, os, urllib, time, re, socket socket.setdefaulttimeout(5.0) from optparse import OptionParser item = "" status = "" price = "" tempdir = "" ifnotify = False notifyimported = False deb = False ifegg = False def rmfolderslash(arg): """ def rmfolderslash(arg) ---------------------- arg - string to remove trailing '/' off of Used when a user is unsure if a folder name such as /tmp/ or /tmp is going to be passed, therefore if arg is /tmp/ it will take the trailing slash off. Example: directory = '/tmp/' urllib.urlretrieve("http://www.gnu.org/licenses/gpl.txt", rmfolderslash(directory) + '/gpl.txt') """ # Check to see if the foldername ends with a '/' if so, remove it if arg[-1:] == '/': arg = arg[:-1] return arg def getwoot(url = "http://www.woot.com/DefaultMicrosummary.ashx", global_bool = True): """ def getwoot(url = "http://www.woot.com/DefaultMicrosummary.ashx", global_bool = True) ------------------------------------------------------------------------------------- url - string containing the url for the microsummary of what's on http://www.woot.com/ global_bool - a boolean variable that tells the function whether to save the information of what's on woot to global variables. Set global_bool to 'False' if using PyWoot as a module. """ # See if the results of this function should be stored in a global variable. if global_bool == True: global price, item, status # Try and open the Woot Microsummary for parsing for i in range(3): try: wootfile = open(urllib.urlretrieve(url)[0], 'r') except IOError: if i < 2: print "**woot-notify: summary download timed-out -- retrying... ", (i + 1) continue else: sys.exit("Error: Third retry failed -- unable to retrieve information from " + url) break #except: this was declared stupid # sys.exit("Unhandled error in downloading summary!") # break else: pass #debug("**woot-notify: Summary downloaded") # Split the summary by ':', essentially, this should return [price, item, status], unless the item is in stock wootlines = wootfile.read().split(':') j = 0 for i in wootlines: wootlines[j] = wootlines[j].strip() j += 1 # If the item is in stock, the status won't show up, so we need a failsafe for this: if len(wootlines) == 2: # If we only get two returns, the price and item, set the status to In Stock (price, item) = wootlines status = "In Stock" elif len(wootlines) == 3: # Otherwise, we'll get the status (price, item, status) = wootlines elif len(wootlines) == 4: (poop, price, item, status) = wootlines else: # I don't freakin' know how, but just incase of something: sys.exit("Unhandled number of items in the variablewootlines.\nContact the author of this script if this always occurs.\nSee __author__") if price.endswith("%"): percent_num = int(price[:-1]) real_item = status if percent_num >= 90: status = "In Stock (" + str(percent_num) + "%)" else: status = str(percent_num) + "% In Stock" price = item item = real_item # Finaly print debug output of the variables, and return them. # debug("Current Woot Info: ", item, " is ", status.lower(), " and on sale for ", price, ".") Not needed, maybe I want the line again later debug("price: ", price, "\nitem: ", item, "\nstatus: ", status) return price, item, status def main(*args): """Main Function, really f**ked up s**t happens here""" # Try to import pygtk: try: global pygtk; import pygtk pygtk.require("2.0") except ImportError: sys.exit("Error importing module: pygtk") try: global gtk; import gtk global gobject; import gobject except ImportError: sys.exit("Error importing modules: gtk, gobject") version = gtk.pygtk_version # See what sort of system tray module is going to be used: global egg if typeoftray == "gtk" and version[0] == 2 and version[1] < 10: debug("Going to try and use egg.trayicon, pygtk version is not at least 2.10") try: import egg.trayicon except ImportError: sys.exit("Tried importing egg.trayicon since your pygtk version was less than 2.10:\n" + "Error importing module: egg.trayicon") else: print "**woot-notify: Using egg.trayicon for the system tray icon" ifegg = True elif typeoftray == "egg": try: import egg.trayicon except ImportError: debug("**woot-notify: Couldn't import egg.trayicon, trying for gtk.StatusIcon") if version[0] == 2 and version[1] < 10: sys.exit("egg.trayicon could not be found, and could not" + "Use gtk.StatusIcon since your pygtk version is less than 2.10\n" + "Error import module: egg.trayicon") else: print "**woot-notify: Using egg.trayicon for the system tray icon" ifegg = True else: print "**woot-notify: Using gtk.StatusIcon for the system tray icon" debug("**woot-notify: (reached else statement)") # See if python-notify is going to be used for pop-ups if ifnotify == True: global notifyimported try: global pynotify import pynotify except ImportError: print ("Error importing module: pynotify\n" + "If not installed or distributed with this program, you can obtain it from:\n" + "\t http://www.galago-project.org/") notifyimported = False else: print "**woot-notify: Using python-notify for pop-up notifications" notifyimported = True wootapp = Woot() wootapp.main() class Woot: """Class for the Application""" def __init__(self): """Initialization of the PyWoot app""" global price, item, status, typeoftray # Declare the sys. tray icon TrayIcon = gtk.StatusIcon() # Create the menu: Menu = gtk.Menu() # Menu Items for menu: Menu_Visit = gtk.Action('Visit', 'Visit Woot', None, 'gtk-home').create_menu_item() Menu_Refresh = gtk.Action('Refresh', 'Refresh', None, 'gtk-refresh').create_menu_item() Menu_Prefs = gtk.Action('Preferences', 'Preferences', None, 'gtk-preferences').create_menu_item() Menu_Separator = gtk.SeparatorMenuItem() Menu_Quit = gtk.Action('Quit', 'Quit', None, 'gtk-quit').create_menu_item() # Order of items on menu: Menu.append(Menu_Visit) Menu.append(Menu_Refresh) Menu.append(Menu_Prefs) Menu.append(Menu_Separator) Menu.append(Menu_Quit) # Events for when buttons are clicked: Menu_Quit.connect("activate", self.destroy) Menu_Refresh.connect("activate", self.callback_refresh, TrayIcon) Menu_Visit.connect("activate", self.callback_visit) Menu_Visit.show(); Menu_Refresh.show(); Menu_Separator.show(); Menu_Quit.show() Menu.show() # Configure the sys. tray icon self.refresh(TrayIcon, 'first check') TrayIcon.set_from_file(self.get_icon()) gtk.status_icon_position_menu(Menu, TrayIcon) # Connect the icon with the menu TrayIcon.connect("popup-menu", self.menu_popup, Menu) TrayIcon.connect("activate", self.tray_clicked) # Every 5 min, update the widget gobject.timeout_add(5 * 60 * 1000, self.refresh, TrayIcon) self.tray_clicked(None) def callback_visit(self, widget, data=None): """Visit callback for Menu_Visit""" self.open_woot() def callback_refresh(self, widget, data=None): """Refresh callback for Menu_Refresh""" self.refresh(data) def callback_clicked(self, widget, data=None): """Click callback for testing""" print "Click!" def tray_clicked(self, widget, data=None): """Callback for when the system tray icon gets clicked""" global notifyimported if notifyimported == True: self.notify(urgency='normal') return True def menu_popup(self, status_icon, button, activate_time, menu, *args): """Callback for popping up a gtk.Menu""" menu.popup(None, None, None, button, activate_time) def refresh(self, staticon, *args): """Refresh Function""" global price, item, status old_price = price; old_item = item; old_status = status (price, item, status) = getwoot() staticon.set_tooltip((item + " is " + status.lower() + " and on sale for " + price + ".")) self.get_pic() if old_item != item and args[0].lower() != 'first check': self.notify(bold=True, subject="New Item at Woot:", urgency='critical') debug("**woot-notify: First refresh") print "**woot-notify: Refreshed at " + time.ctime() return True def notify(self, bold=False, subject="Currently on Woot:", message='default', urgency='normal'): global pynotify, price, item, status if not pynotify.init("Woot"): return False if message.lower() == 'default': message = 'Item: ' + item + "\nPrice: " + price + "\nStatus: " + status + '\nhttp://www.woot.com/' if bold == True: message = '' + message + '' n = pynotify.Notification(subject, message, self.get_pic(skipget=True, width=150)) if urgency.lower() == 'low': n.set_urgency(pynotify.URGENCY_LOW) elif urgency.lower() == 'normal': n.set_urgency(pynotify.URGENCY_NORMAL) elif urgency.lower() == 'critical' or urgency.lower() == 'high': n.set_urgency(pynotify.URGENCY_CRITICAL) if not n.show(): print "Failed to send notification" return False return True def open_woot(self, data=None): os.system("firefox http://www.woot.com/") def destroy(self, widget, data=None): print "**woot-notify: recieved message to quit" self.clean() gtk.main_quit() def clean(self): self.get_icon(deletefile=True) print "**woot-notify: deleted icon file for woot-notify" self.get_pic(deletefile=True) print "**woot-notify: deleted picture file for woot-notify" def main(self): gtk.main() def get_pic(self, dirarg="/tmp", deletefile=False, skipget=False, width=None, height=None): global tempdir, re if len(tempdir) > 0: dirarg = tempdir dirarg = rmfolderslash(dirarg) piclocation = dirarg + "/pywootpic.jpg" modpiclocation = dirarg + "/modwootpic.jpg" if deletefile == True: try: os.remove(piclocation) except: pass try: os.remove(modpiclocation) except: pass return elif skipget == True: pass else: try: webpage = open(urllib.urlretrieve("http://www.woot.com/Default.aspx")[0], 'r') except: #self.clean() #sys.exit('**woot-notify: Unable to retrieve homepage of www.woot.com') print '**woot-notify: Unable to retrieve homepage of www.woot.com' code = webpage.read() picregex = re.search(r'', code, re.I) try: urllib.urlretrieve(picregex.groups()[0], piclocation) except: #self.clean() #sys.exit("Error: Unable to retrieve picture of the current Woot") print '**woot-notify: Unable to retrieve picture of the current Woot' return '' if width == None and height == None: return piclocation elif (width != None and height == None) or (width == None and height != None) or (width != None and height != None): modpic = gtk.gdk.pixbuf_new_from_file(piclocation) pic_width = modpic.get_width() pic_height = modpic.get_height() pic_ratio = pic_width/pic_height if width != None and height == None: # Change width of image new_width = width new_height = width * pic_ratio elif width == None and height != None: # Change height of image new_width = width * pic_ratio new_height = height elif width != None and height != None: # Change height and width of image new_width = width new_height = height modpic = gtk.gdk.pixbuf_new_from_file_at_size(piclocation, new_width, new_height) try: modpic.save(modpiclocation, "jpeg", {"quality":"100"}) except: print "**woot-notify: Error: Unable to save modified picture of the current Woot" return '' else: return modpiclocation else: sys.exit("Error: Unhandled width/height clause\nWidth: " + width + "\nHeight: " + height) def get_icon(self, dirarg="/tmp", deletefile=False, skipget=False, url = "http://www.woot.com/App_Themes/Woot/favicon.ico"): global tempdir if len(tempdir) > 0: dirarg = tempdir dirarg = rmfolderslash(dirarg) iconlocation = dirarg + "/pywooticon.ico" if deletefile == True: try: os.remove(iconlocation) except: pass elif skipget == True: pass else: for i in range(3): try: urllib.urlretrieve(url, iconlocation) except IOError: if i < 2: print "**woot-notify: icon download timed-out -- retrying... ", (i + 1) continue else: self.clean() sys.exit("Error: Third retry failed -- unable to retrieve Woot icon from " + url) break else: debug("**woot-notify: Icon downloaded") return iconlocation if __name__ == '__main__': parser = OptionParser(prog=__program__, usage=__usage__, version="%prog "+__version__) parser.set_defaults(deb=False, iftray="gtk", ifnotify=False, tempdir="/tmp", downgpl=None) parser.add_option("-d", "--debug", action="store_true", dest="deb", help="Print debug information.") parser.add_option("--tray-gtk", action="store_const", const='egg', dest="iftray", help="Use gtk.StatusIcon to put PyWoot in the system tray (pygtk 2.10 required)") parser.add_option("--tray-egg", action="store_const", const='egg', dest="iftray", help="Use egg.trayicon to put PyWoot in the system tray <>") parser.add_option("--notify", action="store_true", dest="ifnotify", help="Use pynotify to use popup messages on new woot icons <>") parser.add_option("--temp-dir", action="store", type="string", dest="tempdir", metavar="DIRECTORY", help="The temporary directory to use for %prog") parser.add_option("--download-license", "--download-gpl", action="store", type="string", dest="downgpl", metavar="DIRECTORY", help="Download the license to this program to designated directory, enter 'current' to use the one from which you ran the script.\nNote that this downloads the most current gpl, and the one downloaded may not be version 2 after 2006.") (options, args) = parser.parse_args() if options.downgpl != None: if options.downgpl.lower() == 'current': location = os.getcwd() else: location = options.downgpl location = rmfolderslash(location) + '/pywoot_license_gpl.txt' try: urllib.urlretrieve("http://www.gnu.org/licenses/gpl.txt", location) except IOError: print "The download of the gpl was timed-out. You are welcome to try again, or if you are on a linux system, type 'man gpl'" else: print "The license was downloaded to: ", location + '/gpl.txt' sys.exit(0) deb = options.deb typeoftray = options.iftray ifnotify = options.ifnotify tempdir = options.tempdir main() # Possible discussed alternate names for this program: # woot fucker -- my favorite, it obviously says that this program allows you to fuck woots # woot checker # wewt # python woot # woot python # wootpy # "Current Woot Notifier" # curwoot