Created
April 21, 2017 17:52
-
-
Save kainage/c18063d49d38c95ed5f2e897eb8674c8 to your computer and use it in GitHub Desktop.
Get Dat NES Classic Brah
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
require 'json' | |
require 'open-uri' | |
require 'nokogiri' | |
require 'byebug' | |
require 'logger' | |
# OPTIONS # | |
# In Seconds | |
INTERVAL = 5 | |
EMAIL = 'YOUR_EMAIL_HERE' | |
VERBOSE = ENV['VERBOSE'] | |
USE_PROXY = ENV['PROXY'] | |
# UA # | |
HEADERS = { | |
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0' | |
} | |
# PRICE CHECKS # | |
# [ <Name (Just for logging & email)>, <Link to product page>, <Desired Price>] | |
AMAZON_PRICE_CHECKS = [ | |
# ['Amazon.ca: Clippers', 'https://www.amazon.ca/dp/B0009XH6ZA', 100.00] | |
] | |
# STOCK CHECKS # | |
# [ <Name (Just for logging & email)>, <Link to product page>] | |
AMAZON_CA = { | |
'Amazon.ca: NES Classic' => 'https://www.amazon.ca/Nintendo-Entertainment-System-NES-Classic/dp/B01IFJBQ1E' | |
} | |
AMAZON_IT = { | |
} | |
AMAZON_DE = { | |
} | |
TRU = { | |
} | |
PROXIES = [ | |
['207.99.118.74', 8080], | |
['45.63.48.26', 3128], | |
['71.205.192.36', 3128], | |
] | |
module StockHunter | |
module_function | |
def hunt | |
attack | |
rescue => e | |
log_error(e, 'StandardError') | |
hunt | |
end | |
def attack | |
loop do | |
check_amazon_ca_prices | |
check_amazon_ca | |
# check_amazon_it | |
# check_amazon_de | |
end | |
end | |
# PRICE CHECKING # | |
def check_amazon_ca_prices | |
AMAZON_PRICE_CHECKS.each do |name, url, target| | |
doc = fetch_page(name, url) | |
next unless doc | |
text = doc.search("span#priceblock_ourprice").text | |
raw = text.match(/\d+\..+/) | |
price = raw[0]&.to_f if raw | |
check_price(doc, name, url, price) do | |
if price | |
price < target | |
else | |
false | |
end | |
end | |
snooze | |
end | |
end | |
# STOCK CHECKING # | |
def check_amazon_ca | |
AMAZON_CA.each do |name, url| | |
doc = fetch_page(name, url) | |
next unless doc | |
stock = doc.css('div#merchant-info').text if doc | |
check_stock(doc, name, url) do | |
stock.match('sold by Amazon') | |
end | |
snooze | |
end | |
end | |
def check_amazon_it | |
AMAZON_IT.each do |name, url| | |
doc = fetch_page(name, url) | |
next unless doc | |
stock = doc.css('div#merchant-info').text if doc | |
check_stock(doc, name, url) do | |
stock.match('Venduto e spedito da Amazon') | |
end | |
snooze | |
end | |
end | |
def check_amazon_de | |
AMAZON_DE.each do |name, url| | |
doc = fetch_page(name, url) | |
next unless doc | |
stock = doc.css('div#availability_feature_div').text if doc | |
check_stock(doc, name, url) do | |
stock.match('Verkauf und Versand durch') && stock.match('Amazon') | |
end | |
snooze | |
end | |
end | |
def check_indigo | |
INDIGO.each do |name, url| | |
doc = fetch_page(name, url) | |
next unless doc | |
stock = doc.css('div.pricing-box').text if doc | |
check_stock(doc, name, url) do | |
stock.match('In stock online') | |
end | |
snooze | |
end | |
end | |
def check_bb | |
BB.each do |name, url| | |
doc = fetch_page(name, url) | |
next unless doc | |
stock = doc.css('div#schemaorg-offer').text if doc | |
check_stock(doc, name, url) do | |
stock.match('In Stock') | |
end | |
snooze | |
end | |
end | |
def check_tru | |
TRU.each do |name, url| | |
doc = fetch_page(name, url) | |
next unless doc | |
stock = doc.css('div#buy-interior').xpath('//comment()').text if doc | |
check_stock(doc, name, url) do | |
stock.match('In Stock') | |
end | |
snooze | |
end | |
end | |
# PRICE CHECKING # | |
def check_price(doc, name, url, price, &block) | |
if doc && block.call | |
alert_price(url, name, price) | |
else | |
log_not_cheaper(name, price) | |
end | |
end | |
def alert_price(url, name, price) | |
msg = "🎉 HOLY MOLY! #{name} is #{price}!!" | |
logger.info msg | |
puts msg if VERBOSE | |
send_email(url, msg) | |
end | |
def log_not_cheaper(name, price) | |
msg = "🙁 #{name} is still #{price}" | |
logger.info msg | |
puts msg if VERBOSE | |
end | |
# STOCK CHECKING # | |
def check_stock(doc, name, url, &block) | |
if doc && block.call | |
alert_in_stock(url, name) | |
else | |
log_not_in_stock(name) | |
end | |
end | |
def alert_in_stock(url, name) | |
msg = "🎉 HOLY MOLY! #{name} is in stock!!" | |
logger.info msg | |
puts msg if VERBOSE | |
send_email(url, msg) | |
end | |
def log_not_in_stock(name) | |
msg = "🙁 #{name} is not in stock" | |
logger.info msg | |
puts msg if VERBOSE | |
end | |
# COMMON # | |
def fetch_page(name, url) | |
ip, port = PROXIES.rotate!.first | |
file = | |
if USE_PROXY | |
open(url, HEADERS.merge(proxy: "http://#{ip}:#{port}")) | |
else | |
open(url, HEADERS) | |
end | |
Nokogiri::HTML(file) | |
rescue => e | |
log_error(e, name, ip) | |
false | |
end | |
def send_email(url, msg) | |
to = EMAIL | |
subject = msg | |
content = url | |
`echo "#{content}" | mailx -s "#{subject}" #{to}` | |
end | |
def log_error(e, name, ip) | |
msg = "#{ip} -- #{name} - #{e.message}" | |
logger.error msg | |
puts msg if VERBOSE | |
end | |
def logger | |
Logger.new('stock_hunter.log') | |
end | |
def snooze | |
sleep(INTERVAL) | |
end | |
end | |
if __FILE__ == $0 | |
puts 'Running ...' | |
StockHunter.hunt | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment