Skip to content

Commit

Permalink
Improvements on PR jpchip#80
Browse files Browse the repository at this point in the history
Apologies if this PR is a duplicate of jpchip#80 but I was not sure if the requested changes would be made after a month. Couldn't contribut to Kyptonova's branch due to restrictions. The original PR was good, but made changes according to the reviews and used some more ES7 syntax.
From the original PR:
Creates a database for tracking sweepstakes entries. After sweepstakes are entered, it will skip checking it in the future. This will prevent unnecessary hops between the main screen and individual sweepstakes. The thought being that this will cut down on login page requests and CAPTCHAs.
Addresses jpchip#74.
  • Loading branch information
rinoma committed Jun 4, 2019
1 parent 767b11a commit 54ff7bb
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 130 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ node_modules/
/eng.traineddata
.ggrc.json
user_data/
.vscode
gg.db
63 changes: 30 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ and the script will always skip those entries.

I wanted a reason to experiment with [Puppeteer](https://github.com/GoogleChrome/puppeteer), so here goes.

Note: does not support entries that require following someone. Some video giveaways require
using Chrome instead of Chromium, see **chromeExecutablePath** section below.
Note: does not support entries that require following someone.

## Installation

Expand Down Expand Up @@ -66,23 +65,19 @@ After running `gg init`, you'll have a `.ggrc.json` file in your directory. It w
"remember_me": false,
"sendgrid_api_key": "",
"sendgrid_cc": "",
"blacklist": "floss,socks,ties",
"chromeExecutablePath": "",
"minimum_price": 0
"blacklist": "floss,socks,ties"
}
```

| Option | Description |
| ------------- | ------------- |
| username | Your Amazon Account Email Address |
| password | Your Amazon Account Password |
| 2FA | Set true if you have two factor authentication enabled. Defaults to false. |
| remember_me | Set true if you want to stay logged in between running scripts. Defaults to false. |
| 2FA | Set true if you have two factor authentication enabled |
| remember_me | Set true if you want to stay logged in between running scripts |
| sendgrid_api_key | Your [sendgrid](https://sendgrid.com/) API key, if you want to receive an email when you win. Optional |
| sendgrid_cc | An email address to be cc'ed if you win |
| blacklist | Comma delimited list of keywords to avoid when entering giveaways. Optional |
| chromeExecutablePath | Path to your own install of Chrome. Optional |
| minimum_price | Skip the giveaways with items with price lower than the minimum price. Optional |

### Two factor Authentication (2FA)

Expand All @@ -99,40 +94,42 @@ Keywords are case insensitive.

The console will let you know when it skips giveaways that you marked as blacklisted like `giveaway 5 is blacklisted [kindle edition].`

### Emails

If you want to receive an email notification on winning, sign up for a free [sendgrid](https://sendgrid.com/) account and
add the API key to your `.ggrc.json` file. Set `sendgrid_cc` to CC the winning email to another address.

### chromeExecutablePath

You can point to an existing install of Chrome using chromeExecutablePath:

On Windows:
```
"chromeExecutablePath" : "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
```
On MacOS:
```
"chromeExecutablePath" : "../../Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
```

This is required if you want to be able to enter all video giveaways. Chromium, which Puppeteer
uses by default, does not support some of the video types used. The script will work fine without it,
but it will only enter video giveaways with YouTube videos.

## CAPTCHAs
### CAPTCHAs

Every so often Amazon will present a CAPTCHA. The script will attempt to enter it automatically, but if it fails, it will
pause and wait for you to enter it. The console will warn you with an `ENTER CAPTCHA!` message,
and you **should** get a system notification (only tested it on Windows 10).

## Winning

### Winning

If you are lucky enough to win, you should get a system notification and the url to
the page will be logged like `Winning Entry URL: https://amazon.com/ga/p/335..`.

If you set your `sendgrid_api_key` you'll also receive an email.
#### Emails

If you want to also receive an email notification, sign up for a free [sendgrid](https://sendgrid.com/) account and
add the API key to your `.ggrc.json` file.

#### Database

Upon startup an SQLite3 database will be created in the application directory called gg.db.

A trimmed URL of the sweepstakes entry is stored along with a code that establishes what happened when the sweepstakes entry was attempted to be processed. The timestamp of when the sweepstakes code was received is stored. As Giveaway-Grabber iterates through the different sweepstakes entries available it will check the database. If Giveaway-Grabber finds that a code was registered for the URL then it will be skipped without actually going into the sweepstakes page. This will cut down on all the unnecessary hops into and out of sweepstakes entries when Giveaway-Grabber is restarted. There is a delay introduced so that pages aren't scrolled through in extremely rapid succession.

The table below outlines the codes used and what they are indicating.
| Code | Description |
| ---- | ----------- |
| W | The sweepstakes was won |
| L | The sweepstakes was lost |
| E | The sweepstakes has ended |
| A | The sweepstakes was already entered |
| C | The sweepstakes cannot be entered |

Codes relate to not being able to be processed are not stored. The expectation is that as Giveaway-Grabber matures these items may be corrected. As such, blocking them due to a failure would force Giveaway-Grabber to skip them after the problem is rectified.

**NOTE:** If you delete your giveaway-grabber's install directory, you may want to grab the gg.db file first. When a new Giveaway-Grabber is installed or cloned you can put the gg.db file into the install directory. This way you will be able to retain all of the sweepstakes you have previously entered. The gg.db file will rebuild on its own, but it Giveaway-Grabber will need to go through each page to do so.

### Good luck!

Expand Down
16 changes: 9 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const findUp = require('find-up');
const fs = require('fs');
const { enterGiveaways } = require('./src/giveaways');
const signIn = require('./src/signIn');
const sqlite = require('./src/database');
const { updateDB } = require('./src/updateDB');

//look for config file
const configPath = findUp.sync(['.ggrc.json']);
Expand Down Expand Up @@ -46,10 +48,6 @@ if (args.sendgrid_api_key && args.sendgrid_api_key !== '') {
if (args.sendgrid_cc && args.sendgrid_cc !== '') {
process.env.SENDGRID_CC = args.sendgrid_cc;
}
if (args.chromeExecutablePath && args.chromeExecutablePath !== '') {
process.env.CHROME_EXECUTABLE_PATH = args.chromeExecutablePath;
}
process.env.MINIMUM_PRICE = args.minimum_price || 0;

//start index code
(async () => {
Expand All @@ -60,9 +58,6 @@ process.env.MINIMUM_PRICE = args.minimum_price || 0;
if (args['remember_me']) {
config.userDataDir = './user_data';
}
if (process.env.CHROME_EXECUTABLE_PATH) {
config.executablePath = args['chromeExecutablePath'];
}
const browser = await puppeteer.launch(config);
const page = await browser.newPage();

Expand All @@ -81,8 +76,15 @@ process.env.MINIMUM_PRICE = args.minimum_price || 0;
args['remember_me']
);

await sqlite.open('./gg.db');

//initialize database and perform any upgrades
await updateDB();

//enter giveaways
await enterGiveaways(page, args.page || 1);

await sqlite.close();

await browser.close();
})();
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"node-notifier": "~5.4.0",
"puppeteer": "~1.13.0",
"tesseract.js": "~1.0.14",
"sqlite3": "~4.0.6",
"yargs": "~13.2.2"
},
"devDependencies": {
Expand Down
109 changes: 109 additions & 0 deletions src/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//Code borrowed from example found at https://www.scriptol.com/sql/sqlite-async-await.php
const sqlite3 = require('sqlite3').verbose();
var db;

/**
* Creates or opens existing database.
* @param path Database location
* @returns {Promise<string>}
*/
const open = path =>
new Promise(resolve => {
this.db = new sqlite3.Database(path, err => {
if (err) reject('Open error: ' + err.message);
else resolve(path + ' opened');
});
});

/**
* Used for insert, delete, or update queries.
* @param query Query that should be executed.
* @param params Array of paramaters of the query.
* @returns {Promise<void>}
*/
const run = (query, params) =>
new Promise((resolve, reject) => {
this.db.run(query, params, err => {
if (err) reject(err.message);
else resolve(true);
});
});

/**
* Read the first row from a Select query.
* @param query The select statement that should be processed.
* @returns {Promise<void>}
*/
const get = (query, params) =>
new Promise((resolve, reject) => {
this.db.get(query, params, (err, row) => {
if (err) reject('Read error: ' + err.message);
else {
resolve(row);
}
});
});

/**
* Read all rows that match the query.
* @param query Query that should be executed.
* @param params Array of parameters to be used with supplied query.
* @returns {Promise<void>}
*/
const all = (query, params) =>
new Promise((resolve, reject) => {
if (params == undefined) params = [];

this.db.all(query, params, (err, rows) => {
if (err) reject('Read error: ' + err.message);
else {
resolve(rows);
}
});
});

/**
* Each row returned one by one
* @param query Query that should be executed.
* @param params Array of parameters to be used with supplied query.
* @param action Call back function used to process results.
* @returns {Promise<void>}
*/
const each = (query, params, action) =>
new Promise((resolve, reject) => {
var db = this.db;
db.serialize(() => {
db.each(query, params, (err, row) => {
if (err) reject('Read error: ' + err.message);
else {
if (row) {
action(row);
}
}
});
db.get('', (err, row) => {
resolve(true);
});
});
});

/**
* Closes existing database.
* @param path Database location
* @returns {Promise<void>}
*/
const close = () =>
new Promise((resolve, reject) => {
this.db.close();
resolve(true);
});

module.exports = {
db,
open,
run,
get,
all,
each,
close
};
Loading

0 comments on commit 54ff7bb

Please sign in to comment.