Skip to content

Instantly share code, notes, and snippets.

@druv5319
Last active December 8, 2021 01:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save druv5319/7b3c4d2626c0e9055e1c51f0d6852081 to your computer and use it in GitHub Desktop.
Save druv5319/7b3c4d2626c0e9055e1c51f0d6852081 to your computer and use it in GitHub Desktop.
Sneaker price tracker using SendGrid and Node.js
require('dotenv').config()
const SneaksAPI = require('sneaks-api');
const fs = require("fs");
const schedule = require('node-schedule');
const sneaks = new SneaksAPI();
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const jsonString = fs.readFileSync("./subscribers.json");
const subscribers = JSON.parse(jsonString);
(async function main() {
const sneakerMap = await getSneakerMap(subscribers);
for await (const subscriber of subscribers) {
if (sneakerMap[subscriber.styleID].price < subscriber.lastResellPrice - 10) {
notifySubscriber(sneakerMap[subscriber.styleID], subscriber.email);
subscriber.lastResellPrice = sneakerMap[subscriber.styleID].price;
}
}
fs.writeFileSync("./subscribers.json", JSON.stringify(subscribers));
})()
async function getSneakerMap(subscribers) {
var sneakerMap = new Object();
for (const subscriber of subscribers) {
if (sneakerMap[subscriber.styleID]) continue;
const sneaker = await sneaksApiFunctionWrapper(subscriber.styleID);
sneakerMap[subscriber.styleID] = sneaker;
}
return sneakerMap;
}
function notifySubscriber(sneaker, email) {
const msg = {
to: RECIPIENT_EMAIL, // Change to your recipient
from: SENDER_EMAIL, // Change to your verified sender
subject: `Price Drop - ${sneaker.name}`,
html: createEmailInHTML(sneaker),
}
sgMail.send(msg)
.then((response) => {
console.log(response[0].statusCode)
console.log(response[0].headers)
})
.catch((error) => {
console.error(error)
})
}
function sneaksApiFunctionWrapper(styleID) {
return new Promise((resolve, reject) => {
sneaks.getProductPrices(styleID, function (err, product) {
const lowestResellSite = Object.keys(product.lowestResellPrice).reduce((a, b) => product.lowestResellPrice[a] > product.lowestResellPrice[b] ? a : b);
const sneaker = {
name: product.shoeName,
image: product.thumbnail,
site: lowestResellSite,
price: product.lowestResellPrice[lowestResellSite],
url: product.resellLinks[lowestResellSite],
styleID: product.styleID
};
resolve(sneaker);
}, (errorResponse) => {
reject(errorResponse);
});
});
}
function createEmailInHTML(sneaker) {
return `
<h2>
<img style="font-size:14px;display:block;margin-left:auto;margin-right:auto;width:200px; padding-bottom:15px" src="https://raw.githubusercontent.com/druv5319/Sneaks-API/master/Screenshots/Sneaks_Logo.png"/>
</h2>
<div style=" margin-left: auto; margin-right: auto;box-sizing:border-box;width:45%; margin-bottom:30px;background:#fdfdfd;border:1px solid #f0f0f0">
<h2 style="text-align: center; color: black; font-family: 'avenir';">Price Drop - ${sneaker.name}</h2>
<p>
<img style="display: block; margin-left: auto; margin-right: auto;" src=${sneaker.image} alt="" width="auto" height="250" />
</p>
<h2 style="text-align: center; color: black; font-family: 'avenir';">$${sneaker.price}</h2>
<p>&nbsp;</p>
<p style="font-size: 1.5em; color: black; font-family: avenir; text-align: center;">${sneaker.name} has dropped to
<strong>$${sneaker.price}</strong> on <strong>${sneaker.site}</strong></p>
<p style="text-align: center;">
<a href="${sneaker.url}" style="box-sizing:border-box;border-color:#348eda;font-weight:400;text-decoration:none;display:inline-block;margin:0;color:#ffffff;background-color:#348eda;border:solid 1px #348eda;border-radius:2px;font-size:14px;padding:12px 45px; font-weight: 600;">Buy now on ${sneaker.site}</a>
</p>
</div>`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment