Repository: frk1/steamhourboostv2 Branch: master Commit: 80a40ebe929b Files: 18 Total size: 21.7 KB Directory structure: gitextract_mouca3w6/ ├── .dockerignore ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .mailmap ├── .yarnclean ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── lib/ │ ├── app.js │ ├── database.js │ ├── migrate.js │ ├── steamaccount.js │ ├── telebot.js │ └── user.js └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ .git ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.{yml,coffee,js,json}] indent_size = 2 [Makefile] indent_style = tab ================================================ FILE: .eslintrc.js ================================================ "use strict"; module.exports = { env: { es6: true, node: true }, extends: ["eslint:recommended"], plugins: ["import"], rules: { curly: ["error", "multi", "consistent"], "import/no-extraneous-dependencies": [ "error", { devDependencies: ["tests*/**", "scripts/**"] } ], "no-console": "off", "no-else-return": "error", "no-inner-declarations": "error", "no-unneeded-ternary": "error", "no-useless-return": "error", "no-var": "error", "no-trailing-spaces": "error", "prefer-arrow-callback": "error", "prefer-const": "error", "react/no-deprecated": "off", semi: ["error", "never"], strict: "error", eqeqeq: 2, "symbol-description": "error", "nonblock-statement-body-position": ["error", "beside"], yoda: ["error", "never", { exceptRange: true }] }, parserOptions: { } }; ================================================ FILE: .gitignore ================================================ database.json node_modules db.json telebot.json *.bak config/ ================================================ FILE: .mailmap ================================================ frk frk frk frk1 frk frk1 ================================================ FILE: .yarnclean ================================================ # test directories __tests__ test tests powered-test # asset directories docs doc website images assets # examples example examples # code coverage directories coverage .nyc_output # build scripts Makefile Gulpfile.js Gruntfile.js # configs appveyor.yml circle.yml codeship-services.yml codeship-steps.yml wercker.yml .tern-project .gitattributes .editorconfig .*ignore .eslintrc .jshintrc .flowconfig .documentup.json .yarn-metadata.json .travis.yml # misc *.md ================================================ FILE: CHANGELOG.md ================================================ 4.1.2 / 2017-11-14 ================== * 🚑 Fix steam accounts not restarting * 🎨 app.js: prefer ternary operator * 📝 readme: install production dependencies only * ✏️ changelog: fix typo * ✨ Add pretty script to format using prettier 4.1.1 / 2017-11-12 ================== * 🚀 Remove dependency on babel * 🔧 Update .editorconfig 4.1.0 / 2017-11-12 ================== * 📝 Update Readme * 🐛 Fix crash if database.json was not created already * 🔨 Cleanup after conversion * ✨ Add husky git-commit hooks * 🐋 Update Dockerfile * 🔥 Remove coffeelint.json * 🔧 Add devDependencies * 🐋 Update Dockerfile * 🚀 Migrate from coffeescript to ES2016+ javascript! * 📝 readme: add snyk.io vulnerabilities badge 4.0.1 / 2017-11-08 ================== * 📝 Add changelog * 🔨 Use manageDB.read() where possible 4.0.0 / 2017-11-08 ================== * 🔧 Update .mailmap * 👥 Add .mailmap * 🔨 Move database migration code to own file * 💥 Move config files to config folder * 🔨 If there are no games do not login * ✨ Add .yarnclean file * 🐋 Add docker-compose.yml * 🔥 Remove package-lock.json * ✨ Integrate telegram bot into app.coffee * 🐋 Use docker multistage feature * 🐋 Add .dockerignore 3.0.2 / 2017-11-07 ================== * 🔨 Move database read/write logic to own file * 🔥 Remove unused dependency * 🚚 Fix telebot.gif path 3.0.1 / 2017-11-06 ================== * 🐛 Add missing require in user.coffee * 🐋 Add Dockerfile * 🚀 Modernize code * ✨ Add .editorconfig * 🔧 Update coffeelint.json 3.0.0 / 2017-11-05 ================== * 🚀 Modernize code * ✨ Add .editorconfig * 🔧 Update coffeelint.json * Merge pull request #4 from M0V3/patch-1 * Fixed directory name in how to 2.3.1 / 2016-08-09 ================== * Fix telegraf-flow incompatibility 2.3.0 / 2016-08-09 ================== * Add telegram bot * Minor code cleanup * Update dependencies 2.2.2 / 2016-08-06 ================== * Generate 2fa code using callback 2.2.1 / 2016-08-05 ================== * Fix readLineSync error 2.2.0 / 2016-07-31 ================== * Use inquirer to select games * Wait 1.5s for the sentry to be confirmed 2.1.1 / 2016-07-30 ================== * Always convert gameid to integer * Reformat timeout error message * Remove dead code * Add missing_fat_arrows to coffeelint 2.1.0 / 2016-07-22 ================== * Make use of Promises * Update steam-user to 3.11.0 * Prefer SteamTotp callback version * Allow retry if 2FA failed * Do not try to log off if steamguard failed earlier * Optimize behaviour if steam guard is invalid * Add support for 'npm start' * Minor code reorganisations 2.0.4 / 2016-07-16 ================== * Remove debug code * Simplify boost.coffee using lodash 2.0.3 / 2016-07-16 ================== * Cleanup boost.coffee * Optimize indentation * Remove superfluous 'loggedOn' event listeners * When logging off set games played to none 2.0.2 / 2016-07-15 ================== * Bump steam-user version * Fix various bugs * Timestamp will now display 24h time 2.0.1 / 2016-07-12 ================== * Improve 'reliable connection' situation * Change timestamp format 2.0.0 / 2016-07-08 ================== * Add minimal node version * Improve handling of common errors * Add coffeelint.json * Add license (MIT) * Add converter.coffee for old db.json style * Do not prompt for steamguard code in boost.coffee * Save sentry files automatically * Initial commit ================================================ FILE: Dockerfile ================================================ FROM mhart/alpine-node:9 WORKDIR /app COPY . /app RUN yarn install --production && yarn cache clean FROM mhart/alpine-node:base-9 WORKDIR /app COPY --from=0 /app . COPY . . CMD [ "node", "/app/lib/app.js" ] ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016-2017 frk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![GitHub package version](https://img.shields.io/github/package-json/v/frk1/steamhourboostv2.svg)](https://github.com/frk1/steamhourboostv2/tree/master) [![Known Vulnerabilities](https://snyk.io/test/github/frk1/steamhourboostv2/badge.svg)](https://snyk.io/test/github/frk1/steamhourboostv2) [![Docker Automated build](https://img.shields.io/docker/automated/frk1/steamhourboostv2.svg)](https://hub.docker.com/r/frk1/steamhourboostv2/) [![Docker Build Status](https://img.shields.io/docker/build/frk1/steamhourboostv2.svg)](https://hub.docker.com/r/frk1/steamhourboostv2/) [![GitHub stars](https://img.shields.io/github/stars/frk1/steamhourboostv2.svg?style=social&label=Stars)](https://github.com/frk1/steamhourboostv2) [![GitHub forks](https://img.shields.io/github/forks/frk1/steamhourboostv2.svg?style=social&label=Fork)](https://github.com/frk1/steamhourboostv2) ## steamhourboost v2 A new version of an application for boosting the hours of Steam games. The new version supports two-factor authentication using a shared secret key. New users can be added to the `config/database.json`. Just run `npm run user` and follow the instructions. By default the application will boost CS 1.6 and CS:GO. The boosted game can be changed by editing the `config/database.json`. ### Installation Install a (version `>= 8.9.0`) `node` and `pm2`. Clone the steamhourboostv2 repository and install dependencies: ```bash apt-get update -yq && \ apt-get install -yq git make curl && \ curl -L https://git.io/n-install | N_PREFIX=~/.n bash -s -- -y latest && \ source /root/.bashrc && \ npm install -g pm2 yarn && \ cd ~ && \ git clone https://github.com/frk1/steamhourboostv2.git && \ cd steamhourboostv2 && \ yarn install --production && \ clear && \ echo "Done. Run 'npm run user' to add users!" ``` ### Usage Accounts can be added with `npm run user`. Start the script using `pm2`: ```bash # Start boosting and telegram bot npm run pm2 # Alternate command pm2 start lib/app.js ``` To start the application **without** pm2: ```bash # Start boosting and telegram bot. npm run app # Alternate command node lib/app.js ``` ### The database.json format The configuration files are saved in the `config` subfolder. If files need to be edited, remember to also edit the files **inside the config folder**! steamhourboost will automatically convert the old database and move it if necessary. A backup of the old database will be created as `database.json.bak`. ### Restarting the application Just do the following: ```bash pm2 restart all ``` If multiple processes are running, the ID of the process to be restarted must be specified. The ID can be found using: ```bash pm2 ls ``` ### Docker [Steamhourboost](https://hub.docker.com/r/frk1/steamhourboostv2/) can be used Docker. Copy the `docker-compose.yml` and run it. The `develop` branch is selected by default. To change branches, edit the compose file to target the `latest` tag. The images are based on [mhart/alpine-node](https://github.com/mhart/alpine-node) and are as minimal as possible. ## Telegram Bot An optional telegram bot is included to generate 2FA tokens using telegram. To use the Telegram Bot, [acquire a bot token from *@BotFather*](https://core.telegram.org/bots#6-botfather). The first execution of steamhourboost will create an empty `config/telebot.json`. Set the bot token and restart the application. To find out the id, write anything to the bot. Set the id as `admin_id` in the `config/telebot.json` and restart the application. That's it! The bot is now waiting for requests. Ask him about tokens! Just enter the username (or something close similar) and it will generate the key: ![telebot](https://raw.githubusercontent.com/frk1/steamhourboostv2/master/docs/telebot.gif) ================================================ FILE: docker-compose.yml ================================================ version: '3' services: hourboost: image: frk1/steamhourboostv2:develop restart: always volumes: - "./config:/app/config" ================================================ FILE: lib/app.js ================================================ "use strict" const _ = require("lodash") const R = require("ramda") const Promise = require("bluebird") const SteamAccount = require("./steamaccount") const migrate = require("./migrate") const manageDB = require("./database") migrate() const database = manageDB.read() const telebot = require("./telebot") telebot() if (database.length === 0) { console.error( "[!] No accounts have been added! Please run 'npm run user' to add accounts!" ) process.exit(0) } const pad = 24 + _.maxBy(R.pluck("name", database), "length").length const accounts = database .map(({ name, password, sentry, secret, games = [] }) => { return games.length > 0 ? new SteamAccount(name, password, sentry, secret, games, pad) : null }) .filter(val => val !== null) const restartBoost = () => { console.log("[=] Restart boosting") return Promise.map(accounts, _.method("restartGames")) .delay(1800000) .finally(restartBoost) } console.log("[=] Start boosting") Promise.map(accounts, _.method("boost")) .delay(1800000) .then(restartBoost) ================================================ FILE: lib/database.js ================================================ "use strict" const fs = require("fs-extra") const approot = require("app-root-path") const write = (obj, path = "config/database.json") => fs.writeJsonSync(approot.resolve(path), obj, { spaces: 2, EOL: "\n" }) const read = (path = "config/database.json") => fs.readJsonSync(approot.resolve(path)) module.exports = { read, write } ================================================ FILE: lib/migrate.js ================================================ "use strict" const _ = require("lodash") const fs = require("fs-extra") const approot = require("app-root-path") const manageDB = require("./database") const moveFileToConfigFolder = filename => { const path = approot.resolve(filename) const pathDest = approot.resolve(`config/${filename}`) if (fs.pathExistsSync(path)) { fs.moveSync(path, pathDest, { overwrite: true }) return console.log(`[+] Moved '${filename}' to config folder!`) } } const convertOldDatabaseFormat = () => { const path = approot.resolve("config/database.json") if (!fs.pathExistsSync(path)) return manageDB.write([]) let database = manageDB.read() if (!_.isPlainObject(database)) return manageDB.write(database, "config/database.json.bak") database = _.map(database, (data, name) => { data.name = name return data }) manageDB.write(database) return console.log( "[+] Converted database to new format! The old one has been backuped as database.json.bak" ) } module.exports = () => { ["database.json", "telebot.json"].forEach(moveFileToConfigFolder) return convertOldDatabaseFormat() } ================================================ FILE: lib/steamaccount.js ================================================ "use strict" const _ = require("lodash") const SteamUser = require("steam-user") const SteamTotp = require("steam-totp") const Promise = require("bluebird") const moment = require("moment") const EventEmitter = require("events") module.exports = class SteamAccount extends EventEmitter { constructor(name, password, sentry, secret, games, indent = 0) { super() this.name = name this.password = password this.sentry = sentry this.secret = secret this.games = games this.indent = indent const options = { promptSteamGuardCode: false, dataDirectory: null } this.client = new SteamUser(null, options) if (this.sentry) this.client.setSentry(Buffer.from(this.sentry, "base64")) this.client.on("error", err => this.emit("clientError", err)) this.client.on("steamGuard", () => this.emit("clientSteamGuard")) this.client.once("steamGuard", () => (this.steamGuardRequested = true)) } logheader() { return _.padEnd( `[${moment().format("YYYY-MM-DD HH:mm:ss")} - ${this.name}]`, this.indent ) } error(err) { return this.emit("customError", err) } login() { if (this.client.client.loggedOn) return Promise.resolve() if (this.steamGuardRequested && !this.secret) return Promise.reject("Steam guard requested!") return new Promise((resolve, reject) => { this.once("clientError", reject) this.once("clientSteamGuard", () => reject("Steam guard requested!")) this.client.once("loggedOn", resolve) if (this.secret) return SteamTotp.getAuthCode(this.secret, (err, code) => { return this.client.logOn({ accountName: this.name, password: this.password, twoFactorCode: code }) }) return this.client.logOn({ accountName: this.name, password: this.password }) }) .timeout(10000) .catch(Promise.TimeoutError, () => Promise.reject("Timed out at login")) .finally(() => { this.removeAllListeners("clientError") this.removeAllListeners("clientSteamGuard") return this.client.removeAllListeners("loggedOn") }) } logoff() { if (!this.client.client.loggedOn) return this.client.gamesPlayed([]) return this.client.logOff() } boost() { return this.login() .then(() => { this.client.setPersona(SteamUser.EPersonaState.Offline) this.client.gamesPlayed(this.games !== null ? this.games : [10, 730]) return console.log(`${this.logheader()} Starting to boost games!`) }) .catch(err => console.error(`${this.logheader()} ${err}`)) } restartGames() { this.client.gamesPlayed([]) return Promise.delay(5000).then(() => this.boost()) } } ================================================ FILE: lib/telebot.js ================================================ "use strict" const R = require("ramda") const Fuse = require("fuse.js") const Promise = require("bluebird") const moment = require("moment") const Telegraf = require("telegraf") const manageDB = require("./database") const database = manageDB.read() const totp = Promise.promisify(require("steam-totp").getAuthCode) let token, admin_id try { ({ token, admin_id } = manageDB.read("config/telebot.json")) } catch (e) { token = "" admin_id = 0 manageDB.write({ token, admin_id }, "config/telebot.json") } const validate_admin = (ctx, next) => { if (ctx.from.id === admin_id) return next() console.error(`[#] [${moment().format()}] ${ctx.from.id} tried to request!`) return ctx.replyWithMarkdown(`\ You *do not* have the permission to use this bot! Your telegram id is: \`${ctx.from.id}\`\ `) } const bot = new Telegraf(token) bot.use(validate_admin) bot.command("cancel", ctx => ctx.scene.leave()) const fuzzy = (query = "") => { if (database.length === 0) return [] if (query.length === 0) return R.pluck("name", database) const fuse = new Fuse(database, { id: "name", keys: ["name"], shouldSort: true, threshold: 0.4, location: 0, distance: 100, maxPatternLength: 32, minMatchCharLength: 0 }) return fuse.search(query) } bot.command("list", ctx => { const query = ctx.message.text.length < 6 ? "" : ctx.message.text.substr(6) let fmt = "The following accounts are available:\n" fuzzy(query).map(a => (fmt += `\`- ${a}\`\n`)) return ctx.replyWithMarkdown(fmt) }) bot.hears(/(.*)/, ctx => { const [name] = Array.from(fuzzy(ctx.message.text)) if (name) { const acc = R.find(R.propEq("name", name), database) return totp(acc.secret) .then(code => ctx.replyWithMarkdown(`\ Selected account *${acc.name}*. 2FA-Code: \`${code}\`\ `) ) .catch(err => ctx.replyWithMarkdown(`\ Selected account *${acc.name}*. Error: \`${err}\`\ `) ) } return ctx.replyWithMarkdown(`\ Could not find a matching account! Try */list* to get a list of available accounts!\ `) }) module.exports = () => { if (token === "") return console.log( "[!] If you want to use the telegram bot please set a valid token in telebot.json!" ) console.log("[=] Start Telegram bot") return bot.startPolling() } ================================================ FILE: lib/user.js ================================================ "use strict" const R = require("ramda") const SteamUser = require("steam-user") const SteamTotp = require("steam-totp") const inquirer = require("inquirer") const migrate = require("./migrate") const manageDB = require("./database") migrate() const database = manageDB.read() const promptGames = { type: "checkbox", name: "games", message: "Select the games to boost:", choices: [ { value: 10, name: "CS 1.6", checked: true }, { value: 730, name: "CS:GO", checked: true }, { value: 570, name: "DOTA2" } ] } inquirer .prompt([ { name: "username", message: "Username:" }, { name: "password", message: "Password:", type: "password" } ]) .then(({ username, password }) => { let index = R.findIndex(R.propEq("name", username), database) if (index === -1) { database.push({ name: username, password }) index = R.findIndex(R.propEq("name", username), database) } const client = new SteamUser() client.setOption("promptSteamGuardCode", false) client.setOption("dataDirectory", null) client.logOn({ accountName: username, password }) client.on("steamGuard", (domain, callback) => { if (domain) return inquirer .prompt([{ name: "code", message: `Steam guard code (${domain}):` }]) .then(({ code }) => callback(code)) return inquirer .prompt([ { name: "secret", message: "Two-factor shared secret:", type: "password" } ]) .then(({ secret }) => SteamTotp.generateAuthCode(secret, (err, code) => { database[index].secret = secret return callback(code) }) ) }) client.on("sentry", sentry => { database[index].sentry = sentry.toString("base64") return manageDB.write(database) }) client.on("loggedOn", () => { database[index].password = password return inquirer.prompt(promptGames).then(({ games }) => { database[index].games = games manageDB.write(database) return process.exit(0) }) }) return client.on("error", err => { console.log(`Error: ${err}`) return process.exit(1) }) }) ================================================ FILE: package.json ================================================ { "name": "steamhourboostv2", "version": "4.1.3", "description": "Steam hour boost with two factor support", "main": "lib/app.js", "scripts": { "app": "node lib/app.js", "boost": "node lib/app.js", "user": "node lib/user.js", "pm2": "pm2 start lib/app.js", "pretty": "prettier-eslint lib/*.js --write", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/frk1/steamhourboostv2.git" }, "keywords": [ "steam", "boost", "csgo" ], "author": "frk ", "license": "MIT", "bugs": { "url": "https://github.com/frk1/steamhourboostv2/issues" }, "homepage": "https://github.com/frk1/steamhourboostv2#readme", "dependencies": { "app-root-path": "^2.0.1", "bluebird": "^3.5.1", "fs-extra": "^5.0.0", "fuse.js": "^3.2.0", "inquirer": "^5.2.0", "lodash": "^4.17.10", "moment": "^2.22.1", "ramda": "^0.25.0", "steam-totp": "^2.1.0", "steam-user": "^3.27.1", "telegraf": "^3.15.3" }, "engines": { "node": ">=8.9.0" }, "devDependencies": { "eslint": "^4.11.0", "eslint-config-recommended": "^2.0.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-node": "^6.0.1", "prettier": "^1.8.2", "prettier-eslint-cli": "^4.4.0" } }