Guides
June 27, 2021

SteamBot, part 2: 24/7 uptime

В предыдущей статье я рассказал про создание бота для Termux.
Но Термукс - херня собачья, мне лень каждый раз что-то запускать и ставить телефон на зарядку. Нужно что-то иное...

Для настройки нам понадобятся:

  • VSCode или любой текстовый редактор (писать в Notepad++ на JS неудобно)
  • NodeJS, да-да... да... Вообще, можно и без него, но дебажить легче на ПК.
  • SDA он же Steam Desktop Authenticator
  • Heroku аккаунт для хостинга бота
  • GitHub аккаунт для пушей, потому что мне так удобней

Подготовка

Первым делом создаём аккаунты и качаем SDA, нам же нужен наш токен.

Если SteamGuard привязан к смартфону на Android OS, вот вам памятка:

Ну либо просто отвяжите Guard и прикрепите его к SDA, 15 дней бана на тп

Должно получиться что-то такое. Собсна, на боте у меня 2 аккаунта, потому оба привязал к Desktop версии

Когда все нужные аккаунты привязаны к SDA, создаём репозиторий на GitHub. Как и куда — найдёте в глобальной.

Клонируете репозиторий на ПК, либо создаете локальный с вашей машины (который потом не забудьте запушить в origin)


Настройка. Ну или вторая подготовка, мне по**й

В этот раз обойдёмся двумя файлами, на ПК запускать нет необходимости, трансферить на смартфон — тоже.

Первый файл, main он же index.js:

const clientBuilder = require('./config');

let clientArray = []

const configsArray = [{

	login: "login",
	password: "pass",
	sharedSecret: "token",
	games: [
		381210 // Dead by Daylight
	]
},
{
	login: "login2",
	password: "pass2",
	sharedSecret: "token2",
	games: [
		570, 	// Dota 2
		548430,	// Deep Rock Galactic
		4000,	// Gmod
	]
}]


console.log('Bot number: ' + configsArray.length);

for (let config of configsArray) {

	let client = clientBuilder.execute(config);
	client.doLogin();
	clientArray.push(client);
}
console.log('Running ' + configsArray.length + ' bots.');

Файл выше собран под мои нужды, поэтому в массиве два элемента: первый аккаунт и второй.

Самое интересное для нас - это token, он же sharedSecret. Его мы берём из файлов SDA. Если ставили пароль на SDA, то на время придётся снять.

Открываем папку SDA/maFiles

Внутри находим значение shared_secret и копируем. Это и есть наш token.

Вставляем его в файл, соблюдая синтаксис (кавычки, запятые, скобки). ID нужных игр для значения games берём из раздела "Параметры" желаемой игры.

Офк я не кручу часы в Лоботомии, просто она была запущена последней, хули выбирать-то...

Заполняем файл config.js. Actually, он должен быть нашим main, но сделать export оказалось проще:

const SteamUser = require('steam-user');
const SteamTotp = require('steam-totp');

let newClient = {};

newClient.execute = function (config) {

	let client = new SteamUser({

		// Никакого SteamGuard, как я и говорил
		promptSteamGuardCode: false,
		dataDirectory: "./sentry",
		singleSentryfile: false
	});

	client.login = config.login;
	client.password = config.password;
	client.sharedSecret = config.sharedSecret;
	client.games = config.games;
	client.messageReceived = {};

	client.on('loggedOn', function (details) {
		console.log("[" + this.login + "] Logged into Steam as " + client.steamID.getSteam3RenderedID());
		client.setPersona(SteamUser.EPersonaState.Snooze); // Set steam status [25-34 for all possible variants]
		client.gamesPlayed(this.games);
	});
	/* Value-to-name mapping for convenience
	"0": "Offline",
	"1": "Online",
	"2": "Busy",
	"3": "Away",
	"4": "Snooze",
	"5": "LookingToTrade",
	"6": "LookingToPlay",
	"7": "Invisible",
	*/


	client.on('error', function (err) {
		console.log("[" + this.login + "] " + err);
		setTimeout(function () { client.doLogin(); }, 30 * 60 * 1000);
		// Время в ms, в течение которого бот будет афк. 1000 ms. * 60 sec. * 30 min. = 18000000 ms. = 30 min. Меняем первое значение, если хотим изменить минуты.
	});

	client.doLogin = function () {
		this.logOn({
			"accountName": this.login,
			"password": this.password
		});
	}

	client.on('steamGuard', function (domain, callback) {
		if (!this.sharedSecret) {

			// Никаких запросов SteamGuard, нам это не подходит. Полная автоматика.
			console.log("Seems like you forgot about SharedSecret. Terminating the process...");
			return;
		}
		else {
			var authCode = SteamTotp.generateAuthCode(this.sharedSecret);
			console.log("[" + this.login + "] Generated Auth Code: " + authCode);
			callback(authCode);
		}

	});

	client.on("friendMessage", function (steamID, message) {
		console.log("[" + this.login + "] Message from " + steamID + ": " + message);
		if (!this.messageReceived[steamID]) {

			// Мягко посылаем нашего другана, у нас тут часы крутятся в конце-то концов. Пусть напишет в другой чат.
			client.chatMessage(steamID, "[Automated Response] I am idling. DM me in Discord or VK.");
			this.messageReceived[steamID] = true;
		}
	});


	client.on('vacBans', function (numBans, appids) {
		if (numBans > 0) {

			// Показывает баны аккаунта, если есть. Спасибо тернарному "?" за сокращение строк.
			console.log("[" + this.login + "] " + numBans + " VAC ban" + (numBans == 1 ? '' : 's') + "." +
				(appids.length == 0 ? '' : " In apps: " + appids.join(', ')));
		}
	});

	client.on('accountLimitations', function (limited, communityBanned, locked, canInviteFriends) {
		var limitations = [];

		if (limited) {
			limitations.push('LIMITED');
		}

		if (communityBanned) {
			limitations.push('COMMUNITY BANNED');
		}

		if (locked) {
			limitations.push('LOCKED');
		}

		if (limitations.length !== 0) {
			console.log("[" + this.login + "] Limitations: " + limitations.join(', ') + ".");
		}
	});

	return client;
}

module.exports = newClient;

Все нужные функции прокомментированы, изучайте и настраивайте.


3... 2... 1... Push!

Создаём в файловой системе бота файл Procfile

Внутри пишем

Worker: node .
либо
Worker: node index.js

Пушим всё это дело в наш репозиторий. Либо через VSCode, либо через Github Dekstop, как вам угодно.

Если кто вдруг не работал с VSCode, то запушить можно через меню в левом углу редактора.

Переходим к Heroku. Надо же бота запустить...

Создаём новое приложение, даём ему название и выбираем сервак, рекомендую Европу, легче пинговать.

Далее коннектим Heroku и GitHub между собой, дабы была возможность автоматически развёртывать новые билды в облако (вдруг список игр решите поменять, в таком случае будет достаточно запушить коммит, Heroku сам соберёт билд)

Добавляем Buildpack во вкладке settings. Нам нужен именно heroku/nodejs

Жмём Enable Automatic Deploys, если хотите автоматизации, о которой я упомянул выше.

Далее Deploy Branch, если не началось развёртывание ветки

Можем запускать бота и следить за его работой через логи

Либо можете подключиться к боту через Heroku CLI, залогинившись командой

 heroku login

Если по какой-то причине бот крашнул, заходим во вкладку Resources и переключаем

web => Worker

Готово.

Бот будет работать 24/7.

Если захотите сыграть, но вам высвечивается уведомление "Ваш аккаунт занят" => жмите продолжить, бота кикнет, а каждые 30 минут он будет пробовать повторное подключение.

Т.е. как только вы закончите играть, он вернётся к фарму.


Репозиторий для примера, почему бы и да