Last active
April 15, 2016 01:33
-
-
Save jashsu/10239863f44261f9419acc1ba77d325c to your computer and use it in GitHub Desktop.
Monitor availability of limited rewards tiers using only API calls (no scraping!)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Python 2.7.10 (default, Oct 23 2015, 18:05:06) | |
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin | |
Type "copyright", "credits" or "license()" for more information. | |
>>> WARNING: The version of Tcl/Tk (8.5.9) in use may be unstable. | |
Visit http://www.python.org/download/mac/tcltk/ for current information. | |
>>> import requests, json | |
>>> from lxml import etree | |
>>> from StringIO import StringIO | |
>>> from pprint import pprint | |
>>> | |
>>> MAX_RWD_DESC = 20 | |
>>> | |
>>> session = requests.Session() | |
>>> r = session.get('https://www.kickstarter.com/projects/248983394/ossic-x-the-first-3d-audio-headphones-calibrated-t/pledge/new') | |
>>> rt = etree.parse(StringIO(r.text), etree.HTMLParser(encoding = 'UTF-8')) | |
>>> reward_data = [json.loads(i.attrib['data-reward']) for i in rt.xpath('//*[contains(concat(" ", @class, " "), " pledge-selectable ")]')] | |
>>> reward_urls = [{i['reward'][0:MAX_RWD_DESC]: i.get('urls')} for i in reward_data] | |
>>> pprint(reward_urls) | |
[{u'No Reward': None}, | |
{u'THANK YOU! Every bit': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815481?signature=1460763286.1cd3f1e0c00a939925918947d0503f58ae71b4c5'}}}, | |
{u'OSSIC BRANDED GOOGLE': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815483?signature=1460763286.03254280d983f1f8ee85beeac57145fd70478a8b'}}}, | |
{u'SUPER EARLY BIRD: SA': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815484?signature=1460763286.afb10489cf62ee612cdd9a9e09573155b8a92703'}}}, | |
{u'EARLY BIRD: SAVE $18': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815485?signature=1460763286.8478f8fe803ae9376e9ea26efd6a7d1d2cfb19d3'}}}, | |
{u'KICKSTARTER SPECIAL:': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815486?signature=1460763286.8aff8fcee9644c819a065ff9ccf6ae3b49bc64ec'}}}, | |
{u'KICKSTARTER LAST CHA': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/5005057?signature=1460763286.dcfba2ae0aeac75891a5f1a005a7606131cf9d27'}}}, | |
{u'EXCLUSIVE INNOVATOR ': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815488?signature=1460763286.5ef69b76905afa352bf7699861035e2ef4b25550'}}}] | |
>>> | |
>>> | |
>>> r2 = session.get('https://api.kickstarter.com/v1/projects/1241205786/rewards/4815486?signature=1460763286.8aff8fcee9644c819a065ff9ccf6ae3b49bc64ec') | |
>>> reward = json.loads(r2.text) | |
>>> reward['shipping_rules'] = [] | |
>>> pprint(reward) | |
{u'backers_count': 4444, | |
u'description': u'KICKSTARTER SPECIAL: SAVE $150\r\nYou\u2019ll receive one OSSIC X headphone. You\u2019re one of the first onboard the audio revolution!', | |
u'estimated_delivery_on': 1483228800, | |
u'id': 4815486, | |
u'limit': 6000, | |
u'minimum': 249.0, | |
u'project_id': 1241205786, | |
u'remaining': 1556, | |
u'reward': u'KICKSTARTER SPECIAL: SAVE $150\r\nYou\u2019ll receive one OSSIC X headphone. You\u2019re one of the first onboard the audio revolution!', | |
u'shipping_enabled': True, | |
u'shipping_preference': u'unrestricted', | |
u'shipping_rules': [], | |
u'shipping_summary': u'Ships anywhere in the world', | |
u'updated_at': 1460677120, | |
u'urls': {u'api': {u'reward': u'https://api.kickstarter.com/v1/projects/1241205786/rewards/4815486?signature=1460763785.02f80a134adb8c26e81b6226dd158987ad1a71b7'}}} | |
>>> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json, urllib2 | |
from time import sleep, ctime | |
from random import random | |
def send_email(): | |
print('Importing libraries...') | |
import smtplib | |
from email.MIMEMultipart import MIMEMultipart | |
from email.MIMEText import MIMEText | |
print('Composing and sending...') | |
#Edit this stuff... | |
user = 'user@example.com' | |
pswd = 'secretkey' | |
recipient = 'user@example.com' | |
smtp = smtplib.SMTP() | |
smtp.connect('smtp.gmail.com', 587) | |
smtp.ehlo() | |
smtp.starttls() | |
smtp.login(user, pswd) | |
message = MIMEMultipart() | |
message['From'] = 'noreply' | |
message['To'] = recipient | |
message['Subject'] = 'Kickstarter alert' | |
html = """<html><head></head><body><p>Kickstarter alert</p></body></html>""" | |
message.attach(MIMEText(html, 'html')) | |
smtp.sendmail('noreply', recipient, message.as_string()) | |
smtp.close() | |
print('Done!') | |
def monitor(url): | |
done = False | |
while (not done): | |
r = urllib2.urlopen(url) | |
reward = json.loads(r.read()) | |
remaining = reward['remaining'] | |
if remaining > 0: | |
send_email() | |
done = True | |
else: | |
#update the url to fetch | |
url = reward['urls']['api']['reward'] | |
signature = url.split("signature=",1)[1].split('.') | |
print(ctime(float(signature[0])) + " hash:" + signature[1]) | |
sleep(8 + random() * 4) |
API call signature parameter is a unix timestamp appended with a SHA1 of an unknown payload.
The hash is somehow tied to the reward object. My guess is the hash payload is something like sha1(userid + projectid + rewardid + timestamp + secret)
>>> monitor(url)
Fri Apr 15 18:22:09 2016 hash:21b901d948befd65bea2c63810540885105b82fc
Fri Apr 15 18:22:18 2016 hash:1c62bcf603f026a6c4779531a57c33b37e9a17ab
Fri Apr 15 18:22:27 2016 hash:b547b09233269d96a7ac2ba955192024f074efa8
Fri Apr 15 18:22:38 2016 hash:1b042d67bdd15fcb9b20997ac105fe01f431b586
Fri Apr 15 18:22:47 2016 hash:e6cc1f257a248158c0b52b8fc36fce0a51b02414
Fri Apr 15 18:22:57 2016 hash:06922a3507672c943d38ce7a9f4422f1b6c5a808
Fri Apr 15 18:23:08 2016 hash:4d4dcd28014f284a33a1820ed93dd3ba0c9091a9
Fri Apr 15 18:23:17 2016 hash:f721734fc264b460a65f58d05b9ac374190d090f
Fri Apr 15 18:23:26 2016 hash:332c0088a29bc4c5f82303beee3e11a4010e4160
Fri Apr 15 18:23:39 2016 hash:8c835aa98532ddec188e1c880ecf61d080428c5d
Fri Apr 15 18:23:47 2016 hash:2b4b2ef82506e0416eac02b12ede5928628a669b
Importing libraries...
Composing and sending...
Done!
Works :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Error message if the API is called without a valid signature: