You are not logged in.

#1 2007-12-08 22:38:12

buttons
Member
From: NJ, USA
Registered: 2007-08-04
Posts: 620

SteepANDCheap Notification Tool

Hi folks.  I don't know if any of you follow Steep And Cheap (SAC), but I'm a climber and a runner and I like paying very little for things I don't really need, so I do.  All of their notification tools are slow or windows-only, so I wrote my own.

No, I don't work for them.

sac.py

#!/usr/bin/python

"""

SAC Notification Tool v0.1
(c) 2007 Michael Seiler


"""

import urllib, htmllib, formatter
from pynotify import *
import time


class MyParser(htmllib.HTMLParser):
    """

    MyParser

        Parse incoming SAC data into a product name and price details

    """

    def start_div(self, attrs):

        if len(attrs) > 0:
            for attr in attrs:
                if attr[0] == 'id':
                    if attr[1] == 'price':
                        self.price_flag = 1 
                    elif attr[1] == 'percent_off':
                        self.price_flag = 1
                    elif attr[1] == 'product':
                        self.product_flag = 1

    def end_div(self):

        self.price_flag = 0
        self.product_flag = 0
    
    def start_h2(self, attrs):
    
        if self.product_flag:
            self.product_title_flag = 1

    def end_h2(self):

        self.product_title_flag = 0

    def handle_data(self, text):

        if self.price_flag:
            self.pricing.append(text)
        elif self.product_title_flag:
            self.product = text

    def clear_buffers(self):

        self.pricing = []
        self.product = ""

    def __init__(self):

        null_format = formatter.NullFormatter()
        htmllib.HTMLParser.__init__(self, null_format)

        self.pricing = []
        self.product = ""

        self.price_flag = 0
        self.product_flag = 0
        self.product_title_flag = 0


def notify(title, message):
    """Show notification"""

    init('sac.py')

    a = Notification(title, message)

    a.set_urgency(URGENCY_LOW)
    a.set_timeout(EXPIRES_DEFAULT)

    a.show()


if __name__ == '__main__':

    old_product = ""
    
    p = MyParser()

    while True:

        data = urllib.urlopen('http://www.steepandcheap.com').read()
        p.feed(data)
        p.close()
    
        new_product = p.product
        new_pricing =  " - ".join(p.pricing)
    
        if new_product != old_product:
            if new_product:
                notify(new_product, new_pricing)
                old_product = new_product

        p.clear_buffers()

        time.sleep(15)

This requires python-notify, which is in community.  And python, obviously.

It checks SAC for updates every 15 seconds and pops up (using libnotify) a little window with the item and the pricing details (Price - Percent Off) if it finds something new.

To run, just run sac.py and forget about it.

If for some reason you want to check slower (or, better yet, check faster), change time.sleep(15) to time.sleep(SECONDS), where SECONDS is how long you want it to wait.  The windows desktop tool checks every 30 seconds, for reference.

Hope there's someone out there that can use this!

Last edited by buttons (2007-12-08 22:39:32)


Cthulhu For President!

Offline

#2 2007-12-10 02:57:37

buttons
Member
From: NJ, USA
Registered: 2007-08-04
Posts: 620

Re: SteepANDCheap Notification Tool

Update.

