Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2.0.0 #184

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
"use strict";

const { execFileSync } = require("child_process");
const { unlink, writeFile } = require("fs").promises;
const { "Client": EpicGames } = require("fnbr");
const Logger = require("tracer").console(`${__dirname}/logger.js`);

let DeviceAuths = {};
try {
DeviceAuths = require(`${__dirname}/data/device_auths.json`);
unlink(`${__dirname}/data/device_auths.json`);
writeFile(`${__dirname}/data/deviceAuths.json`, JSON.stringify(DeviceAuths, null, 4));
} catch (error) { /* No device_auths.json */ }
try {
DeviceAuths = require(`${__dirname}/data/deviceAuths.json`);
} catch (error) { /* No deviceAuths.json */ }

// eslint-disable-next-line max-len
const AUTH_URL = "https://www.epicgames.com/id/logout?redirectUrl=https%3A//www.epicgames.com/id/login%3FredirectUrl%3Dhttps%253A%252F%252Fwww.epicgames.com%252Fid%252Fapi%252Fredirect%253FclientId%253D3446cd72694c4a4485d81b77adbb2141%2526responseType%253Dcode";

function commands() {
let { platform } = process;
switch (platform) {
case "android":
case "linux":
return ["xdg-open"];
case "darwin":
return ["open"];
case "win32":
return ["cmd", ["/c", "start"] ];
default:
throw new Error(`Platform ${platform} isn't supported.`);
}
}

function open(url) {
return new Promise((resolve, reject) => {
try {
const [command, args = []] = commands();
execFileSync(
command,
[...args, url],
);
return resolve();
} catch (error) {
return reject(error);
}
});
}

function printAccounts() {
Logger.info("Your accounts:");
let emails = Object.keys(DeviceAuths);
for (let i = 0; i < emails.length; i++) {
Logger.info(`${i + 1}) ${emails[i]}`);
}
return emails;
}

function printModes() {
printAccounts();
Logger.info("");
Logger.info("Available modes:");
Logger.info("1) Add account");
Logger.info("2) Remove account");
Logger.info("0) Exit");
Logger.info("");
}

async function selectMode() {
printModes();

let mode = await EpicGames.consoleQuestion("Please select a mode (0-2): ");
switch (Number(mode)) {
case 0:
process.exit();
break;

case 1:
// eslint-disable-next-line no-use-before-define
addAccount();
break;

case 2:
// eslint-disable-next-line no-use-before-define
removeAccount();
break;

default:
Logger.error("Invalid mode, please choose 1 or 2");
selectMode();
break;
}
}

async function saveDeviceAuth(email, deviceAuth) {
if (!email) {
throw new Error("Found no email in account!");
}

DeviceAuths[email] = deviceAuth;
await writeFile(`${__dirname}/data/deviceAuths.json`, JSON.stringify(DeviceAuths, null, 4));
Logger.info(`Successfully added account: ${email}`);
Logger.info("");
selectMode();
}

function addAccount() {
open(AUTH_URL).catch(() => Logger.info(`Failed to open: ${AUTH_URL}. Please open it manually.`));
let auth = { "authorizationCode": () => EpicGames.consoleQuestion("Please enter an authorization code: ") };
let debug = false;
let client = new EpicGames({ auth, debug });
let deviceAuth = null;
let saveIfPossible = () => {
if (client.user && client.user.email && deviceAuth) {
let { email } = client.user;
saveDeviceAuth(email, deviceAuth);
client.logout();
}
};

client.on("ready", () => saveIfPossible());
client.on("deviceauth:created", (da) => {
deviceAuth = da;
saveIfPossible();
});

client.login();
}

async function removeAccount() {
let emails = printAccounts();
Logger.info("0) Cancel");
Logger.info("");
let i = await EpicGames.consoleQuestion(`Please select an account to delete (0-${emails.length}): `);
if (Number(i) === 0) {
selectMode();
return;
}

let email = emails[i - 1];
if (!email) {
Logger.error("Invalid number, please choose a valid account");
removeAccount();
return;
}

delete DeviceAuths[email];
await writeFile(`${__dirname}/data/deviceAuths.json`, JSON.stringify(DeviceAuths, null, 4));
Logger.info(`Successfully removed account: ${email}`);
Logger.info("");
selectMode();
}

function start() {
Logger.info("Welcome to account management");
Logger.info("");
selectMode();
}

start();
155 changes: 72 additions & 83 deletions claimer.js
Original file line number Diff line number Diff line change
@@ -1,92 +1,81 @@
"use strict";

const { writeFile, writeFileSync, existsSync, readFileSync } = require("fs");
if (!existsSync(`${__dirname}/data/config.json`)) {
writeFileSync(`${__dirname}/data/config.json`, readFileSync(`${__dirname}/data/config.example.json`));
}
if (!existsSync(`${__dirname}/data/history.json`)) {
writeFileSync(`${__dirname}/data/history.json`, "{}");
}

const { "Launcher": EpicGames } = require("epicgames-client");
const { writeFile, prepareData, sleep } = require(`${__dirname}/src/utils`);
prepareData();

const { "Client": EpicGames } = require("fnbr");
const { appriseNotify } = require(`${__dirname}/src/appriseNotify`);
const { freeGamesPromotions } = require(`${__dirname}/src/gamePromotions`);
const { latestVersion } = require(`${__dirname}/src/latestVersion.js`);
const Auths = require(`${__dirname}/data/device_auths.json`);
const { offerPurchase } = require(`${__dirname}/src/offerPurchase.js`);
const AutoGitUpdate = require("auto-git-update");
const Config = require(`${__dirname}/data/config.json`);
const Fork = require("child_process");
const DeviceAuths = require(`${__dirname}/data/deviceAuths.json`);
const History = require(`${__dirname}/data/history.json`);
const Logger = require("tracer").console(`${__dirname}/logger.js`);
const OS = require("os");
const Package = require(`${__dirname}/package.json`);

function appriseNotify(appriseUrl, notificationMessages) {
if (!appriseUrl || notificationMessages.length === 0) {
return;
}

let notification = notificationMessages.join("\n");
try {
let s = Fork.spawnSync("apprise", [
"-vv",
"-t",
`Epicgames Freebies Claimer ${Package.version}`,
"-b",
notification,
appriseUrl,
]);

let output = s.stdout ? s.stdout.toString() : "ERROR: Maybe apprise not found?";
if (output && output.includes("ERROR")) {
Logger.error(`Failed to send push notification (${output})`);
} else if (output) {
Logger.info("Push notification sent");
} else {
Logger.warn("No output from apprise");
}
} catch (err) {
Logger.error(`Failed to send push notification (${err})`);
}
}

function write(path, data) {
// eslint-disable-next-line no-extra-parens
return new Promise((res, rej) => writeFile(path, data, (err) => (err ? rej(err) : res(true))));
}

function sleep(delay) {
return new Promise((res) => setTimeout(res, delay * 60000));
}