Changelog:
Now adds a buy link.  And an icon, to make it look nice.
Added comments, so you can follow along at home
Made it easier to change the time between checks (it's in the constants)
Won't die if your internet connection does, or SAC goes down

#!/usr/bin/python

"""

SAC Notification Tool v0.2
(c) 2007 Michael Seiler


"""

import urllib, htmllib, formatter
from pynotify import *
import gtk, gobject


#Global Constants
TIMEOUT = EXPIRES_DEFAULT                   #Time in milliseconds for the popup to stick around
                                            #Use EXPIRE_DEFAULT for the default or EXPIRE_NEVER for forever
WEBSITE = 'http://www.steepandcheap.com'    #SAC Website
CHECK_DELAY = 15                            #Time in seconds between website checks


class MyParser(htmllib.HTMLParser):
    """

    MyParser

        Parse incoming SAC data into a product name and price details

        USAGE:  p = MyParser()
                p.feed(some_html)
                p.close()

        Methods:
            
            clearbuffers()
                Clears cached price and product data

            getproduct()
                Returns the product name

            getpricing()
                Returns the price and percent off


    """

    def start_div(self, attrs):
        """Called at the start of each DIV tag"""

        if len(attrs) > 0:
            for attr in attrs:
                if attr[0] == 'id':
                    if attr[1] == 'price':
                        self.price_flag = 1 
                    elif attr[1] == 'percent_off':
                        self.price_flag = 1
                    elif attr[1] == 'product':
                        self.product_flag = 1

    def end_div(self):
        """Called at the end of each DIV tag"""

        self.price_flag = 0
        self.product_flag = 0
    
    def start_h2(self, attrs):
        """Called at the start of each H2 tag"""
    
        if self.product_flag:
            self.product_title_flag = 1

    def end_h2(self):
        """Called at the end of each H2 tag"""

        self.product_title_flag = 0

    def handle_data(self, text):
        """Called on any text between tags"""

        if self.price_flag:
            self.pricing.append(text)
        elif self.product_title_flag:
            self.product = text

    def clear_buffers(self):
        """Clear cached price and product data"""

        self.pricing = []
        self.product = ""

    def getproduct(self):
        """Return product name"""

        return self.product

    def getpricing(self):
        """Return price and percent off"""

        return " - ".join(self.pricing)

    def __init__(self):

        null_format = formatter.NullFormatter()
        htmllib.HTMLParser.__init__(self, null_format)

        self.pricing = []
        self.product = ""

        self.price_flag = 0
        self.product_flag = 0
        self.product_title_flag = 0


class SACNotify(object):
    """

    SACNotify

        Handle notification and gtk interaction

        USAGE:  s = SACNotify()
                s.main()

        Creates a timeout that downloads information from SAC every 15s,
        then pops up a notification using pynotify if it finds a different product.


    """

    def notify(self, title, message):
        """Show notification, adding a buy link"""
    
        init('sac.py')
   
        message += "\n<a href='%s'>Buy this item</a>" % WEBSITE

        a = Notification(title, message, gtk.STOCK_DIALOG_WARNING)
    
        a.set_urgency(URGENCY_LOW)
        a.set_timeout(TIMEOUT)
    
        a.show()
    
    def read_data(self):
        """Read data from the SAC website, then call the parser"""

        try:
            self.data = urllib.urlopen(WEBSITE).read()
        except:
            #No interweb connection? SAC down?
            self.data = None

        if self.data:
            self.parser.feed(self.data)
            self.parser.close()

    def site_poll_timeout(self):
        """Timeout called every 15s, which calls read_data, then notify if the item changed"""

        self.read_data()

        if self.data:
            self.new_product = self.parser.getproduct()
            
            if self.new_product != self.old_product:
                self.new_pricing = self.parser.getpricing()
    
                if self.new_product:
                    self.notify(self.new_product, self.new_pricing)
                    self.old_product = self.new_product
    
            self.parser.clear_buffers()

        return True

    def main(self):
        """Create the timeout object and let the program sit in gtk.main()"""

        self.site_poller = gobject.timeout_add(self.seconds_to_check, self.site_poll_timeout)
        gtk.main()

    def __init__(self):

        self.parser = MyParser()

        self.old_product = ""
        self.new_product = ""
        self.new_pricing = ""

        self.data = None

        self.seconds_to_check = CHECK_DELAY * 1000

        self.site_poll_timeout()


if __name__ == '__main__':

    s = SACNotify()
    s.main()

Screenshot:
200712091934591280x1024qd4.th.png

smile

Last edited by buttons (2007-12-10 03:00:07)


Cthulhu For President!

Offline

#3 2008-05-02 18:31:07

Eric89GXL
Member
Registered: 2008-05-02
Posts: 3

Re: SteepANDCheap Notification Tool

This is great. I'm in the midst of un-windows-ing myself, and the SAC notifier was one thing I really missed. Thanks.

Do you mind if I modify this code---perhaps to display the item and add a notification-area icon---and eventually distribute it (with due credit)?

Eric

Offline

#4 2008-05-02 22:58:23

dcraven
Member
Registered: 2008-04-27
Posts: 11

Re: SteepANDCheap Notification Tool

I'm sure the website would appreciate lengthening the polling delay as well. I'd call every 15 seconds "hammering" myself.

Cheers,
~djc

Offline

#5 2008-05-20 07:21:06

buttons
Member
From: NJ, USA
Registered: 2007-08-04
Posts: 620

Re: SteepANDCheap Notification Tool

Thanks to Rob for pointing out this had borked.

SAC added an ad to their site, which confused the parser.  So...here's a new version:

NOTE: I've made other changes to this!  You *must* set the BROWSER variable at the top of the script to be the full path to your browser.  I happen to use swiftfox, so that's the default.  The reason for this is this version has significant changes to the way GTK is handled, and now has a button rather than a link.

NOTE2: This version also fixes the strange problem SAC had with coming up with old items for a split second inexplicably.

#!/usr/bin/python

"""

SAC Notification Tool v0.7
(c) 2007 Michael Seiler


"""

import urllib, htmllib, formatter
from pynotify import *
import gtk, gobject
import subprocess

#Global Constants
TIMEOUT = EXPIRES_DEFAULT                   #Time in milliseconds for the popup to stick around
                                            #Use EXPIRE_DEFAULT for the default or EXPIRE_NEVER for forever
WEBSITE = 'http://www.steepandcheap.com'    #SAC Website
CHECK_DELAY = 15                            #Time in seconds between website checks
BROWSER = '/opt/swiftfox/swiftfox'          #No symlinks!


class MyParser(htmllib.HTMLParser):
    """

    MyParser

        Parse incoming SAC data into a product name and price details

        USAGE:  p = MyParser()
                p.feed(some_html)
                p.close()

        Methods:
            
            clearbuffers()
                Clears cached price and product data

            getproduct()
                Returns the product name

            getpricing()
                Returns the price and percent off


    """

    def start_div(self, attrs):
        """Called at the start of each DIV tag"""

        if len(attrs) > 0:
            for attr in attrs:
                if attr[0] == 'id':
                    if attr[1] == 'price':
                        self.price_flag = 1 
                    elif attr[1] == 'percent_off':
                        self.price_flag = 1
                    elif attr[1] == 'product':
                        self.product_flag = 1
                    elif attr[1][:8] == 'ad_space':
                        self.ad_flag = 1

    def end_div(self):
        """Called at the end of each DIV tag"""

        self.price_flag = 0
        if not self.ad_flag:
            self.product_flag = 0
    
    def start_h2(self, attrs):
        """Called at the start of each H2 tag"""
    
        if self.product_flag:
            self.product_title_flag = 1

    def end_h2(self):
        """Called at the end of each H2 tag"""

        self.product_title_flag = 0
        self.ad_flag = 0

    def handle_data(self, text):
        """Called on any text between tags"""

        if self.price_flag:
            self.pricing.append(text)
        elif self.product_title_flag:
            self.product = text

    def clear_buffers(self):
        """Clear cached price and product data"""

        self.pricing = []
        self.product = ""

    def getproduct(self):
        """Return product name"""

        return self.product

    def getpricing(self):
        """Return price and percent off"""

        return " - ".join(self.pricing)

    def __init__(self):

        null_format = formatter.NullFormatter()
        htmllib.HTMLParser.__init__(self, null_format)

        self.pricing = []
        self.product = ""

        self.ad_flag = 0
        self.price_flag = 0
        self.product_flag = 0
        self.product_title_flag = 0


class MyNotify(object):
    """

    MyNotify

        Create and maintain the notification window and corresponding action methods

        USAGE:  n = MyNotify()
                n.notify(title, message)

    """

    def notify(self, title, message):
        """Show notification with a buy button"""
   
        self.notification.update(title, message, gtk.STOCK_DIALOG_WARNING)

        self.notification.show()
        gtk.main()

    def open_site(self, n, action):
        """Called if the user clicks the buy button"""

        subprocess.Popen([BROWSER, WEBSITE])

    def destroy(self, n, action=None):
        """Called whenever the window disappears"""

        gtk.main_quit()
    
    def __init__(self):

        init('sac.py')
   
        self.notification = Notification("Product", "Pricing", gtk.STOCK_DIALOG_WARNING)
    
        self.notification.set_urgency(URGENCY_LOW)
        self.notification.set_timeout(TIMEOUT)
  
        self.notification.connect('closed', self.destroy)

        self.notification.add_action('buy', 'Buy this item', self.open_site)    #User clicks button


class SAC_notifier(MyNotify):
    """

    SAC_notifier

        Handle timing, notification, and gtk interaction

        USAGE:  s = SAC_notifier()
                s.main()

        Creates a timeout that downloads information from SAC every CHECK_DELAY seconds,
        then pops up a notification using pynotify if it finds a different product.


    """

    def read_data(self):
        """Read data from the SAC website, then call the parser"""

        try:
            self.data = urllib.urlopen(WEBSITE).read()
        except:
            #No interweb connection? SAC down?
            self.data = None

        if self.data:
            self.parser.feed(self.data)
            self.parser.close()

    def site_poll_timeout(self):
        """Timeout called every CHECK_DELAY secs, which calls read_data, then notify if the item changed"""

        self.read_data()

        if self.data:
            self.new_product = self.parser.getproduct()
            
            if self.new_product != self.old_product and not self.new_product in self.seen_products:
                self.new_pricing = self.parser.getpricing()
    
                if self.new_product:
                    self.notify(self.new_product, self.new_pricing)
                    self.seen_products.append(self.new_product)
                    self.old_product = self.new_product
    
            self.parser.clear_buffers()

        return True

    def main(self):
        """Create the timeout object and let the program sit in gtk.main()"""

        self.site_poller = gobject.timeout_add(self.seconds_to_check, self.site_poll_timeout)
        gtk.main()

    def __init__(self):

        MyNotify.__init__(self)

        self.parser = MyParser()

        self.old_product = ""
        self.new_product = ""
        self.new_pricing = ""

        self.seen_products = list()

        self.data = None

        self.seconds_to_check = CHECK_DELAY * 1000
        
        self.site_poll_timeout()


if __name__ == '__main__':

    s = SAC_notifier()
    s.main()

Cthulhu For President!

Offline

#6 2008-05-20 16:49:15

Eric89GXL
Member
Registered: 2008-05-02
Posts: 3

Re: SteepANDCheap Notification Tool

Is there some reason you didn't use the RSS feed (http://feeds.feedburner.com/SteepandCheap) instead of parsing the HTML of the main site? Seems like it would be less prone to problems due to them changing site layout, plus you might be able to grab the product image easily.

Last edited by Eric89GXL (2008-05-20 16:55:53)

Offline

#7 2008-05-31 19:27:44

Eric89GXL
Member
Registered: 2008-05-02
Posts: 3

Re: SteepANDCheap Notification Tool

I've changed the code to use the RSS feed, pull an image of the item, and (optionally) display a tray icon so that you can close or activate the notifier as you please. Have a try:

#!/usr/bin/python

"""

SAC RSS Notification Tool v0.2
(c) 2008 Michael Seiler and Eric Larson


"""

import urllib
import Image, cStringIO
from xml.dom import minidom
from pynotify import *
import gtk, gobject
import subprocess

#Global Constants
TIMEOUT = EXPIRES_DEFAULT                   #Time in milliseconds for the popup to stick around
                                            #Use EXPIRES_DEFAULT for the default or EXPIRE_NEVER for forever
WEBSITE = 'http://www.steepandcheap.com'    #SAC Website
CHECK_DELAY = 15                            #Time in seconds between website checks
BROWSER = '/usr/lib/firefox-3.0/firefox.sh' #No symlinks!
FEEDADDRESS = 'http://feeds.feedburner.com/SteepandCheap' # SAC RSS Feed
ICONURL = 'http://images.steepandcheap.com/images/icon/steepcheap.ico' # SAC statusbar icon URL
SHOW_NOTIFICATION_ICON = True               # Show SAC icon in notification area?
RECHECK_ON_CLICK = False                    # Check the SAC deal every time the notifier is activated by clicking the icon?

def url2pixbuf(imgurl):
    img_feed = None
    
    try:
        img_feed = urllib.urlopen(imgurl).read()
    except:
        img_feed = None
    if img_feed:
        im = Image.open(cStringIO.StringIO(img_feed)).convert("RGB")
        return gtk.gdk.pixbuf_new_from_data(im.tostring(),gtk.gdk.COLORSPACE_RGB,False,8,im.size[0],im.size[1],3*im.size[0])
    else:
        return None

class MyNotify(object):
    """

    MyNotify

        Create and maintain the notification window and corresponding action methods

        USAGE:  n = MyNotify()
                n.notify(title, message)

    """

    def notify(self, title, message, imgPixbuf):
        """Show notification with a buy button and image"""
        
        self.notification.update(title, message)
        
        if imgPixbuf:
            self.notification.set_icon_from_pixbuf(imgPixbuf)
            
        self.notification.show()
        self.isvisible = True

    def icon_clicked(icon,event,self):
        if self.isvisible:
            self.notification.close()
        else:
            if RECHECK_ON_CLICK and not self.checkingnow:
                self.site_poll_timeout()
            self.notification.show()
            self.isvisible = True

    def show_menu(self, icon, button, time):
        self.menu.popup(None, None, gtk.status_icon_position_menu, button, time, icon)

    def quit_clicked(self, menuItem):
        if self.isvisible:
            self.notification.close()
        gtk.main_quit()

    def open_site(self, n, action):
        """Called if the user clicks the buy button"""

        subprocess.Popen([BROWSER, WEBSITE])

    def destroy(self, n, action=None):
        """Called whenever the window disappears"""

        self.isvisible = False
    
    def __init__(self):

        init('sacrss.py')

        self.notification = Notification("Product", "Pricing", gtk.STOCK_DIALOG_WARNING)
    
        self.notification.set_urgency(URGENCY_LOW)
        self.notification.set_timeout(TIMEOUT)
  
        self.notification.connect('closed', self.destroy)

        self.notification.add_action('buy', 'Buy this item', self.open_site)    #User clicks button


class SAC_notifier(MyNotify):
    """

    SAC_notifier

        Handle timing, notification, and gtk interaction

        USAGE:  s = SAC_notifier()
                s.main()

        Creates a timeout that downloads information from SAC every CHECK_DELAY seconds,
        then pops up a notification using pynotify if it finds a different product.


    """

    def read_data(self):
        """Read data from the SAC website, then parse it"""
        self.checkingnow = True
        self.new_product = ""
        self.description = ""
        self.imgLocation = ""
        self.priceCurrent = ""
        self.priceRegular = ""
        self.imgPixbuf = None

        try:
            self.file_feed = urllib.urlopen(FEEDADDRESS).read()
        except:
            #No interweb connection? SAC down?
            self.file_feed = None

        if self.file_feed:
            self.file_xml = minidom.parseString(self.file_feed)
            self.item_node = self.file_xml.getElementsByTagName("item")
        

            for childNode in self.item_node[0].childNodes:
                if childNode.nodeType == childNode.ELEMENT_NODE:
                    if childNode.tagName == 'title':
                        self.new_product = childNode.firstChild.data
                    if childNode.tagName == 'sac:listDescription':
                        self.description = childNode.firstChild.data
                    if childNode.tagName == 'sac:priceCurrent':
                        self.priceCurrent = childNode.firstChild.data
                    if childNode.tagName == 'sac:priceRegular':
                        self.priceRegular = childNode.firstChild.data
                    if childNode.tagName == 'sac:tinyImage':
                        self.imgLocation = childNode.firstChild.data

            self.imgPixbuf = url2pixbuf(self.imgLocation)

        self.checkingnow = False

    def site_poll_timeout(self):
        """Timeout called every CHECK_DELAY secs, which calls read_data, then notify if the item changed"""

        self.read_data()

        if self.file_feed:
            if self.new_product != self.old_product and not self.new_product in self.seen_products:
                if self.new_product:
                    percOff = '%d' % round(100.0*(float(self.priceRegular) - float(self.priceCurrent))/float(self.priceRegular))
                    descriptionString = "\n<b>Price: $" + self.priceCurrent + " (" + percOff + "% off!)</b>\nRegularly: $" + self.priceRegular + "\n\n" + self.description
                    self.notify(self.new_product, descriptionString, self.imgPixbuf)
                    self.seen_products.append(self.new_product)
                    self.old_product = self.new_product

        return True

    def main(self):
        """Create the timeout object and let the program sit in gtk.main()"""

        self.site_poller = gobject.timeout_add(self.seconds_to_check, self.site_poll_timeout)

        if SHOW_NOTIFICATION_ICON:
            self.icon = gtk.StatusIcon()
            iconBuf = url2pixbuf(ICONURL)
            if iconBuf:
                self.icon.set_from_pixbuf(iconBuf)
            else:
                self.icon.set_from_stock(gtk.STOCK_DIALOG_ERROR)
            self.menu = gtk.Menu()
            quit = gtk.MenuItem("Quit")
            quit.connect("activate", self.quit_clicked)
            quit.show()
            self.menu.append(quit)
            self.icon.connect("activate", self.icon_clicked, self)
            self.icon.connect("popup-menu", self.show_menu)
    
        gtk.main()

    def __init__(self):

        MyNotify.__init__(self)

        self.old_product = ""
        self.new_product = ""
        self.priceCurrent = ""
        self.priceRegular = ""
        self.imgPixbuf = None
        self.isvisible = False

        self.seen_products = list()

        self.seconds_to_check = CHECK_DELAY * 1000
        
        self.checkingnow = False

        self.site_poll_timeout()


if __name__ == '__main__':

    s = SAC_notifier()
    s.main()

Last edited by Eric89GXL (2008-05-31 22:12:45)

Offline

#8 2009-10-07 21:20:50

76tdffixie
Member
Registered: 2009-10-07
Posts: 1

Re: SteepANDCheap Notification Tool

I don't know if any of you guys have been following the golden deal days or one dollar deal days on steepandcheap but if you have you probably have missed out on a few deals because you were too slow to purchase the item.  I would love to create a script that could alert me when ever there was a item up for 1 dollar on any of the backcounty.com sites (steepandcheap.com, chainlove.com, brociety.com, tramdock.com, bonktown.com, and whiskeymilitia.com).  It would be sweet if it could automatically purchase the item as well, but that may be too difficult.

The problem is I know nothing about scripting and by the time I could learn the one dollar deal days will probably be over.  If one of you guys could create something like this maybe we could all benefit.

Offline

#9 2010-02-15 21:55:33

whaevr
Member
Registered: 2008-03-17
Posts: 182

Re: SteepANDCheap Notification Tool

Why yes I am necroing this thread. tongue

I messed with the script and made it work for whiskeymilitia.com...since I'm not all that into hiking and climbing gear.

I also changed the delay to 120 secs (2mins) because every 15 secs seemed a bit excessive..the deals only change every 20mins or so.
Feeling a bit paranoid and you might miss a deal? Just change the CHECK_DELAY field back to 15 if you want.

I also have it set to use Chromium for the "Buy now!" button. If you want to change it to something else just change the BROWSER field.
Im not claiming any credit here for this, all did was setup a feedburner for it and changed literally 4 or 5 things in the script. roll

 #!/usr/bin/python

"""

MhiskeyMilitia RSS Notification Tool v0.2
(c) 2008 Michael Seiler and Eric Larson


"""

import urllib
import Image, cStringIO
from xml.dom import minidom
from pynotify import *
import gtk, gobject
import subprocess

#Global Constants
TIMEOUT = EXPIRES_DEFAULT                   #Time in milliseconds for the popup to stick around
                                            #Use EXPIRES_DEFAULT for the default or EXPIRE_NEVER for forever
WEBSITE = 'http://www.whiskeymilitia.com/'    #WM Website
CHECK_DELAY = 120                           #Time in seconds between website checks
BROWSER = '/usr/lib/chromium-browser/chromium-browser' #No symlinks!
FEEDADDRESS = 'http://feeds.feedburner.com/whiskeymilitia/rtzq' # WM RSS Feed
ICONURL = 'http://images.whiskeymilitia.com/images/icon/wm.ico' # WM statusbar icon URL
SHOW_NOTIFICATION_ICON = True               # Show WM icon in notification area?
RECHECK_ON_CLICK = False                    # Check the WM deal every time the notifier is activated by clicking the icon?

def url2pixbuf(imgurl):
    img_feed = None
    
    try:
        img_feed = urllib.urlopen(imgurl).read()
    except:
        img_feed = None
    if img_feed:
        im = Image.open(cStringIO.StringIO(img_feed)).convert("RGB")
        return gtk.gdk.pixbuf_new_from_data(im.tostring(),gtk.gdk.COLORSPACE_RGB,False,8,im.size[0],im.size[1],3*im.size[0])
    else:
        return None

class MyNotify(object):
    """

    MyNotify

        Create and maintain the notification window and corresponding action methods

        USAGE:  n = MyNotify()
                n.notify(title, message)

    """

    def notify(self, title, message, imgPixbuf):
        """Show notification with a buy button and image"""
        
        self.notification.update(title, message)
        
        if imgPixbuf:
            self.notification.set_icon_from_pixbuf(imgPixbuf)
            
        self.notification.show()
        self.isvisible = True

    def icon_clicked(icon,event,self):
        if self.isvisible:
            self.notification.close()
        else:
            if RECHECK_ON_CLICK and not self.checkingnow:
                self.site_poll_timeout()
            self.notification.show()
            self.isvisible = True

    def show_menu(self, icon, button, time):
        self.menu.popup(None, None, gtk.status_icon_position_menu, button, time, icon)

    def quit_clicked(self, menuItem):
        if self.isvisible:
            self.notification.close()
        gtk.main_quit()

    def open_site(self, n, action):
        """Called if the user clicks the buy button"""

        subprocess.Popen([BROWSER, WEBSITE])

    def destroy(self, n, action=None):
        """Called whenever the window disappears"""

        self.isvisible = False
    
    def __init__(self):

        init('WMrss.py')

        self.notification = Notification("Product", "Pricing", gtk.STOCK_DIALOG_WARNING)
    
        self.notification.set_urgency(URGENCY_LOW)
        self.notification.set_timeout(TIMEOUT)
  
        self.notification.connect('closed', self.destroy)

        self.notification.add_action('buy', 'Buy this item', self.open_site)    #User clicks button


class WM_notifier(MyNotify):
    """

    WM_notifier

        Handle timing, notification, and gtk interaction

        USAGE:  s = WM_notifier()
                s.main()

        Creates a timeout that downloads information from WM every CHECK_DELAY seconds,
        then pops up a notification using pynotify if it finds a different product.


    """

    def read_data(self):
        """Read data from the WM website, then parse it"""
        self.checkingnow = True
        self.new_product = ""
        self.description = ""
        self.imgLocation = ""
        self.priceCurrent = ""
        self.priceRegular = ""
        self.imgPixbuf = None

        try:
            self.file_feed = urllib.urlopen(FEEDADDRESS).read()
        except:
            #No interweb connection? WM down?
            self.file_feed = None

        if self.file_feed:
            self.file_xml = minidom.parseString(self.file_feed)
            self.item_node = self.file_xml.getElementsByTagName("item")
        

            for childNode in self.item_node[0].childNodes:
                if childNode.nodeType == childNode.ELEMENT_NODE:
                    if childNode.tagName == 'title':
                        self.new_product = childNode.firstChild.data
                    if childNode.tagName == 'odat:listDescription':
                        self.description = childNode.firstChild.data
                    if childNode.tagName == 'odat:priceCurrent':
                        self.priceCurrent = childNode.firstChild.data
                    if childNode.tagName == 'odat:priceRegular':
                        self.priceRegular = childNode.firstChild.data
                    if childNode.tagName == 'odat:tinyImage':
                        self.imgLocation = childNode.firstChild.data

            self.imgPixbuf = url2pixbuf(self.imgLocation)

        self.checkingnow = False

    def site_poll_timeout(self):
        """Timeout called every CHECK_DELAY secs, which calls read_data, then notify if the item changed"""

        self.read_data()

        if self.file_feed:
            if self.new_product != self.old_product and not self.new_product in self.seen_products:
                if self.new_product:
                    percOff = '%d' % round(100.0*(float(self.priceRegular) - float(self.priceCurrent))/float(self.priceRegular))
                    descriptionString = "\n<b>Price: $" + self.priceCurrent + " (" + percOff + "% off!)</b>\nRegularly: $" + self.priceRegular + "\n\n" + self.description
                    self.notify(self.new_product, descriptionString, self.imgPixbuf)
                    self.seen_products.append(self.new_product)
                    self.old_product = self.new_product

        return True

    def main(self):
        """Create the timeout object and let the program sit in gtk.main()"""

        self.site_poller = gobject.timeout_add(self.seconds_to_check, self.site_poll_timeout)

        if SHOW_NOTIFICATION_ICON:
            self.icon = gtk.StatusIcon()
            iconBuf = url2pixbuf(ICONURL)
            if iconBuf:
                self.icon.set_from_pixbuf(iconBuf)
            else:
                self.icon.set_from_stock(gtk.STOCK_DIALOG_ERROR)
            self.menu = gtk.Menu()
            quit = gtk.MenuItem("Quit")
            quit.connect("activate", self.quit_clicked)
            quit.show()
            self.menu.append(quit)
            self.icon.connect("activate", self.icon_clicked, self)
            self.icon.connect("popup-menu", self.show_menu)
    
        gtk.main()

    def __init__(self):

        MyNotify.__init__(self)

        self.old_product = ""
        self.new_product = ""
        self.priceCurrent = ""
        self.priceRegular = ""
        self.imgPixbuf = None
        self.isvisible = False

        self.seen_products = list()

        self.seconds_to_check = CHECK_DELAY * 1000
        
        self.checkingnow = False

        self.site_poll_timeout()


if __name__ == '__main__':

    s = WM_notifier()
    s.main()

Proof? tongue
th_snapshot5.png
<.<
Dont judge my wallpaper. big_smile

Last edited by whaevr (2010-02-15 22:20:27)

Offline

#10 2010-02-19 17:17:23

buttons
Member
From: NJ, USA
Registered: 2007-08-04
Posts: 620

Re: SteepANDCheap Notification Tool

Oh wow! Sweet! I'm glad this is getting love.


Cthulhu For President!

Offline

#11 2010-03-24 15:07:37

hansendc
Member
Registered: 2010-03-24
Posts: 1

Re: SteepANDCheap Notification Tool

Especially for the $1.00 items, the RSS feed is too slow.  It is cached much more aggressively by the load balancing servers.  It can take 30-40 seconds for it to get updated.  Just look at the HTTP headers coming back.  The web server isn't even the same:

js.header:Server: ECS (sjo/5226)
rss.header:Server: lighttpd/1.4.20

You'll also notice that you can use the 'If-Modified-Since:' HTTP header for the RSS feed, and http keepalives.  You can poll much more often since each each request is only a couple hundred bytes.  In fact, the internal javascript on the SAC pages (see dynodat.js) sets and uses this header.

Their servers also take 'Accept-Encoding'.  These files compress pretty well, so this can further reduce bandwidth usage.  Any bandwidth reduction is good because it means you can poll more often.

Offline

Board footer

Powered by FluxBB