(async() => {
let { options, delay, loop, appriseUrl, notifyIfNoUnclaimedFreebies } = Config;
let {
appriseUrl,
autoUpdate,
delay,
loop,
notifyIfNoUnclaimedFreebies,
options,
} = Config;
let updater = new AutoGitUpdate({
"executeOnComplete": "npm start",
"exitOnComplete": true,
"fromReleases": true,
"logConfig": {
"logGeneral": false,
"logWarning": false,
"logError": false,
},
"repository": Package.url,
"tempLocation": OS.tmpdir(),
});

if (process.env.EFC_DISABLE_AUTO_UPDATE) {
autoUpdate = false;
} else if (autoUpdate !== false) {
autoUpdate = true;
}

do {
Logger.info(`Epicgames Freebies Claimer (${Package.version}) by ${Package.author.name || Package.author}`);

let latest = await latestVersion().catch((err) => {
Logger.error(`Failed to check for updates (${err})`);
});
if (autoUpdate) {
await updater.autoUpdate();
}

if (latest && latest !== Package.version) {
Logger.warn(`There is a new release available (${latest}): ${Package.url}`);
if (Object.keys(DeviceAuths).length === 0) {
Logger.warn("You should first add an account!");
Logger.warn("Run the following command:");
Logger.warn("");
Logger.warn("npm run account");
process.exit(0);
}

let notificationMessages = [];
for (let email in Auths) {
let { country } = Auths[email];
for (let email in DeviceAuths) {
let { country } = DeviceAuths[email];
let claimedPromos = History[email] || [];
let newlyClaimedPromos = [];
let useDeviceAuth = true;
let rememberDevicesPath = `${__dirname}/data/device_auths.json`;
let clientOptions = { email, ...options, rememberDevicesPath };
let checkEULA = true;
let createLauncherSession = true;
let deviceAuth = DeviceAuths[email];
let auth = { deviceAuth, checkEULA, createLauncherSession };
let clientOptions = {
...options,
auth,
"createParty": false,
"connectToXMPP": false,
"fetchFriends": false,
};
let client = new EpicGames(clientOptions);

if (!await client.init()) {
let errMess = "Error while initialize process.";
notificationMessages.push(errMess);
Logger.error(errMess);
break;
}

// Check before logging in
let freePromos = await freeGamesPromotions(client, country, country);
let unclaimedPromos = freePromos.filter((offer) => !claimedPromos.find(
Expand All @@ -101,21 +90,21 @@ function sleep(delay) {
continue;
}

let success = await client.login({ useDeviceAuth }).catch(() => false);
if (!success) {
let errMess = `Failed to login as ${email}`;
notificationMessages.push(errMess);
Logger.error(errMess);
let err = await client.login().catch((err) => err);
if (err) {
err = `Failed to login as ${email}: ${err}`;
notificationMessages.push(err);
Logger.error(err);
continue;
}

Logger.info(`Logged in as ${client.account.name} (${client.account.id})`);
Auths[email].country = client.account.country;
write(rememberDevicesPath, JSON.stringify(Auths, null, 4)).catch(() => false); // ignore fails
Logger.info(`Logged in as ${client.user.displayName} (${client.user.id})`);
DeviceAuths[email].country = client.user.country;
await writeFile(`${__dirname}/data/deviceAuths.json`, JSON.stringify(DeviceAuths, null, 4));

for (let offer of unclaimedPromos) {
try {
let purchased = await client.purchase(offer, 1);
let purchased = await offerPurchase(client, offer);
if (purchased) {
Logger.info(`Successfully claimed ${offer.title} (${purchased})`);
newlyClaimedPromos.push(offer.title);
Expand All @@ -132,9 +121,9 @@ function sleep(delay) {
&& err.response.body
&& err.response.body.errorCode === "errors.com.epicgames.purchase.purchase.captcha.challenge") {
// It's pointless to try next one as we'll be asked for captcha again.
let errMess = "Aborting! Captcha detected.";
notificationMessages.push(errMess);
Logger.error(errMess);
let err = "Aborting! Captcha detected.";
notificationMessages.push(err);
Logger.error(err);
break;
}
}
Expand All @@ -151,11 +140,11 @@ function sleep(delay) {
}

await client.logout();
Logger.info(`Logged ${client.account.name} out of Epic Games`);
Logger.info(`Logged ${client.user.displayName} out of Epic Games`);
}
appriseNotify(appriseUrl, notificationMessages);
appriseNotify(appriseUrl, notificationMessages, Logger);

await write(`${__dirname}/data/history.json`, JSON.stringify(History, null, 4));
await writeFile(`${__dirname}/data/history.json`, JSON.stringify(History, null, 4));
if (loop) {
Logger.info(`Waiting ${delay} minutes`);
await sleep(delay);
Expand Down
1 change: 1 addition & 0 deletions data/config.example.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"appriseUrl": null,
"autoUpdate": true,
"delay": 1440,
"loop": false,
"notifyIfNoUnclaimedFreebies": false,
Expand Down
Loading