├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── components ├── Client_440.js ├── Client_730.js ├── Client_Shared.js ├── Server_440.js ├── Server_730.js └── Server_Shared.js ├── config.json.example ├── helpers ├── Coordinator.js ├── EventTypes.js ├── Helper.js └── Protobufs.js ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | config.json 4 | private 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protobufs"] 2 | path = protobufs 3 | url = https://github.com/SteamDatabase/Protobufs.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Isla 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fake Stattrak 2 | 3 | Apply fake kills to your stattrak and strange weapons in CSGO and TF2. 4 | 5 | *I will be using "StatTrak", "CSGO" and "Kill" throughout this README but it also applies to TF2 and any Strange filter* 6 | 7 | **While Valve has not banned anyone for modifying their items like this they can ban you if they want to. Use at your own risk. I am not responsible for any damages.** 8 | 9 | --- 10 | 11 | ## How does it work? 12 | 13 | StatTrak kills count on community servers too, so there logically are only two ways Valve would possibly know that you killed someone: Either the client sends something to let the game know you got a kill or the server does it. So I researched and turns out the server sends something telling the game you got a kill. 14 | 15 | This script creates a fake server and fake joins it with a bot account and the account you want to boost a StatTrak weapon on. Valve will think its a real match going on and will allow our fake server to send StatTrak increments. 16 | 17 | These StatTrak increments basically just tell Valve to update the item and change the kill-count on it. 18 | 19 | ## Requirements 20 | 21 | - [NodeJS](https://nodejs.org/en/) - `v18.10.0` or later 22 | - [A bit of JSON knowledge](https://www.json.org/) 23 | - A bot account 24 | 25 | ## Usage 26 | 27 | 1. Download this repository 28 | 2. Rename `config.json.example` to `config.json` 29 | 3. Adjust your config - [More Info](#config) 30 | 4. Open a command prompt inside the folder 31 | 5. Run `npm install` to install all dependencies 32 | 6. Exit out of Steam - [Read Why](#valve-anti-cheat) 33 | 7. Run `node index.js` 34 | 35 | ## Config 36 | 37 | - `boostingAccount`: Object - Account details of the account with the item you want to boost 38 | - `username`: String - Login username 39 | - `password`: String - Login password 40 | - `botAccount`: Object - Account details of any account 41 | - `username`: String - Login username 42 | - `password`: String - Login password 43 | - `itemID`: String - Item ID of the item you want to boost - [How to find the item ID](#find-item-id) 44 | - `appID`: Number - ID of the game your item is from - *Currently only CSGO (`730`) and TF2 (`440`) are supported* 45 | - `eventType`: Number - The event type which defines what stat on an item gets changed - [More Info](#event-type) 46 | - `incrementValue`: Number - How much you want to add to the current item 47 | - **Note:** Many changes have been made behind the scenes, this might not properly work! If you have problems please open a new [Issue on Github](https://github.com/BeepIsla/fake-stattrak/issues) 48 | 49 | ## Valve Anti-Cheat 50 | 51 | VAC is a client-side Anti-Cheat, VAC never gets enabled with this. The only thing you can recieve is a `VAC was unable to verify your game session.` or `You cannot play on secure servers for one of the following reasons`. This happens when you are logged into Steam while using this. Simply exit Steam or log out of your account before running this. Once the script is done you can start Steam again. VAC errors have nothing to do with using this, to fix the errors above just follow [Steam's Support Article](https://support.steampowered.com/kb_article.php?ref=2117-ilzv-2837). 52 | 53 | ## Find Item ID 54 | 55 | To find your item ID go to [your inventory](http://steamcommunity.com/my/inventory) and search the item you want to boost, right click it and click `Copy link address`. You will get something like this: `/inventory/#440_2_143113807`, this is the schema it follows: `AppID_Context_ItemID`. Context is irrelevant for you, only AppID and ItemID matter. In this case AppID is `440` and ItemID is `143113807`. 56 | 57 | ## Event Type 58 | 59 | An event type tells Steam what statistic on a weapon you want to modify, this is important because some items have multiple different counters. Here is a list of all event types I am aware of and their meaning **(Last Updated: 9th March 2020)** 60 | 61 | **Note:** Some my not work due to them being only counted on official servers, one example is the MVP counter on music kits. I have attempted to automate everything, including splitting StatTrak increases, and detecting "Official Server Only" event types. This is not perfect so some may not work. 62 | 63 |
64 | Counter-Strike: Global Offensive 65 | 66 | | Type ID | Name | Internal Name | 67 | |---------|-------------------------------------|---------------| 68 | | 0 | StatTrak™ Confirmed Kills | Kills | 69 | | 1 | StatTrak™ Official Competitive MVPs | OCMVPs | 70 |
71 | 72 |
73 | Team Fortress 2 74 | 75 | | Type ID | Name | Internal Name | 76 | |---------|-----------------------------------------|-------------------------------------| 77 | | 0 | Kills | Kills | 78 | | 1 | Ubers | Ubers | 79 | | 2 | Kill Assists | KillAssists | 80 | | 3 | Sentry Kills | SentryKills | 81 | | 4 | Sodden Victims | PeeVictims | 82 | | 5 | Spies Shocked | BackstabsAbsorbed | 83 | | 6 | Heads Taken | HeadsTaken | 84 | | 7 | Humiliations | Humiliations | 85 | | 8 | Gifts Given | GiftsGiven | 86 | | 9 | Deaths Feigned | FeignDeaths | 87 | | 10 | Scouts Killed | ScoutsKilled | 88 | | 11 | Snipers Killed | SnipersKilled | 89 | | 12 | Soldiers Killed | SoldiersKilled | 90 | | 13 | Demomen Killed | DemomenKilled | 91 | | 14 | Heavies Killed | HeaviesKilled | 92 | | 15 | Pyros Killed | PyrosKilled | 93 | | 16 | Spies Killed | SpiesKilled | 94 | | 17 | Engineers Killed | EngineersKilled | 95 | | 18 | Medics Killed | MedicsKilled | 96 | | 19 | Buildings Destroyed | BuildingsDestroyed | 97 | | 20 | Projectiles Reflected | ProjectilesReflected | 98 | | 21 | Headshot Kills | HeadshotKills | 99 | | 22 | Airborne Enemy Kills | AirborneEnemyKills | 100 | | 23 | Gib Kills | GibKills | 101 | | 24 | Buildings Sapped | BuildingsSapped | 102 | | 25 | Tickle Fights Won | PlayersTickled | 103 | | 26 | Opponents Flattened | MenTreaded | 104 | | 27 | Kills Under A Full Moon | KillsDuringFullMoon | 105 | | 28 | Dominations | StartDominationKills | 106 | | 30 | Revenges | RevengeKills | 107 | | 31 | Posthumous Kills | PosthumousKills | 108 | | 32 | Teammates Extinguished | AlliesExtinguished | 109 | | 33 | Critical Kills | CriticalKills | 110 | | 34 | Kills While Explosive-Jumping | KillsWhileExplosiveJumping | 111 | | 36 | Sappers Removed | SapperDestroyed | 112 | | 37 | Cloaked Spies Killed | InvisibleSpiesKilled | 113 | | 38 | Medics Killed That Have Full ÜberCharge | MedicsWithFullUberKilled | 114 | | 39 | Robots Destroyed | RobotsKilled | 115 | | 40 | Giant Robots Destroyed | MinibossRobotsKilled | 116 | | 44 | Kills While Low Health | LowHealthKill | 117 | | 45 | Kills During Halloween | HalloweenKills | 118 | | 46 | Robots Killed During Halloween | HalloweenRobotKills | 119 | | 47 | Defenders Killed | DefenderKills | 120 | | 48 | Submerged Enemy Kills | UnderwaterKills | 121 | | 49 | Kills While Invuln ÜberCharged | KillsWhileUbercharged | 122 | | 50 | Food Items Eaten | FoodEaten | 123 | | 51 | Banners Deployed | BannersDeployed | 124 | | 58 | Seconds Cloaked | TimeCloaked | 125 | | 59 | Health Dispensed to Teammates | HealthGiven | 126 | | 60 | Teammates Teleported | TeleportsGiven | 127 | | 61 | Tanks Destroyed | TanksDestroyed | 128 | | 62 | Long-Distance Kills | LongDistanceKills | 129 | | 64 | Points Scored | PointsScored | 130 | | 65 | Double Donks | DoubleDonks | 131 | | 66 | Teammates Whipped | TeammatesWhipped | 132 | | 67 | Kills during Victory Time | VictoryTimeKill | 133 | | 68 | Robot Scouts Destroyed | RobotScoutKill | 134 | | 74 | Robot Spies Destroyed | RobotSpyKill | 135 | | 77 | Taunt Kills | TauntKill | 136 | | 78 | Unusual-Wearing Player Kills | PlayerWearingUnusualKill | 137 | | 79 | Burning Player Kills | BurningPlayerKill | 138 | | 80 | Killstreaks Ended | KillstreaksEnded | 139 | | 81 | Freezecam Taunt Appearances | KillcamTaunts | 140 | | 82 | Damage Dealt | DamageDealt | 141 | | 83 | Fires Survived | FiresSurvived | 142 | | 84 | Allied Healing Done | AllyHealingDone | 143 | | 85 | Point Blank Kills | PointBlankKill | 144 | | 86 | Wrangled Sentry Kills | PlayerKillsBySentry | 145 | | 87 | Kills | CosmeticKills | 146 | | 88 | Full Health Kills | FullHealthKills | 147 | | 89 | Taunting Player Kills | TauntingPlayerKills | 148 | | 90 | Carnival Kills | HalloweenOverworldKills | 149 | | 91 | Carnival Underworld Kills | HalloweenUnderworldKills | 150 | | 92 | Carnival Games Won | HalloweenMinigamesWon | 151 | | 93 | Not Crit nor MiniCrit Kills | NonCritKills | 152 | | 94 | Players Hit | PlayersHit | 153 | | 95 | Assists | CosmeticAssists | 154 | | 96 | Contracts Completed | CosmeticOperationContractsCompleted | 155 | | 97 | Kills | CosmeticOperationKills | 156 | | 98 | Contract Points | CosmeticOperationContractsPoints | 157 | | 99 | Contract Bonus Points | CosmeticOperationBonusObjectives | 158 | | 100 | Times Performed | TauntsPerformed | 159 | | 101 | Kills and Assists during Invasion Event | InvasionKills | 160 | | 102 | Kills and Assists on 2Fort Invasion | InvasionKillsOnMap01 | 161 | | 103 | Kills and Assists on Probed | InvasionKillsOnMap02 | 162 | | 104 | Kills and Assists on Byre | InvasionKillsOnMap03 | 163 | | 105 | Kills and Assists on Watergate | InvasionKillsOnMap04 | 164 | | 106 | Souls Collected | HalloweenSouls | 165 | | 107 | Merasmissions Completed | HalloweenContractsCompleted | 166 | | 108 | Halloween Transmutes Performed | HalloweenOfferings | 167 | | 109 | Power Up Canteens Used | PowerupBottlesUsed | 168 | | 110 | Contract Points Earned | ContractPointsEarned | 169 | | 111 | Contract Points Contributed To Friends | ContractPointsContributedToFriends | 170 |
171 | -------------------------------------------------------------------------------- /components/Client_440.js: -------------------------------------------------------------------------------- 1 | const ClientShared = require("./Client_Shared.js"); 2 | 3 | module.exports = class TF2Client extends ClientShared { 4 | constructor() { 5 | super(440); 6 | } 7 | 8 | login(username, password) { 9 | return new Promise(async (resolve, reject) => { 10 | try { 11 | let data = await super.login(username, password); 12 | 13 | // Finalizing 14 | await this.coordinator.sendMessage( 15 | 440, 16 | this.protobufs.data.tf2.ETFGCMsg.k_EMsgGC_TFClientInit, 17 | { 18 | steamid: this.client.steamID.getSteamID64(), 19 | client_sessionid: this.client._sessionID 20 | }, 21 | this.protobufs.encodeProto("CMsgTFClientInit", { 22 | client_version: this.clientVersion, 23 | language: 0 24 | }) 25 | ); 26 | 27 | await this.coordinator.sendMessage( 28 | 440, 29 | this.protobufs.data.tf2.ESOMsg.k_ESOMsg_CacheSubscriptionRefresh, 30 | { 31 | steamid: this.client.steamID.getSteamID64(), 32 | client_sessionid: this.client._sessionID 33 | }, 34 | this.protobufs.encodeProto("CMsgSOCacheSubscriptionRefresh", { 35 | owner: this.steamID.getSteamID64() 36 | }) 37 | ); 38 | 39 | // Done 40 | resolve(data); 41 | } catch (err) { 42 | reject(err); 43 | } 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /components/Client_730.js: -------------------------------------------------------------------------------- 1 | const StdLib = require("@doctormckay/stdlib"); 2 | const ClientShared = require("./Client_Shared.js"); 3 | 4 | module.exports = class CSGOClient extends ClientShared { 5 | constructor() { 6 | super(730); 7 | } 8 | 9 | login(username, password) { 10 | return new Promise(async (resolve, reject) => { 11 | try { 12 | let data = await super.login(username, password); 13 | 14 | // Finalizing 15 | let mmWelcome = await this.coordinator.sendMessage( 16 | 730, 17 | this.protobufs.data.csgo.ECsgoGCMsg.k_EMsgGCCStrike15_v2_MatchmakingClient2GCHello, 18 | { 19 | steamid: this.client.steamID.getSteamID64(), 20 | client_sessionid: this.client._sessionID 21 | }, 22 | this.protobufs.encodeProto("CMsgGCCStrike15_v2_MatchmakingClient2GCHello", {}), 23 | this.protobufs.data.csgo.ECsgoGCMsg.k_EMsgGCCStrike15_v2_MatchmakingGC2ClientHello, 24 | 5000 25 | ); 26 | mmWelcome = mmWelcome instanceof Buffer ? this.protobufs.decodeProto("CMsgGCCStrike15_v2_MatchmakingGC2ClientHello", mmWelcome) : mmWelcome; 27 | this.mmWelcome = mmWelcome; 28 | 29 | // Done 30 | resolve(data); 31 | } catch (err) { 32 | reject(err); 33 | } 34 | }); 35 | } 36 | 37 | joinServer(serverID, appTicket) { 38 | return new Promise(async (resolve, reject) => { 39 | try { 40 | let serverInfoFails = 0; 41 | let serverInfo = null; 42 | while (!serverInfo) { 43 | let servers = await this.client.getServerList("\\steamid\\" + serverID + "\\", 10); 44 | serverInfo = servers.servers ? servers.servers[0] : undefined; 45 | 46 | if (++serverInfoFails >= 10 && !serverInfo) { 47 | reject(new Error("Failed to fetch server information")); 48 | return; 49 | } else if (!serverInfo) { 50 | await new Promise(p => setTimeout(p, 5000)); 51 | } 52 | } 53 | 54 | let serverIP = serverInfo.addr.split(":").shift(); 55 | let serverPort = serverInfo.addr.split(":").pop(); 56 | 57 | await this.coordinator.sendMessage( 58 | 730, 59 | this.protobufs.data.csgo.ECsgoGCMsg.k_EMsgGCCStrike15_v2_ClientRequestJoinServerData, 60 | {}, 61 | this.protobufs.encodeProto("CMsgGCCStrike15_v2_ClientRequestJoinServerData", { 62 | version: this.clientVersion, 63 | account_id: this.steamID.accountid, 64 | serverid: serverID, 65 | server_ip: StdLib.IPv4.stringToInt(serverIP), 66 | server_port: serverPort 67 | }) 68 | ); 69 | 70 | let data = await super.joinServer(serverID, appTicket); 71 | resolve(data); 72 | } catch (err) { 73 | reject(err); 74 | } 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /components/Client_Shared.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const Events = require("events"); 3 | const SteamUser = require("steam-user"); 4 | const StdLib = require("@doctormckay/stdlib"); 5 | const Protobufs = require("../helpers/Protobufs.js"); 6 | const Coordinator = require("../helpers/Coordinator.js"); 7 | const Helper = require("../helpers/Helper.js"); 8 | const validAppIDs = [440, 730]; 9 | const appIdProtobufs = { 10 | "440": { 11 | name: "tf2", 12 | protos: path.join(__dirname, "..", "protobufs", "tf2") 13 | }, 14 | "730": { 15 | name: "csgo", 16 | protos: path.join(__dirname, "..", "protobufs", "csgo") 17 | } 18 | }; 19 | const versionNiceAppIdParser = { 20 | "440": function (version) { 21 | return version; 22 | }, 23 | "730": function (verion) { 24 | return verion[0] + "." + verion[1] + verion[2] + "." + verion[3] + "." + verion[4]; 25 | } 26 | }; 27 | 28 | module.exports = class ClientShared extends Events { 29 | constructor(appID) { 30 | super(); 31 | 32 | if (!validAppIDs.includes(appID)) { 33 | throw new Error("Shared server structure not setup to support app ID " + appID); 34 | } 35 | this.appID = appID; 36 | 37 | this.protobufs = new Protobufs([ 38 | { 39 | name: "steam", 40 | protos: path.join(__dirname, "..", "protobufs", "steam") 41 | }, 42 | appIdProtobufs[this.appID] 43 | ]); 44 | 45 | this.client = new SteamUser(); 46 | this.coordinator = new Coordinator(this.client, this.appID); 47 | 48 | this.coordinator.on("receivedFromSteam", this._receivedFromSteam.bind(this)); 49 | this.coordinator.on("receivedFromGC", this._receivedFromGameCoordinator.bind(this)); 50 | 51 | this.clientVersion = 0; 52 | this.clientVersionNice = 0; 53 | 54 | this.serverInfo = null; 55 | this.gcWelcome = null; 56 | } 57 | 58 | get steamID() { 59 | return this.client.steamID; 60 | } 61 | 62 | login(username, password) { 63 | return new Promise(async (resolve, reject) => { 64 | try { 65 | let version = await Helper.GetSteamAPI("ISteamApps", "UpToDateCheck", "v1", { 66 | appid: this.appID, 67 | version: 0 68 | }); 69 | if (!version.success) { 70 | reject(new Error(version)); 71 | return; 72 | } 73 | this.clientVersion = String(version.required_version); 74 | this.clientVersionNice = versionNiceAppIdParser[this.appID](this.clientVersion); 75 | this.clientVersion = Number(this.clientVersion); 76 | 77 | // Login 78 | this.client.logOn({ 79 | accountName: username, 80 | password: password 81 | }); 82 | 83 | await new Promise((res, rej) => { 84 | this.client.on("loggedOn", (details) => { 85 | res(); 86 | }); 87 | this.client.on("error", (err) => { 88 | rej(err); 89 | }); 90 | }).finally(() => { 91 | this.client.removeAllListeners("loggedOn"); 92 | this.client.removeAllListeners("error"); 93 | }); 94 | 95 | // Get license and set as online & playing 96 | await this.client.requestFreeLicense([this.appID]); 97 | this.client.setPersona(SteamUser.EPersonaState.Online); 98 | this.client.gamesPlayed({ 99 | game_id: this.appID 100 | }); 101 | 102 | // Get latest version for CS 103 | let gcVersion = undefined; 104 | if (this.appID === 730) { 105 | const text = await fetch("https://raw.githubusercontent.com/SteamDatabase/GameTracking-CS2/master/game/csgo/steam.inf").then(r => r.text()); 106 | const lines = text.split("\n").map(l => l.replace(/\r/g, "").trim()); 107 | const idx = lines.findIndex(l => l.startsWith("ClientVersion=")); 108 | if (idx >= 0) { 109 | const [_, version] = lines[idx].split("="); 110 | gcVersion = parseInt(version); 111 | } 112 | } 113 | 114 | // GC register 115 | let welcomeFails = 0; 116 | let welcome = null; 117 | while (!welcome) { 118 | welcome = await this.coordinator.sendMessage( 119 | this.appID, 120 | 4006, // Always "4006" 121 | {}, 122 | this.protobufs.encodeProto("CMsgClientHello", { 123 | version: gcVersion 124 | }), 125 | 4004, // Always "4004" 126 | 5000 127 | ).catch(() => { }); 128 | 129 | if (++welcomeFails >= 12 && !welcome) { 130 | reject(new Error("Failed to connect to Steam")); 131 | this.logOff(); 132 | return; 133 | } 134 | } 135 | welcome = welcome instanceof Buffer ? this.protobufs.decodeProto("CMsgClientWelcome", welcome) : welcome; 136 | this.gcWelcome = welcome; 137 | 138 | // Done 139 | resolve(true); 140 | } catch (err) { 141 | this.logOff(); 142 | reject(err); 143 | } 144 | }); 145 | } 146 | 147 | _receivedFromSteam(header, body) { 148 | let decoder = ""; 149 | switch (header.msg) { 150 | default: { 151 | break; 152 | } 153 | } 154 | 155 | if (!decoder) { 156 | return; 157 | } 158 | 159 | let obj = this.protobufs.decodeProto(decoder, body); 160 | switch (header.msg) { 161 | default: { 162 | break; 163 | } 164 | } 165 | } 166 | 167 | _receivedFromGameCoordinator(msgType, body) { 168 | let decoder = ""; 169 | switch (msgType) { 170 | default: { 171 | break; 172 | } 173 | } 174 | 175 | if (!decoder) { 176 | return; 177 | } 178 | 179 | let obj = this.protobufs.decodeProto(decoder, body); 180 | switch (msgType) { 181 | default: { 182 | break; 183 | } 184 | } 185 | } 186 | 187 | logOff() { 188 | this.client.logOff(); 189 | 190 | this.clientVersion = 0; 191 | this.clientVersionNice = 0; 192 | 193 | this.serverInfo = null; 194 | this.gcWelcome = null; 195 | } 196 | 197 | async generateTicket() { 198 | let authTicket = await this.client.createAuthSessionTicket(this.appID); 199 | return authTicket.sessionTicket; 200 | } 201 | 202 | joinServer(serverID, appTicket) { 203 | return new Promise(async (resolve, reject) => { 204 | try { 205 | let serverInfoFails = 0; 206 | this.serverInfo = null; 207 | while (!this.serverInfo) { 208 | let servers = await this.client.getServerList("\\steamid\\" + serverID + "\\", 10); 209 | this.serverInfo = servers.servers ? servers.servers[0] : undefined; 210 | 211 | if (++serverInfoFails >= 10 && !this.serverInfo) { 212 | reject(new Error("Failed to fetch server information")); 213 | return; 214 | } else if (!this.serverInfo) { 215 | await new Promise(p => setTimeout(p, 5000)); 216 | } 217 | } 218 | 219 | let ticketCrc = StdLib.Hashing.crc32(appTicket); 220 | 221 | let authList = await this.coordinator.sendMessage( 222 | undefined, 223 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthList, // Do not encode protobuf below like usually, Steam-User automatically does this for us 224 | { 225 | steamid: this.client.steamID.getSteamID64(), 226 | client_sessionid: this.client._sessionID 227 | }, { 228 | tokens_left: this.client._gcTokens.length, 229 | last_request_seq: this.client._authSeqMe, 230 | last_request_seq_from_server: this.client._authSeqThem, 231 | tickets: [ 232 | { 233 | estate: 0, 234 | eresult: 4294967295, 235 | steamid: 0, 236 | gameid: this.appID, 237 | h_steam_pipe: this.client._hSteamPipe, 238 | ticket_crc: ticketCrc, 239 | ticket: appTicket 240 | } 241 | ], 242 | app_ids: [ 243 | this.appID 244 | ], 245 | message_sequence: ++this.client._authSeqMe 246 | }, 247 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthListAck, 248 | 5000 249 | ); 250 | authList = authList instanceof Buffer ? this.protobufs.decodeProto("CMsgClientAuthListAck", authList) : authList; 251 | 252 | this.client.gamesPlayed({ 253 | steam_id_gs: serverID, 254 | game_id: this.appID 255 | }); 256 | 257 | resolve(true); 258 | } catch (err) { 259 | reject(err); 260 | } 261 | }); 262 | } 263 | 264 | leaveServer() { 265 | return new Promise(async (resolve, reject) => { 266 | try { 267 | await this.coordinator.sendMessage( 268 | undefined, 269 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthList, // Do not encode protobuf below like usually, Steam-User automatically does this for us 270 | { 271 | steamid: this.client.steamID.getSteamID64(), 272 | client_sessionid: this.client._sessionID 273 | }, 274 | this.protobufs.encodeProto("CMsgClientAuthList", { 275 | tokens_left: this.client._gcTokens.length, 276 | last_request_seq: this.client._authSeqMe, 277 | last_request_seq_from_server: this.client._authSeqThem, 278 | app_ids: [ 279 | this.appID 280 | ], 281 | message_sequence: ++this.client._authSeqMe 282 | }), 283 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthListAck, 284 | 5000 285 | ); 286 | 287 | this.client.gamesPlayed({ 288 | game_id: this.appID 289 | }); 290 | 291 | resolve(); 292 | } catch (err) { 293 | reject(err); 294 | } 295 | }); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /components/Server_440.js: -------------------------------------------------------------------------------- 1 | const SteamID = require("steamid"); 2 | const ServerShared = require("./Server_Shared.js"); 3 | const EventTypes = require("../helpers/EventTypes.js"); 4 | 5 | module.exports = class TF2Server extends ServerShared { 6 | constructor(map = "pl_badwater", serverName = "Development Test Server") { 7 | super(440, map, serverName); 8 | 9 | this.map = map; 10 | } 11 | 12 | login() { 13 | return new Promise(async (resolve, reject) => { 14 | try { 15 | let data = await super.login(); 16 | 17 | // Finalizing 18 | await this.coordinator.sendMessage( 19 | 440, 20 | this.protobufs.data.tf2.ESOMsg.k_ESOMsg_CacheSubscriptionRefresh, 21 | { 22 | steamid: this.client.steamID.getSteamID64(), 23 | client_sessionid: this.client._sessionID 24 | }, 25 | this.protobufs.encodeProto("CMsgSOCacheSubscriptionRefresh", { 26 | owner: this.steamID.getSteamID64() 27 | }) 28 | ); 29 | 30 | await this.coordinator.sendMessage( 31 | 440, 32 | this.protobufs.data.tf2.ETFGCMsg.k_EMsgGC_GameServer_LevelInfo, 33 | { 34 | steamid: this.client.steamID.getSteamID64(), 35 | client_sessionid: this.client._sessionID 36 | }, 37 | this.protobufs.encodeProto("CMsgGC_GameServer_LevelInfo", { 38 | level_loaded: true, 39 | level_name: this.map 40 | }) 41 | ); 42 | 43 | // Done 44 | resolve(data); 45 | } catch (err) { 46 | reject(err); 47 | } 48 | }); 49 | } 50 | 51 | addPlayer(steamID, appTicket) { 52 | return new Promise(async (resolve, reject) => { 53 | try { 54 | steamID = new SteamID(steamID.toString()); 55 | } catch (err) { 56 | reject(err); 57 | return; 58 | } 59 | 60 | try { 61 | let data = await super.addPlayer(steamID, appTicket); 62 | 63 | await this.coordinator.sendMessage( 64 | 440, 65 | this.protobufs.data.tf2.ESOMsg.k_ESOMsg_CacheSubscriptionRefresh, 66 | { 67 | steamid: this.client.steamID.getSteamID64(), 68 | client_sessionid: this.client._sessionID 69 | }, 70 | this.protobufs.encodeProto("CMsgSOCacheSubscriptionRefresh", { 71 | owner: steamID.getSteamID64() 72 | }) 73 | ); 74 | 75 | resolve(data); 76 | } catch (err) { 77 | reject(err); 78 | } 79 | }); 80 | } 81 | 82 | async incrementKillCountAttribute(killerID, victimID, itemID, eventType, amount) { 83 | let eventTypeInfo = EventTypes[440]?.[eventType]; 84 | let killerID64 = killerID.getSteamID64(); 85 | let victimID64 = victimID.getSteamID64(); 86 | 87 | // Hardcode the maxmium amount of "CMsgIncrementKillCountAttribute_Multiple" children we use at once 88 | // Steam ignores packets too large so this sadly can't go into the thousands 89 | let maximumMultipleChildren = 1_000; 90 | let increment = eventTypeInfo?.allowIncrement ? 1_000 : 1; 91 | let multiMessagesNeeded = Math.ceil(amount / increment); 92 | let chunksNeeded = Math.ceil(multiMessagesNeeded / maximumMultipleChildren); 93 | 94 | for (let i = 0; i < chunksNeeded; i++) { 95 | console.log(`Progress: ${i * increment * maximumMultipleChildren} / ${amount}`); 96 | await new Promise(p => setTimeout(p, 50)); 97 | 98 | this.coordinator.sendMessage( 99 | this.appID, 100 | this.protobufs.data.tf2.EGCItemMsg.k_EMsgGC_IncrementKillCountAttribute_Multiple, 101 | {}, 102 | this.protobufs.encodeProto("CMsgIncrementKillCountAttribute_Multiple", { 103 | msgs: new Array(Math.min(maximumMultipleChildren, multiMessagesNeeded - (i * maximumMultipleChildren))).fill(0).map((_, j) => { 104 | let data = { 105 | killer_steam_id: killerID64, 106 | victim_steam_id: victimID64, 107 | item_id: itemID, 108 | event_type: eventType 109 | }; 110 | if (eventTypeInfo?.allowIncrement) { 111 | data.increment_value = Math.min(increment, amount - (j * increment)); 112 | } 113 | return data; 114 | }) 115 | }) 116 | ); 117 | } 118 | 119 | // We are done! Final progress log (Its not truly calculated so if the above math is wrong this will be wrong too, lets hope I am smart) 120 | console.log(`Progress: ${amount} / ${amount}`); 121 | } 122 | 123 | upgradeMerasmusLevel(player, level) { 124 | return this.coordinator.sendMessage( 125 | this.appID, 126 | this.protobufs.data.tf2.ETFGCMsg.k_EMsgGC_Halloween_UpdateMerasmusLootLevel, 127 | {}, 128 | this.protobufs.encodeProto("CMsgUpdateHalloweenMerasmusLootLevel", { 129 | merasmus_level: level, 130 | players: [ 131 | { 132 | steam_id: new SteamID(player.toString()).getSteamID64() 133 | } 134 | ] 135 | }) 136 | ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /components/Server_730.js: -------------------------------------------------------------------------------- 1 | const SteamID = require("steamid"); 2 | const ServerShared = require("./Server_Shared.js"); 3 | const EventTypes = require("../helpers/EventTypes.js"); 4 | 5 | module.exports = class CSGOServer extends ServerShared { 6 | constructor(map = "de_dust2", serverName = "Development Test Server") { 7 | super(730, map, serverName); 8 | 9 | this.map = map; 10 | } 11 | 12 | login() { 13 | return new Promise(async (resolve, reject) => { 14 | try { 15 | let data = await super.login(); 16 | 17 | // Finalizing 18 | await this.coordinator.sendMessage( 19 | 730, 20 | this.protobufs.data.csgo.ECsgoGCMsg.k_EMsgGCCStrike15_v2_MatchmakingServerReservationResponse, 21 | { 22 | steamid: this.client.steamID.getSteamID64(), 23 | client_sessionid: this.client._sessionID 24 | }, 25 | this.protobufs.encodeProto("CMsgGCCStrike15_v2_MatchmakingServerReservationResponse", { 26 | map: "de_dust2", 27 | server_version: this.serverVersion 28 | }) 29 | ); 30 | 31 | // Done 32 | resolve(data); 33 | } catch (err) { 34 | reject(err); 35 | } 36 | }); 37 | } 38 | 39 | addPlayer(steamID, appTicket) { 40 | return new Promise(async (resolve, reject) => { 41 | try { 42 | steamID = new SteamID(steamID.toString()); 43 | } catch (err) { 44 | reject(err); 45 | return; 46 | } 47 | 48 | try { 49 | let data = await super.addPlayer(steamID, appTicket); 50 | 51 | await this.coordinator.sendMessage( 52 | 730, 53 | this.protobufs.data.csgo.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Server2GCClientValidate, 54 | { 55 | steamid: this.client.steamID.getSteamID64(), 56 | client_sessionid: this.client._sessionID 57 | }, 58 | this.protobufs.encodeProto("CMsgGCCStrike15_v2_Server2GCClientValidate", { 59 | accountid: steamID.accountid 60 | }) 61 | ); 62 | 63 | resolve(data); 64 | } catch (err) { 65 | reject(err); 66 | } 67 | }); 68 | } 69 | 70 | async incrementKillCountAttribute(killerID, victimID, itemID, eventType, amount) { 71 | let eventTypeInfo = EventTypes[730]?.[eventType]; 72 | let maximumMultiSendAtOnce = 100; // CSGO doesn't support multi-messages but we can send multiple GC messages at once 73 | let increment = eventTypeInfo?.allowIncrement ? 1_000 : 1; 74 | let multiSendsNeeded = Math.ceil(amount / increment); 75 | let chunksNeeded = Math.ceil(multiSendsNeeded / maximumMultiSendAtOnce); 76 | 77 | // We send 10K at once 78 | for (let i = 0; i < chunksNeeded; i ++) { 79 | console.log(`Progress: ${i * increment * maximumMultiSendAtOnce} / ${amount}`); 80 | await new Promise(p => setTimeout(p, 50)); 81 | 82 | let sendAtOnce = Math.min(maximumMultiSendAtOnce, multiSendsNeeded - (i * maximumMultiSendAtOnce)); 83 | for (let j = 0; j < sendAtOnce; j++) { 84 | let data = { 85 | killer_account_id: killerID.accountid, 86 | victim_account_id: victimID.accountid, 87 | item_id: itemID, 88 | event_type: eventType, 89 | amount: Math.min(increment, amount - (j * increment)) 90 | }; 91 | this.coordinator.sendMessage( 92 | this.appID, 93 | this.protobufs.data.csgo.EGCItemMsg.k_EMsgGC_IncrementKillCountAttribute, 94 | {}, 95 | this.protobufs.encodeProto("CMsgIncrementKillCountAttribute", data) 96 | ); 97 | } 98 | } 99 | 100 | // We are done! Final progress log (Its not truly calculated so if the above math is wrong this will be wrong too, lets hope I am smart) 101 | console.log(`Progress: ${amount} / ${amount}`); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /components/Server_Shared.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const Events = require("events"); 3 | const SteamUser = require("steam-user"); 4 | const SteamID = require("steamid"); 5 | const StdLib = require("@doctormckay/stdlib"); 6 | const Protobufs = require("../helpers/Protobufs.js"); 7 | const Coordinator = require("../helpers/Coordinator.js"); 8 | const Helper = require("../helpers/Helper.js"); 9 | const validAppIDs = [440, 730]; 10 | const appIdProtobufs = { 11 | "440": { 12 | name: "tf2", 13 | protos: path.join(__dirname, "..", "protobufs", "tf2") 14 | }, 15 | "730": { 16 | name: "csgo", 17 | protos: path.join(__dirname, "..", "protobufs", "csgo") 18 | } 19 | }; 20 | const versionNiceAppIdParser = { 21 | "440": function (version) { 22 | return version; 23 | }, 24 | "730": function (verion) { 25 | return verion[0] + "." + verion[1] + verion[2] + "." + verion[3] + "." + verion[4]; 26 | } 27 | }; 28 | const appIdDir = { 29 | "440": "tf", 30 | "730": "csgo" 31 | }; 32 | const appIdGCHello = { 33 | "440": { 34 | version: 1077, 35 | }, 36 | "730": { 37 | client_launcher: 0, 38 | } 39 | }; 40 | const appIdGameData = { 41 | "440": "tf_mm_trusted:0,tf_mm_servermode:0,lobby:0,steamblocking:0", 42 | "730": "g:csgo,gt:0,gm:1,sk:0,f1:0," 43 | }; 44 | const appIdGcWelcomeDecode = { 45 | "440": "CMsgServerWelcome", 46 | "730": "CMsgClientWelcome" 47 | }; 48 | const EServerFlags = { 49 | None: 0, 50 | Active: 1, 51 | Secure: 2, 52 | Dedicated: 4, 53 | Linux: 8, 54 | Passworded: 16, 55 | Private: 32 56 | }; 57 | 58 | 59 | module.exports = class ServerShared extends Events { 60 | constructor(appID, map = "itemtest", serverName = "Development Test Server") { 61 | super(); 62 | 63 | if (!validAppIDs.includes(appID)) { 64 | throw new Error("Shared server structure not setup to support app ID " + appID); 65 | } 66 | this.appID = appID; 67 | this.map = map; 68 | this.serverName = serverName; 69 | 70 | this.protobufs = new Protobufs([ 71 | { 72 | name: "steam", 73 | protos: path.join(__dirname, "..", "protobufs", "steam") 74 | }, 75 | appIdProtobufs[this.appID] 76 | ]); 77 | 78 | this.client = new SteamUser(); 79 | this.coordinator = new Coordinator(this.client, this.appID); 80 | 81 | this.coordinator.on("receivedFromSteam", this._receivedFromSteam.bind(this)); 82 | this.coordinator.on("receivedFromGC", this._receivedFromGameCoordinator.bind(this)); 83 | 84 | this.serverVersion = 0; 85 | this.serverVersionNice = ""; 86 | 87 | this.gcWelcome = null; 88 | this.lastServerUpdate = null; 89 | this.players = []; 90 | this.loggedOff = false; 91 | } 92 | 93 | get steamID() { 94 | return this.client.steamID; 95 | } 96 | 97 | login() { 98 | return new Promise(async (resolve, reject) => { 99 | try { 100 | let version = await Helper.GetSteamAPI("ISteamApps", "UpToDateCheck", "v1", { 101 | appid: this.appID, 102 | version: 0 103 | }); 104 | if (!version.success) { 105 | reject(new Error(version)); 106 | return; 107 | } 108 | this.serverVersion = String(version.required_version); 109 | this.serverVersionNice = versionNiceAppIdParser[this.appID](this.serverVersion); 110 | this.serverVersion = Number(this.serverVersion); 111 | 112 | // Login 113 | this.client._logOnDetails = { 114 | _steamid: "90071992547409920", // Anon gameserver 115 | 116 | protocol_version: 65580, 117 | obfustucated_private_ip: 2130706433, 118 | cell_id: 4294967295, 119 | client_os_type: 10, 120 | qos_level: 2, 121 | is_steam_box: false, 122 | client_instance_id: 0 123 | }; 124 | this.client.logOn(true); 125 | 126 | await new Promise((res, rej) => { 127 | this.client.on("loggedOn", (details) => { 128 | res(); 129 | }); 130 | this.client.on("error", (err) => { 131 | rej(err); 132 | }); 133 | }).finally(() => { 134 | this.client.removeAllListeners("loggedOn"); 135 | this.client.removeAllListeners("error"); 136 | }); 137 | 138 | // Register server 139 | let data = await this.coordinator.sendMessage( 140 | undefined, 141 | this.protobufs.data.steam.EMsg.k_EMsgGSServerType, 142 | { 143 | steamid: this.client.steamID.getSteamID64(), 144 | client_sessionid: this.client._sessionID 145 | }, 146 | this.protobufs.encodeProto("CMsgGSServerType", { 147 | app_id_served: this.appID, 148 | flags: EServerFlags.Active | EServerFlags.Dedicated | EServerFlags.Passworded | EServerFlags.Private, 149 | game_port: 27015, 150 | game_dir: appIdDir[this.appID], 151 | game_version: this.serverVersionNice, 152 | game_query_port: 27015 153 | }), 154 | this.protobufs.data.steam.EMsg.k_EMsgGSStatusReply, 155 | 10000 156 | ); 157 | 158 | await this.coordinator.sendMessage( 159 | undefined, 160 | this.protobufs.data.steam.EMsg.k_EMsgClientConnectionStats, 161 | { 162 | steamid: this.client.steamID.getSteamID64(), 163 | client_sessionid: this.client._sessionID 164 | }, 165 | this.protobufs.encodeProto("CMsgClientConnectionStats", { 166 | stats_logon: { 167 | connect_attempts: 1, 168 | connect_successes: 1, 169 | connect_failures: 0, 170 | connections_dropped: 0, 171 | seconds_running: 14, 172 | msec_tologonthistime: 12881, 173 | count_bad_cms: 0 174 | }, 175 | stats_vconn: { 176 | connections_udp: 0, 177 | connections_tcp: 5, 178 | stats_udp: { 179 | pkts_sent: 2, 180 | bytes_sent: 8, 181 | pkts_recv: 0, 182 | pkts_processed: 0, 183 | bytes_recv: 0 184 | }, 185 | pkts_abandoned: 0, 186 | conn_req_received: 0, 187 | pkts_resent: 0, 188 | msgs_sent: 0, 189 | msgs_sent_failed: 0, 190 | msgs_recv: 0, 191 | datagrams_sent: 0, 192 | datagrams_recv: 0, 193 | bad_pkts_recv: 0, 194 | unknown_conn_pkts_recv: 0, 195 | missed_pkts_recv: 0, 196 | dup_pkts_recv: 0, 197 | failed_connect_challenges: 0, 198 | micro_sec_avg_latency: 0, 199 | micro_sec_min_latency: 0, 200 | micro_sec_max_latency: 0, 201 | mem_pool_msg_in_use: 0 202 | } 203 | }) 204 | ); 205 | 206 | this.loggedOff = false; 207 | 208 | this.sendServerUpdate({ 209 | steam_id_gs: this.client.steamID.getSteamID64(), 210 | query_port: 27015, 211 | game_port: 27015, 212 | sourcetv_port: 0, 213 | name: this.serverName, 214 | app_id: this.appID, 215 | gamedir: appIdDir[this.appID], 216 | version: this.serverVersionNice, 217 | product: appIdDir[this.appID], 218 | region: "-1", 219 | max_players: 10, 220 | bot_count: 0, 221 | password: true, 222 | secure: false, 223 | dedicated: true, 224 | os: "w", 225 | game_data: appIdGameData[this.appID], 226 | game_data_version: 1, 227 | game_type: "empty", 228 | map: this.map 229 | }); 230 | 231 | // Get latest version for CS 232 | if (this.appID === 730) { 233 | const text = await fetch("https://raw.githubusercontent.com/SteamDatabase/GameTracking-CS2/master/game/csgo/steam.inf").then(r => r.text()); 234 | const lines = text.split("\n").map(l => l.replace(/\r/g, "").trim()); 235 | const idx = lines.findIndex(l => l.startsWith("ServerVersion=")); 236 | if (idx >= 0) { 237 | const [_, version] = lines[idx].split("="); 238 | appIdGCHello[this.appID].version = parseInt(version); 239 | } 240 | } 241 | 242 | // GC register 243 | let welcome = await this.coordinator.sendMessage( 244 | this.appID, 245 | 4007, // Always 4007 246 | { 247 | steamid: this.client.steamID.getSteamID64(), 248 | client_sessionid: this.client._sessionID 249 | }, 250 | this.protobufs.encodeProto("CMsgServerHello", appIdGCHello[this.appID]), 251 | 4005, // Always 4005 252 | 5000 253 | ); 254 | welcome = welcome instanceof Buffer ? this.protobufs.decodeProto(appIdGcWelcomeDecode[this.appID], welcome) : welcome; 255 | this.gcWelcome = welcome; 256 | 257 | // Done 258 | resolve(this.steamID.getSteamID64()); 259 | } catch (err) { 260 | this.logOff(); 261 | reject(err); 262 | } 263 | }); 264 | } 265 | 266 | sendServerUpdate(update) { 267 | if (this.loggedOff) { 268 | return false; 269 | } 270 | 271 | if (!update) { 272 | update = this.lastServerUpdate || {}; 273 | } 274 | 275 | if (this.lastServerUpdate) { 276 | for (let key in this.lastServerUpdate) { 277 | if (typeof update[key] !== "undefined") { 278 | continue; 279 | } 280 | 281 | update[key] = this.lastServerUpdate[key]; 282 | } 283 | } 284 | this.lastServerUpdate = update; 285 | 286 | return this.coordinator.sendMessage( 287 | undefined, 288 | this.protobufs.data.steam.EMsg.k_EMsgAMGameServerUpdate, 289 | { 290 | steamid: this.client.steamID.getSteamID64(), 291 | client_sessionid: this.client._sessionID 292 | }, 293 | this.protobufs.encodeProto("CMsgGameServerData", update) 294 | ); 295 | } 296 | 297 | _receivedFromSteam(header, body) { 298 | let decoder = ""; 299 | switch (header.msg) { 300 | case this.protobufs.data.steam.EMsg.k_EMsgGameServerOutOfDate: { 301 | decoder = "CMsgGameServerOutOfDate"; 302 | break; 303 | } 304 | default: { 305 | break; 306 | } 307 | } 308 | 309 | if (!decoder) { 310 | return; 311 | } 312 | 313 | let obj = this.protobufs.decodeProto(decoder, body); 314 | switch (header.msg) { 315 | case this.protobufs.data.steam.EMsg.k_EMsgGameServerOutOfDate: { 316 | this.emit("outOfDate", obj); 317 | break; 318 | } 319 | default: { 320 | break; 321 | } 322 | } 323 | } 324 | 325 | _receivedFromGameCoordinator(msgType, body) { 326 | let decoder = ""; 327 | switch (msgType) { 328 | case 24: { // k_ESOMsg_CacheSubscribed 329 | decoder = "CMsgSOCacheSubscribed"; 330 | break; 331 | } 332 | case 26: { // k_ESOMsg_UpdateMultiple 333 | decoder = "CMsgSOMultipleObjects"; 334 | break; 335 | } 336 | case 25: { // k_ESOMsg_CacheUnsubscribed 337 | decoder = "CMsgSOCacheUnsubscribed"; 338 | break; 339 | } 340 | default: { 341 | break; 342 | } 343 | } 344 | 345 | if (!decoder) { 346 | return; 347 | } 348 | 349 | let obj = this.protobufs.decodeProto(decoder, body); 350 | switch (msgType) { 351 | case 24: { // k_ESOMsg_CacheSubscribed 352 | this.emit("cacheSubscribed", obj); 353 | break; 354 | } 355 | case 26: { // k_ESOMsg_UpdateMultiple 356 | this.emit("cacheUpdateMultiple", obj); 357 | break; 358 | } 359 | case 25: { // k_ESOMsg_CacheUnsubscribed 360 | this.emit("cacheUnsubscribe", obj); 361 | break; 362 | } 363 | default: { 364 | break; 365 | } 366 | } 367 | } 368 | 369 | logOff() { 370 | this.client.logOff(); 371 | 372 | this.serverVersion = 0; 373 | this.serverVersionNice = ""; 374 | 375 | this.gcWelcome = null; 376 | this.lastServerUpdate = null; 377 | this.players = []; 378 | 379 | this.loggedOff = true; 380 | } 381 | 382 | addPlayer(steamID, appTicket) { 383 | return new Promise(async (resolve, reject) => { 384 | try { 385 | steamID = new SteamID(steamID.toString()); 386 | } catch (err) { 387 | reject(err); 388 | return; 389 | } 390 | 391 | let playerIndex = this.players.length; 392 | try { 393 | // Add player 394 | this.players.push({ 395 | steamID: steamID, 396 | ticket: appTicket, 397 | ticketResponse: null, 398 | sessionId: null 399 | }); 400 | 401 | // Verify user's well-being 402 | let verify = await this.coordinator.sendMessage( 403 | undefined, 404 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthList, // Do not encode protobuf below like usually, Steam-User automatically does this for us 405 | { 406 | steamid: this.client.steamID.getSteamID64(), 407 | client_sessionid: this.client._sessionID 408 | }, { 409 | tokens_left: 0, 410 | last_request_seq: 0, 411 | last_request_seq_from_server: 0, 412 | tickets: this.players.map((player, index) => { 413 | let ticketCrc = StdLib.Hashing.crc32(player.ticket); 414 | return { 415 | estate: playerIndex === index ? 1 : 3, 416 | eresult: 4294967295, 417 | steamid: player.steamID.getSteamID64(), 418 | gameid: this.appID, 419 | h_steam_pipe: this.client._hSteamPipe, 420 | ticket_crc: ticketCrc, 421 | ticket: player.ticket 422 | }; 423 | }), 424 | app_ids: [ 425 | this.appID 426 | ], 427 | message_sequence: ++this.client._authSeqMe 428 | }, 429 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthListAck, 430 | 5000 431 | ); 432 | verify = verify instanceof Buffer ? this.protobufs.decodeProto("CMsgClientAuthListAck", verify) : verify; 433 | this.players[playerIndex].ticketResponse = verify; 434 | 435 | // Update server info 436 | await this.sendServerUpdate({ 437 | players: this.players.map((player) => { 438 | return { 439 | steamid_id: player.steamID.getSteamID64() 440 | }; 441 | }), 442 | game_data: appIdGameData[this.appID], 443 | game_data_version: 1, 444 | game_type: this.players.length <= 0 ? "empty" : "" 445 | }); 446 | 447 | resolve(true); 448 | } catch (err) { 449 | this.players.splice(playerIndex, 1); 450 | reject(err); 451 | } 452 | }); 453 | } 454 | 455 | removePlayer(steamID) { 456 | return new Promise(async (resolve, reject) => { 457 | try { 458 | steamID = new SteamID(steamID.toString()); 459 | } catch (err) { 460 | reject(err); 461 | return; 462 | } 463 | 464 | // Remove player 465 | let playerIndex = this.players.findIndex(p => p.steamID.accountid === steamID.accountid); 466 | this.players.splice(playerIndex, 1); 467 | 468 | 469 | try { 470 | // Unverify user 471 | let verify = await this.coordinator.sendMessage( 472 | undefined, 473 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthList, // Do not encode protobuf below like usually, Steam-User automatically does this for us 474 | { 475 | steamid: this.client.steamID.getSteamID64(), 476 | client_sessionid: this.client._sessionID 477 | }, 478 | { 479 | tokens_left: this.client._gcTokens.length, 480 | last_request_seq: this.client._authSeqMe, 481 | last_request_seq_from_server: this.client._authSeqThem, 482 | tickets: this.players.map((player) => { 483 | let ticketCrc = StdLib.Hashing.crc32(player.ticket); 484 | return { 485 | estate: 1, 486 | eresult: 4294967295, 487 | steamid: player.steamID.getSteamID64(), 488 | gameid: this.appID, 489 | h_steam_pipe: this.client._hSteamPipe, 490 | ticket_crc: ticketCrc, 491 | ticket: player.ticket 492 | }; 493 | }), 494 | app_ids: [ 495 | this.appID 496 | ], 497 | message_sequence: ++this.client._authSeqMe 498 | }, 499 | this.protobufs.data.steam.EMsg.k_EMsgClientAuthListAck, 500 | 5000 501 | ); 502 | verify = verify instanceof Buffer ? this.protobufs.decodeProto("CMsgClientAuthListAck", verify) : verify; 503 | 504 | // Update server info 505 | await this.sendServerUpdate({ 506 | players: this.players.map((player) => { 507 | return { 508 | steamid_id: player.steamID.getSteamID64() 509 | }; 510 | }), 511 | game_data: appIdGameData[this.appID], 512 | game_data_version: 1, 513 | game_type: this.players.length <= 0 ? "empty" : "" 514 | }); 515 | 516 | resolve(true); 517 | } catch (err) { 518 | reject(err); 519 | } 520 | }); 521 | } 522 | } 523 | -------------------------------------------------------------------------------- /config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "boostingAccount": { 3 | "username": "MainAccount", 4 | "password": "MainPassword" 5 | }, 6 | "botAccount": { 7 | "username": "BotAccount", 8 | "password": "BotPassword" 9 | }, 10 | "itemID": "1111111111", 11 | "appID": 730, 12 | "eventType": 0, 13 | "incrementValue": 1 14 | } 15 | -------------------------------------------------------------------------------- /helpers/Coordinator.js: -------------------------------------------------------------------------------- 1 | const Events = require("events"); 2 | const ByteBuffer = require("bytebuffer"); 3 | 4 | module.exports = class Coordinator extends Events { 5 | constructor(steamUser, appID) { 6 | super(); 7 | 8 | this.appID = appID; 9 | this.steamUser = steamUser; 10 | 11 | this.steamUser.on("receivedFromGC", (appid, msgType, payload) => { 12 | if (appid !== this.appID) { 13 | return; 14 | } 15 | 16 | this.emit("receivedFromGC", msgType, payload); 17 | }); 18 | 19 | let _handleMessage = this.steamUser._handleMessage; 20 | this.steamUser._handleMessage = (header, body, ...args) => { 21 | this.emit("receivedFromSteam", header, body); 22 | _handleMessage.call(this.steamUser, header, body, ...args); 23 | } 24 | } 25 | 26 | sendRaw(buffer) { 27 | this.steamUser._connection.send(buffer); 28 | } 29 | 30 | /** 31 | * Send a message and get the response from it if needed 32 | * @param {Number|undefined} appid AppID where to send the GC message to - Pass "undefined" for customized proto 33 | * @param {Number} msg The identifier of the message we are sending 34 | * @param {Object} proto Header proto 35 | * @param {Buffer} buffer Buffer to send 36 | * @param {Number|undefined} responseHeader The response header to our request 37 | * @param {Number} timeout Max number of milliseconds before we give up on waiting for our response 38 | * @param {Object} extraSendOpts Should we use a callback to send this message? 39 | * @returns {Promise} Promise which resolves in the object of our response, or undefined if "responseHeader" is undefined or rejects in a timeout error 40 | */ 41 | sendMessage(appid, msg, proto, buffer, responseHeader, timeout = 30000, extraSendOpts = {}) { 42 | return new Promise((resolve, reject) => { 43 | if (!appid) { 44 | this.steamUser._send({ 45 | msg: msg, 46 | proto: proto, 47 | ...extraSendOpts 48 | }, buffer); 49 | 50 | if (!responseHeader) { 51 | resolve(); 52 | return; 53 | } 54 | 55 | let sendTimeout = setTimeout(() => { 56 | if (this.steamUser._handlerManager._handlers[responseHeader] && this.steamUser._handlerManager._handlers[responseHeader].length > 0) { 57 | this.steamUser._handlerManager._handlers[responseHeader].pop(); // We added our message last (I assume) so remove the last one 58 | 59 | if (this.steamUser._handlerManager._handlers[responseHeader].length <= 0) { 60 | delete this.steamUser._handlerManager._handlers[responseHeader]; 61 | } 62 | } 63 | 64 | reject(new Error("Failed to send message: Timeout")); 65 | }, timeout); 66 | 67 | this.steamUser._handlerManager.add(responseHeader, (body) => { 68 | if (this.steamUser._handlerManager.hasHandler(responseHeader)) { 69 | if (this.steamUser._handlerManager._handlers[responseHeader] && this.steamUser._handlerManager._handlers[responseHeader].length > 0) { 70 | this.steamUser._handlerManager._handlers[responseHeader].pop(); // We added our message last (I assume) so remove the last one 71 | 72 | if (this.steamUser._handlerManager._handlers[responseHeader].length <= 0) { 73 | delete this.steamUser._handlerManager._handlers[responseHeader]; 74 | } 75 | } 76 | } 77 | 78 | clearTimeout(sendTimeout); 79 | resolve(ByteBuffer.isByteBuffer(body) ? body.toBuffer() : body); 80 | }); 81 | return; 82 | } 83 | 84 | this.steamUser.sendToGC(appid, msg, proto, buffer); 85 | if (!responseHeader) { 86 | resolve(); 87 | return; 88 | } 89 | 90 | let sendTimeout = setTimeout(() => { 91 | this.removeListener("receivedFromGC", sendMessageResponse); 92 | reject(new Error("Failed to send GC message: Timeout")); 93 | }, timeout); 94 | 95 | this.on("receivedFromGC", sendMessageResponse); 96 | function sendMessageResponse(msgType, payload) { 97 | if (msgType === responseHeader) { 98 | clearTimeout(sendTimeout); 99 | this.removeListener("receivedFromGC", sendMessageResponse); 100 | 101 | resolve(ByteBuffer.isByteBuffer(payload) ? payload.toBuffer() : payload); 102 | } 103 | } 104 | }); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /helpers/EventTypes.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 730: { 3 | // CSGO does not support "allowIncrement" at all 4 | 0: { 5 | name: "StatTrak™ Confirmed Kills", 6 | internal: "Kills", 7 | allowIncrement: false, 8 | officialOnly: false 9 | }, 10 | 1: { 11 | name: "StatTrak™ Official Competitive MVPs", 12 | internal: "OCMVPs", 13 | allowIncrement: false, 14 | officialOnly: true 15 | } 16 | }, 17 | 440: { 18 | 0: { 19 | name: "Kills", 20 | internal: "Kills", 21 | allowIncrement: false, 22 | officialOnly: false 23 | }, 24 | 1: { 25 | name: "Ubers", 26 | internal: "Ubers", 27 | allowIncrement: false, 28 | officialOnly: false 29 | }, 30 | 2: { 31 | name: "Kill Assists", 32 | internal: "KillAssists", 33 | allowIncrement: false, 34 | officialOnly: false 35 | }, 36 | 3: { 37 | name: "Sentry Kills", 38 | internal: "SentryKills", 39 | allowIncrement: false, 40 | officialOnly: false 41 | }, 42 | 4: { 43 | name: "Sodden Victims", 44 | internal: "PeeVictims", 45 | allowIncrement: false, 46 | officialOnly: false 47 | }, 48 | 5: { 49 | name: "Spies Shocked", 50 | internal: "BackstabsAbsorbed", 51 | allowIncrement: false, 52 | officialOnly: false 53 | }, 54 | 6: { 55 | name: "Heads Taken", 56 | internal: "HeadsTaken", 57 | allowIncrement: false, 58 | officialOnly: false 59 | }, 60 | 7: { 61 | name: "Humiliations", 62 | internal: "Humiliations", 63 | allowIncrement: false, 64 | officialOnly: false 65 | }, 66 | 8: { 67 | name: "Gifts Given", 68 | internal: "GiftsGiven", 69 | allowIncrement: false, 70 | officialOnly: true 71 | }, 72 | 9: { 73 | name: "Deaths Feigned", 74 | internal: "FeignDeaths", 75 | allowIncrement: false, 76 | officialOnly: false 77 | }, 78 | 10: { 79 | name: "Scouts Killed", 80 | internal: "ScoutsKilled", 81 | allowIncrement: false, 82 | officialOnly: false 83 | }, 84 | 11: { 85 | name: "Snipers Killed", 86 | internal: "SnipersKilled", 87 | allowIncrement: false, 88 | officialOnly: false 89 | }, 90 | 12: { 91 | name: "Soldiers Killed", 92 | internal: "SoldiersKilled", 93 | allowIncrement: false, 94 | officialOnly: false 95 | }, 96 | 13: { 97 | name: "Demomen Killed", 98 | internal: "DemomenKilled", 99 | allowIncrement: false, 100 | officialOnly: false 101 | }, 102 | 14: { 103 | name: "Heavies Killed", 104 | internal: "HeaviesKilled", 105 | allowIncrement: false, 106 | officialOnly: false 107 | }, 108 | 15: { 109 | name: "Pyros Killed", 110 | internal: "PyrosKilled", 111 | allowIncrement: false, 112 | officialOnly: false 113 | }, 114 | 16: { 115 | name: "Spies Killed", 116 | internal: "SpiesKilled", 117 | allowIncrement: false, 118 | officialOnly: false 119 | }, 120 | 17: { 121 | name: "Engineers Killed", 122 | internal: "EngineersKilled", 123 | allowIncrement: false, 124 | officialOnly: false 125 | }, 126 | 18: { 127 | name: "Medics Killed", 128 | internal: "MedicsKilled", 129 | allowIncrement: false, 130 | officialOnly: false 131 | }, 132 | 19: { 133 | name: "Buildings Destroyed", 134 | internal: "BuildingsDestroyed", 135 | allowIncrement: false, 136 | officialOnly: false 137 | }, 138 | 20: { 139 | name: "Projectiles Reflected", 140 | internal: "ProjectilesReflected", 141 | allowIncrement: false, 142 | officialOnly: false 143 | }, 144 | 21: { 145 | name: "Headshot Kills", 146 | internal: "HeadshotKills", 147 | allowIncrement: false, 148 | officialOnly: false 149 | }, 150 | 22: { 151 | name: "Airborne Enemy Kills", 152 | internal: "AirborneEnemyKills", 153 | allowIncrement: false, 154 | officialOnly: false 155 | }, 156 | 23: { 157 | name: "Gib Kills", 158 | internal: "GibKills", 159 | allowIncrement: false, 160 | officialOnly: false 161 | }, 162 | 24: { 163 | name: "Buildings Sapped", 164 | internal: "BuildingsSapped", 165 | allowIncrement: false, 166 | officialOnly: false 167 | }, 168 | 25: { 169 | name: "Tickle Fights Won", 170 | internal: "PlayersTickled", 171 | allowIncrement: false, 172 | officialOnly: false 173 | }, 174 | 26: { 175 | name: "Opponents Flattened", 176 | internal: "MenTreaded", 177 | allowIncrement: false, 178 | officialOnly: false 179 | }, 180 | 27: { 181 | name: "Kills Under A Full Moon", 182 | internal: "KillsDuringFullMoon", 183 | allowIncrement: false, 184 | officialOnly: false 185 | }, 186 | 28: { 187 | name: "Dominations", 188 | internal: "StartDominationKills", 189 | allowIncrement: false, 190 | officialOnly: false 191 | }, 192 | 30: { 193 | name: "Revenges", 194 | internal: "RevengeKills", 195 | allowIncrement: false, 196 | officialOnly: false 197 | }, 198 | 31: { 199 | name: "Posthumous Kills", 200 | internal: "PosthumousKills", 201 | allowIncrement: false, 202 | officialOnly: false 203 | }, 204 | 32: { 205 | name: "Teammates Extinguished", 206 | internal: "AlliesExtinguished", 207 | allowIncrement: false, 208 | officialOnly: false 209 | }, 210 | 33: { 211 | name: "Critical Kills", 212 | internal: "CriticalKills", 213 | allowIncrement: false, 214 | officialOnly: false 215 | }, 216 | 34: { 217 | name: "Kills While Explosive-Jumping", 218 | internal: "KillsWhileExplosiveJumping", 219 | allowIncrement: false, 220 | officialOnly: false 221 | }, 222 | 36: { 223 | name: "Sappers Removed", 224 | internal: "SapperDestroyed", 225 | allowIncrement: false, 226 | officialOnly: false 227 | }, 228 | 37: { 229 | name: "Cloaked Spies Killed", 230 | internal: "InvisibleSpiesKilled", 231 | allowIncrement: false, 232 | officialOnly: false 233 | }, 234 | 38: { 235 | name: "Medics Killed That Have Full ÜberCharge", 236 | internal: "MedicsWithFullUberKilled", 237 | allowIncrement: false, 238 | officialOnly: false 239 | }, 240 | 39: { 241 | name: "Robots Destroyed", 242 | internal: "RobotsKilled", 243 | allowIncrement: false, 244 | officialOnly: false 245 | }, 246 | 40: { 247 | name: "Giant Robots Destroyed", 248 | internal: "MinibossRobotsKilled", 249 | allowIncrement: false, 250 | officialOnly: false 251 | }, 252 | 44: { 253 | name: "Kills While Low Health", 254 | internal: "LowHealthKill", 255 | allowIncrement: false, 256 | officialOnly: false 257 | }, 258 | 45: { 259 | name: "Kills During Halloween", 260 | internal: "HalloweenKills", 261 | allowIncrement: false, 262 | officialOnly: false 263 | }, 264 | 46: { 265 | name: "Robots Killed During Halloween", 266 | internal: "HalloweenRobotKills", 267 | allowIncrement: false, 268 | officialOnly: false 269 | }, 270 | 47: { 271 | name: "Defenders Killed", 272 | internal: "DefenderKills", 273 | allowIncrement: false, 274 | officialOnly: false 275 | }, 276 | 48: { 277 | name: "Submerged Enemy Kills", 278 | internal: "UnderwaterKills", 279 | allowIncrement: false, 280 | officialOnly: false 281 | }, 282 | 49: { 283 | name: "Kills While Invuln ÜberCharged", 284 | internal: "KillsWhileUbercharged", 285 | allowIncrement: false, 286 | officialOnly: false 287 | }, 288 | 50: { 289 | name: "Food Items Eaten", 290 | internal: "FoodEaten", 291 | allowIncrement: false, 292 | officialOnly: false 293 | }, 294 | 51: { 295 | name: "Banners Deployed", 296 | internal: "BannersDeployed", 297 | allowIncrement: false, 298 | officialOnly: false 299 | }, 300 | 58: { 301 | name: "Seconds Cloaked", 302 | internal: "TimeCloaked", 303 | allowIncrement: true, 304 | officialOnly: false 305 | }, 306 | 59: { 307 | name: "Health Dispensed to Teammates", 308 | internal: "HealthGiven", 309 | allowIncrement: true, 310 | officialOnly: false 311 | }, 312 | 60: { 313 | name: "Teammates Teleported", 314 | internal: "TeleportsGiven", 315 | allowIncrement: false, 316 | officialOnly: false 317 | }, 318 | 61: { 319 | name: "Tanks Destroyed", 320 | internal: "TanksDestroyed", 321 | allowIncrement: false, 322 | officialOnly: false 323 | }, 324 | 62: { 325 | name: "Long-Distance Kills", 326 | internal: "LongDistanceKills", 327 | allowIncrement: false, 328 | officialOnly: false 329 | }, 330 | // 63: kKillEaterEvent_UniqueEvent__KilledAccountWithItem (Unused everywhere) 331 | 64: { 332 | name: "Points Scored", 333 | internal: "PointsScored", 334 | allowIncrement: true, 335 | officialOnly: false 336 | }, 337 | 65: { 338 | name: "Double Donks", 339 | internal: "DoubleDonks", 340 | allowIncrement: false, 341 | officialOnly: false 342 | }, 343 | 66: { 344 | name: "Teammates Whipped", 345 | internal: "TeammatesWhipped", 346 | allowIncrement: false, 347 | officialOnly: false 348 | }, 349 | 67: { 350 | name: "Kills during Victory Time", 351 | internal: "VictoryTimeKill", 352 | allowIncrement: false, 353 | officialOnly: false 354 | }, 355 | 68: { 356 | name: "Robot Scouts Destroyed", 357 | internal: "RobotScoutKill", 358 | allowIncrement: false, 359 | officialOnly: false 360 | }, 361 | 74: { 362 | name: "Robot Spies Destroyed", 363 | internal: "RobotSpyKill", 364 | allowIncrement: false, 365 | officialOnly: false 366 | }, 367 | 77: { 368 | name: "Taunt Kills", 369 | internal: "TauntKill", 370 | allowIncrement: false, 371 | officialOnly: false 372 | }, 373 | 78: { 374 | name: "Unusual-Wearing Player Kills", 375 | internal: "PlayerWearingUnusualKill", 376 | allowIncrement: false, 377 | officialOnly: false 378 | }, 379 | 79: { 380 | name: "Burning Player Kills", 381 | internal: "BurningPlayerKill", 382 | allowIncrement: false, 383 | officialOnly: false 384 | }, 385 | 80: { 386 | name: "Killstreaks Ended", 387 | internal: "KillstreaksEnded", 388 | allowIncrement: false, 389 | officialOnly: false 390 | }, 391 | 81: { 392 | name: "Freezecam Taunt Appearances", 393 | internal: "KillcamTaunts", 394 | allowIncrement: false, 395 | officialOnly: false 396 | }, 397 | 82: { 398 | name: "Damage Dealt", 399 | internal: "DamageDealt", 400 | allowIncrement: true, 401 | officialOnly: false 402 | }, 403 | 83: { 404 | name: "Fires Survived", 405 | internal: "FiresSurvived", 406 | allowIncrement: false, 407 | officialOnly: false 408 | }, 409 | 84: { 410 | name: "Allied Healing Done", 411 | internal: "AllyHealingDone", 412 | allowIncrement: true, 413 | officialOnly: false 414 | }, 415 | 85: { 416 | name: "Point Blank Kills", 417 | internal: "PointBlankKill", 418 | allowIncrement: false, 419 | officialOnly: false 420 | }, 421 | 86: { 422 | name: "Wrangled Sentry Kills", 423 | internal: "PlayerKillsBySentry", 424 | allowIncrement: false, 425 | officialOnly: false 426 | }, 427 | 87: { 428 | name: "Kills", 429 | internal: "CosmeticKills", 430 | allowIncrement: false, 431 | officialOnly: false 432 | }, 433 | 88: { 434 | name: "Full Health Kills", 435 | internal: "FullHealthKills", 436 | allowIncrement: false, 437 | officialOnly: false 438 | }, 439 | 89: { 440 | name: "Taunting Player Kills", 441 | internal: "TauntingPlayerKills", 442 | allowIncrement: false, 443 | officialOnly: false 444 | }, 445 | 90: { 446 | name: "Carnival Kills", 447 | internal: "HalloweenOverworldKills", 448 | allowIncrement: false, 449 | officialOnly: false 450 | }, 451 | 91: { 452 | name: "Carnival Underworld Kills", 453 | internal: "HalloweenUnderworldKills", 454 | allowIncrement: false, 455 | officialOnly: false 456 | }, 457 | 92: { 458 | name: "Carnival Games Won", 459 | internal: "HalloweenMinigamesWon", 460 | allowIncrement: false, 461 | officialOnly: false 462 | }, 463 | 93: { 464 | name: "Not Crit nor MiniCrit Kills", 465 | internal: "NonCritKills", 466 | allowIncrement: false, 467 | officialOnly: false 468 | }, 469 | 94: { 470 | name: "Players Hit", 471 | internal: "PlayersHit", 472 | allowIncrement: false, 473 | officialOnly: false 474 | }, 475 | 95: { 476 | name: "Assists", 477 | internal: "CosmeticAssists", 478 | allowIncrement: false, 479 | officialOnly: false 480 | }, 481 | 96: { 482 | name: "Contracts Completed", 483 | internal: "CosmeticOperationContractsCompleted", 484 | allowIncrement: false, 485 | officialOnly: false 486 | }, 487 | 97: { 488 | name: "Kills", 489 | internal: "CosmeticOperationKills", 490 | allowIncrement: false, 491 | officialOnly: false 492 | }, 493 | 98: { 494 | name: "Contract Points", 495 | internal: "CosmeticOperationContractsPoints", 496 | allowIncrement: true, 497 | officialOnly: false // I am not sure... 498 | }, 499 | 99: { 500 | name: "Contract Bonus Points", 501 | internal: "CosmeticOperationBonusObjectives", 502 | allowIncrement: true, 503 | officialOnly: false // I am not sure... 504 | }, 505 | 100: { 506 | name: "Times Performed", 507 | internal: "TauntsPerformed", 508 | allowIncrement: false, 509 | officialOnly: false 510 | }, 511 | 101: { 512 | name: "Kills and Assists during Invasion Event", 513 | internal: "InvasionKills", 514 | allowIncrement: false, 515 | officialOnly: false 516 | }, 517 | 102: { 518 | name: "Kills and Assists on 2Fort Invasion", 519 | internal: "InvasionKillsOnMap01", 520 | allowIncrement: false, 521 | officialOnly: false 522 | }, 523 | 103: { 524 | name: "Kills and Assists on Probed", 525 | internal: "InvasionKillsOnMap02", 526 | allowIncrement: false, 527 | officialOnly: false 528 | }, 529 | 104: { 530 | name: "Kills and Assists on Byre", 531 | internal: "InvasionKillsOnMap03", 532 | allowIncrement: false, 533 | officialOnly: false 534 | }, 535 | 105: { 536 | name: "Kills and Assists on Watergate", 537 | internal: "InvasionKillsOnMap04", 538 | allowIncrement: false, 539 | officialOnly: false 540 | }, 541 | 106: { 542 | name: "Souls Collected", 543 | internal: "HalloweenSouls", 544 | allowIncrement: false, 545 | officialOnly: false 546 | }, 547 | 107: { 548 | name: "Merasmissions Completed", 549 | internal: "HalloweenContractsCompleted", 550 | allowIncrement: false, 551 | officialOnly: false 552 | }, 553 | 108: { 554 | name: "Halloween Transmutes Performed", 555 | internal: "HalloweenOfferings", 556 | allowIncrement: false, 557 | officialOnly: false 558 | }, 559 | 109: { 560 | name: "Power Up Canteens Used", 561 | internal: "PowerupBottlesUsed", 562 | allowIncrement: false, 563 | officialOnly: false 564 | }, 565 | 110: { 566 | name: "Contract Points Earned", 567 | internal: "ContractPointsEarned", 568 | allowIncrement: true, 569 | officialOnly: false // I am not sure... 570 | }, 571 | 111: { 572 | name: "Contract Points Contributed To Friends", 573 | internal: "ContractPointsContributedToFriends", 574 | allowIncrement: true, 575 | officialOnly: false // I am not sure... 576 | } 577 | } 578 | }; 579 | -------------------------------------------------------------------------------- /helpers/Helper.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const URL = require("url"); 4 | const unzipper = require("unzipper"); 5 | const Protobufs = require("./Protobufs.js"); 6 | 7 | module.exports = class Helper { 8 | static async GetSteamAPI(interf, method, version, params) { 9 | let json = await this.getJSON("https://api.steampowered.com/" + interf + "/" + method + "/" + version, params); 10 | return json.response; 11 | } 12 | 13 | static async getJSON(url, qs) { 14 | let uri = new URL.URL(url); 15 | for (let key in qs) { 16 | uri.searchParams.append(key, qs[key]); 17 | } 18 | 19 | let res = await fetch(uri.href); 20 | return await res.json(); 21 | } 22 | 23 | static async downloadProtobufs(dir) { 24 | let protobufDir = path.join(dir, "protobufs"); 25 | fs.rmSync(protobufDir, { 26 | force: true, 27 | recursive: true 28 | }); 29 | 30 | // Yes I know the ones I download here are technically not the same as the ones in the submodule 31 | // but that doesn't really matter, I doubt Valve will do any major changes with the protobufs I use here anyways 32 | let res = await fetch("https://github.com/SteamDatabase/Protobufs/archive/master.zip"); 33 | let buf = Buffer.from(await res.arrayBuffer()); 34 | 35 | let zip = await unzipper.Open.buffer(buf); 36 | for (const file of zip.files) { 37 | if (file.type === "File") { 38 | const filePath = file.path.replace("Protobufs-master", "protobufs"); 39 | await fs.mkdirSync(path.dirname(filePath), { 40 | recursive: true 41 | }); 42 | await fs.writeFileSync(filePath, await file.buffer()); 43 | } 44 | } 45 | } 46 | 47 | static verifyProtobufs() { 48 | try { 49 | // Not a full verification, constructors are all missing but whatever 50 | let protobufs = new Protobufs([ 51 | { 52 | name: "steam", 53 | protos: [ 54 | path.join(__dirname, "..", "protobufs", "steam", "enums_clientserver.proto") 55 | ] 56 | }, 57 | { 58 | name: "tf2", 59 | protos: [ 60 | path.join(__dirname, "..", "protobufs", "tf2", "tf_gcmessages.proto"), 61 | path.join(__dirname, "..", "protobufs", "tf2", "gcsystemmsgs.proto"), 62 | path.join(__dirname, "..", "protobufs", "tf2", "econ_gcmessages.proto") 63 | ] 64 | }, 65 | { 66 | name: "csgo", 67 | protos: [ 68 | path.join(__dirname, "..", "protobufs", "csgo", "cstrike15_gcmessages.proto"), 69 | path.join(__dirname, "..", "protobufs", "csgo", "econ_gcmessages.proto"), 70 | path.join(__dirname, "..", "protobufs", "csgo", "gcsystemmsgs.proto") 71 | ] 72 | } 73 | ]); 74 | let verification = { 75 | "steam": { 76 | "EMsg": [ 77 | "k_EMsgClientAuthList", 78 | "k_EMsgClientAuthListAck", 79 | "k_EMsgGSServerType", 80 | "k_EMsgGSStatusReply", 81 | "k_EMsgClientConnectionStats", 82 | "k_EMsgAMGameServerUpdate", 83 | "k_EMsgGameServerOutOfDate" 84 | ] 85 | }, 86 | "tf2": { 87 | "ETFGCMsg": [ 88 | "k_EMsgGC_TFClientInit", 89 | "k_EMsgGC_GameServer_LevelInfo" 90 | ], 91 | "ESOMsg": [ 92 | "k_ESOMsg_CacheSubscriptionRefresh" 93 | ], 94 | "EGCItemMsg": [ 95 | "k_EMsgGC_IncrementKillCountAttribute_Multiple" 96 | ], 97 | "EGCBaseClientMsg": [ 98 | "k_EMsgGCClientHello", 99 | "k_EMsgGCClientWelcome", 100 | "k_EMsgGCServerHello" 101 | ] 102 | }, 103 | "csgo": { 104 | "ECsgoGCMsg": [ 105 | "k_EMsgGCCStrike15_v2_MatchmakingClient2GCHello", 106 | "k_EMsgGCCStrike15_v2_MatchmakingGC2ClientHello", 107 | "k_EMsgGCCStrike15_v2_ClientRequestJoinServerData", 108 | "k_EMsgGCCStrike15_v2_MatchmakingServerReservationResponse", 109 | "k_EMsgGCCStrike15_v2_Server2GCClientValidate", 110 | "k_EMsgGCCStrike15_v2_MatchEndRunRewardDrops" 111 | ], 112 | "EGCItemMsg": [ 113 | "k_EMsgGC_IncrementKillCountAttribute" 114 | ], 115 | "EGCBaseClientMsg": [ 116 | "k_EMsgGCClientHello", 117 | "k_EMsgGCClientWelcome", 118 | "k_EMsgGCServerHello" 119 | ] 120 | } 121 | }; 122 | 123 | for (let type in verification) { 124 | for (let key in verification[type]) { 125 | for (let msg of verification[type][key]) { 126 | if (typeof protobufs.data[type][key][msg] === "number") { 127 | continue; 128 | } 129 | 130 | return false; 131 | } 132 | } 133 | } 134 | 135 | return true; 136 | } catch (e) { 137 | return false; 138 | } 139 | } 140 | 141 | static deleteRecursive(dir) { 142 | return new Promise((resolve, reject) => { 143 | fs.readdir(dir, async (err, files) => { 144 | if (err) { 145 | reject(err); 146 | return; 147 | } 148 | 149 | for (let file of files) { 150 | let filePath = path.join(dir, file); 151 | let stat = fs.statSync(filePath); 152 | 153 | if (stat.isDirectory()) { 154 | await this.deleteRecursive(filePath); 155 | } else { 156 | await new Promise((res, rej) => { 157 | fs.unlink(filePath, (err) => { 158 | if (err) { 159 | rej(err); 160 | return; 161 | } 162 | 163 | res(); 164 | }); 165 | }); 166 | } 167 | } 168 | 169 | fs.rmdir(dir, (err) => { 170 | if (err) { 171 | reject(err); 172 | return; 173 | } 174 | 175 | resolve(); 176 | }); 177 | }); 178 | }); 179 | } 180 | } -------------------------------------------------------------------------------- /helpers/Protobufs.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const ByteBuffer = require("bytebuffer"); 4 | const Protobuf = require("protobufjs"); 5 | 6 | module.exports = class Protobufs { 7 | /** 8 | * @typedef protosObject 9 | * @type {Object} 10 | * @property {String} name Name ot use in the output 11 | * @property {Array.|String} protos Array of protobuf file paths to load OR directory path to load all 12 | */ 13 | 14 | /** 15 | * Parse an array of protobuf files 16 | * @param {Array.} protos Array of objets to parse 17 | * @param {Boolean} ignoreErrors Should we ignore errors or not 18 | * @returns {Object} 19 | */ 20 | constructor(protos, ignoreErrors = true) { 21 | this.data = {}; 22 | 23 | for (let proto of protos) { 24 | let root = new Protobuf.Root(); 25 | let files = Array.isArray(proto.protos) ? proto.protos : fs.readdirSync(proto.protos).map(file => path.join(proto.protos, file)); 26 | 27 | for (let file of files) { 28 | if (!file.endsWith(".proto") || !fs.existsSync(file)) { 29 | continue; 30 | } 31 | 32 | try { 33 | root = root.loadSync(file, { 34 | keepCase: true 35 | }); 36 | } catch (err) { 37 | if (!ignoreErrors) { 38 | throw err; 39 | } 40 | }; 41 | } 42 | 43 | this.data[proto.name] = root; 44 | } 45 | } 46 | 47 | /** 48 | * Decode a protobuf 49 | * @param {String|Object} protobuf Protobuf to decode buffer 50 | * @param {Buffer|ByteBuffer} buffer Buffer to decode 51 | * @returns {Object} 52 | */ 53 | decodeProto(protobuf, buffer) { 54 | if (typeof protobuf === "string") { 55 | let protobufName = protobuf; 56 | protobuf = this.data[protobufName]; 57 | 58 | if (!protobuf) { 59 | for (let key in this.data) { 60 | protobuf = this.data[key][protobufName]; 61 | if (protobuf) { 62 | break; 63 | } 64 | } 65 | } 66 | 67 | if (!protobuf) { 68 | return undefined; 69 | } 70 | } 71 | 72 | if (ByteBuffer.isByteBuffer(buffer)) { 73 | buffer = buffer.toBuffer(); 74 | } 75 | 76 | let decoded = protobuf.decode(buffer); 77 | return protobuf.toObject(decoded); 78 | } 79 | 80 | /** 81 | * Encode a protobuf 82 | * @param {String|Object} protobuf Protobuf to encode data 83 | * @param {Object} data Data to encode 84 | * @returns {Buffer} 85 | */ 86 | encodeProto(protobuf, data) { 87 | if (typeof protobuf === "string") { 88 | let protobufName = protobuf; 89 | protobuf = this.data[protobufName]; 90 | 91 | if (!protobuf) { 92 | for (let key in this.data) { 93 | protobuf = this.data[key][protobufName]; 94 | if (protobuf) { 95 | break; 96 | } 97 | } 98 | } 99 | 100 | if (!protobuf) { 101 | return undefined; 102 | } 103 | } 104 | 105 | let message = protobuf.create(data); 106 | let encoded = protobuf.encode(message); 107 | return encoded.finish(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const config = require("./config.json"); 2 | const Helper = require("./helpers/Helper.js"); 3 | const EventTypes = require("./helpers/EventTypes.js"); 4 | const allowedAppIDs = [440, 730]; 5 | 6 | (async () => { 7 | if (!allowedAppIDs.includes(Number(config.appID))) { 8 | console.log("Only supported app IDs are " + allowedAppIDs.join(", ")); 9 | return; 10 | } 11 | 12 | if (typeof config.repeat === "number" || typeof config.incrementValue !== "number") { 13 | console.log("Error: Invalid config detected."); 14 | console.log("Please read the README for up-to-date information: https://github.com/BeepIsla/fake-stattrak"); 15 | console.log(""); 16 | console.log("The 'repeat' value is no longer supported."); 17 | console.log("Using 'null' for 'incrementValue' is no longer supported."); 18 | console.log("This script now attempts to automatically calculate the 'repeat' value, you do not need it in your config anymore."); 19 | console.log("Please remove 'repeat' from your config and set 'incrementValue' to a valid number."); 20 | return; 21 | } 22 | 23 | let eventTypeInfo = EventTypes[config.appID]?.[config.eventType]; 24 | if (!eventTypeInfo) { 25 | console.log(`Unknown event type ${config.eventType} for app ${config.appID}`); 26 | return; 27 | } 28 | 29 | if (eventTypeInfo.officialOnly) { 30 | console.log(`Event type "${eventTypeInfo.name}" (${eventTypeInfo.internal}) can only be increased on Official Valve Servers`); 31 | return; 32 | } 33 | 34 | console.log("Validating protobufs..."); 35 | if (!Helper.verifyProtobufs()) { 36 | console.log("Failed to find protobufs, downloading..."); 37 | await Helper.downloadProtobufs(__dirname).catch((err) => { 38 | console.error(err); 39 | }); 40 | 41 | if (!Helper.verifyProtobufs()) { 42 | console.log("Failed to download protobufs."); 43 | return; 44 | } 45 | } else { 46 | console.log("Found protobufs!"); 47 | } 48 | 49 | console.log("Requiring files for appID " + config.appID); 50 | let Server = require("./components/Server_" + config.appID + ".js"); 51 | let Client = require("./components/Client_" + config.appID + ".js"); 52 | 53 | console.log("Creating constructors..."); 54 | let server = new Server(); 55 | let clients = [ 56 | new Client(), 57 | new Client() 58 | ]; 59 | 60 | console.log("Logging into main account..."); 61 | await clients[1].login(config.boostingAccount.username, config.boostingAccount.password); // Log onto main first cuz SteamGuard 62 | console.log("Successfully logged into Steam and signed up as main account as " + clients[1].steamID.getSteamID64()); 63 | 64 | console.log("Logging into Steam with bot and as server..."); 65 | let resolveds = await Promise.all([ 66 | clients[0].login(config.botAccount.username, config.botAccount.password), 67 | server.login() 68 | ]); 69 | let serverID = resolveds[1]; 70 | console.log("Successfully logged into Steam and signed up as bot account as " + clients[0].steamID.getSteamID64()); 71 | console.log("Successfully logged into Steam and signed up as server as " + server.steamID.getSteamID64()); 72 | 73 | for (let i = 0; i < clients.length; i++) { 74 | let ticket = await clients[i].generateTicket(); 75 | console.log("Fake joining server with bot account " + clients[i].steamID.getSteamID64() + "..."); 76 | await server.addPlayer(clients[i].steamID, ticket); 77 | console.log("Successfully added player to server, verifying on client..."); 78 | await clients[i].joinServer(serverID, ticket); 79 | console.log("Successfully connected client and server!"); 80 | } 81 | 82 | // A little delay just to make sure its all set and data has been received 83 | await new Promise(p => setTimeout(p, 1000)); 84 | 85 | await server.incrementKillCountAttribute(clients[1].steamID, clients[0].steamID, config.itemID, config.eventType, config.incrementValue); 86 | console.log("Item increments are now being processed by Valve. Depending on how much you incremented your item by this can take a while."); 87 | console.log("It is not guaranteed that all increments will get processed by the end of this delay."); 88 | console.log("Your inventory may be inaccessible for a few minutes."); 89 | 90 | // Random number: 2s per 200K (1M = 10s) 91 | let secondDelay = 2 * Math.ceil(config.incrementValue / 200_000); 92 | 93 | console.log(`Logging off in ${secondDelay} seconds... (You can exit early by closing the command prompt)`); 94 | console.log(`You can view your item here: https://steamcommunity.com/profiles/${clients[1].steamID.getSteamID64()}/inventory/#${config.appID}_2_${config.itemID}`); 95 | console.log("If the item does not show in your inventory using the link above you might have entered the wrong ItemID."); 96 | setTimeout(() => { 97 | server.logOff(); 98 | clients.forEach(c => c.logOff()); 99 | 100 | console.log("Successfully logged off"); 101 | 102 | // Something somewhere keeps the event loop alive and we never exit. So we just kill the process after a bit 103 | setTimeout(process.exit, 5000, 0).unref(); 104 | }, secondDelay * 1000); 105 | })(); 106 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fake-stattrak", 3 | "version": "1.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "fake-stattrak", 9 | "version": "1.0.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@doctormckay/stdlib": "^2.7.0", 13 | "bytebuffer": "^5.0.1", 14 | "protobufjs": "^7.1.2", 15 | "steam-user": "^5.0.4", 16 | "steamid": "^2.0.0", 17 | "unzipper": "^0.10.11" 18 | } 19 | }, 20 | "node_modules/@bbob/parser": { 21 | "version": "2.9.0", 22 | "resolved": "https://registry.npmjs.org/@bbob/parser/-/parser-2.9.0.tgz", 23 | "integrity": "sha512-tldSYsMoEclke/B1nqL7+HbYMWZHTKvpbEHRSHuY+sZvS1o7Jpdfjb+KPpwP9wLI3p3r7GPv69/wGy+Xibs9yA==", 24 | "dependencies": { 25 | "@bbob/plugin-helper": "^2.9.0" 26 | } 27 | }, 28 | "node_modules/@bbob/plugin-helper": { 29 | "version": "2.9.0", 30 | "resolved": "https://registry.npmjs.org/@bbob/plugin-helper/-/plugin-helper-2.9.0.tgz", 31 | "integrity": "sha512-idpUcNQ2co6T1oU/7/DG/ZRfipSSkTn9Ozw9f5vaXH7nzV3qhqZnhFVlHTzGGnRlzKlBwWOBzOdWi4Zeqg1c5A==" 32 | }, 33 | "node_modules/@doctormckay/stdlib": { 34 | "version": "2.10.0", 35 | "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-2.10.0.tgz", 36 | "integrity": "sha512-bwy+gPn6oa2KTpfxJKX3leZoV/wHDVtO0/gq3usPvqPswG//dcf3jVB8LcbRRsKO3BXCt5DqctOQ+Xb07ivxnw==", 37 | "dependencies": { 38 | "psl": "^1.9.0" 39 | }, 40 | "engines": { 41 | "node": ">=12.22.0" 42 | } 43 | }, 44 | "node_modules/@doctormckay/steam-crypto": { 45 | "version": "1.2.0", 46 | "resolved": "https://registry.npmjs.org/@doctormckay/steam-crypto/-/steam-crypto-1.2.0.tgz", 47 | "integrity": "sha512-lsxgLw640gEdZBOXpVIcYWcYD+V+QbtEsMPzRvjmjz2XXKc7QeEMyHL07yOFRmay+cUwO4ObKTJO0dSInEuq5g==" 48 | }, 49 | "node_modules/@doctormckay/user-agents": { 50 | "version": "1.0.0", 51 | "resolved": "https://registry.npmjs.org/@doctormckay/user-agents/-/user-agents-1.0.0.tgz", 52 | "integrity": "sha512-F+sL1YmebZTY2CnjoR9BXFEULpq7y8dxyLx48LZVa0BSDseXdLG/DtPISfM1iNv1XKCeiBzVNfAT/MOQ69v1Zw==" 53 | }, 54 | "node_modules/@protobufjs/aspromise": { 55 | "version": "1.1.2", 56 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 57 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 58 | }, 59 | "node_modules/@protobufjs/base64": { 60 | "version": "1.1.2", 61 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 62 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 63 | }, 64 | "node_modules/@protobufjs/codegen": { 65 | "version": "2.0.4", 66 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 67 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 68 | }, 69 | "node_modules/@protobufjs/eventemitter": { 70 | "version": "1.1.0", 71 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 72 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 73 | }, 74 | "node_modules/@protobufjs/fetch": { 75 | "version": "1.1.0", 76 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 77 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 78 | "dependencies": { 79 | "@protobufjs/aspromise": "^1.1.1", 80 | "@protobufjs/inquire": "^1.1.0" 81 | } 82 | }, 83 | "node_modules/@protobufjs/float": { 84 | "version": "1.0.2", 85 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 86 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 87 | }, 88 | "node_modules/@protobufjs/inquire": { 89 | "version": "1.1.0", 90 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 91 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 92 | }, 93 | "node_modules/@protobufjs/path": { 94 | "version": "1.1.2", 95 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 96 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 97 | }, 98 | "node_modules/@protobufjs/pool": { 99 | "version": "1.1.0", 100 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 101 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 102 | }, 103 | "node_modules/@protobufjs/utf8": { 104 | "version": "1.1.0", 105 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 106 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 107 | }, 108 | "node_modules/@types/long": { 109 | "version": "4.0.2", 110 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", 111 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" 112 | }, 113 | "node_modules/@types/node": { 114 | "version": "20.11.20", 115 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", 116 | "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", 117 | "dependencies": { 118 | "undici-types": "~5.26.4" 119 | } 120 | }, 121 | "node_modules/adm-zip": { 122 | "version": "0.5.10", 123 | "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", 124 | "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", 125 | "engines": { 126 | "node": ">=6.0" 127 | } 128 | }, 129 | "node_modules/agent-base": { 130 | "version": "6.0.2", 131 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 132 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 133 | "dependencies": { 134 | "debug": "4" 135 | }, 136 | "engines": { 137 | "node": ">= 6.0.0" 138 | } 139 | }, 140 | "node_modules/balanced-match": { 141 | "version": "1.0.2", 142 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 143 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 144 | }, 145 | "node_modules/big-integer": { 146 | "version": "1.6.52", 147 | "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", 148 | "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", 149 | "engines": { 150 | "node": ">=0.6" 151 | } 152 | }, 153 | "node_modules/binary": { 154 | "version": "0.3.0", 155 | "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", 156 | "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", 157 | "dependencies": { 158 | "buffers": "~0.1.1", 159 | "chainsaw": "~0.1.0" 160 | }, 161 | "engines": { 162 | "node": "*" 163 | } 164 | }, 165 | "node_modules/binarykvparser": { 166 | "version": "2.2.0", 167 | "resolved": "https://registry.npmjs.org/binarykvparser/-/binarykvparser-2.2.0.tgz", 168 | "integrity": "sha512-mGBKngQF9ui53THcMjgjd0LrBH/HsI2Vywfjq52udSAmRGG87h0vjhkqun0kF+iC4rQ2jLZqldwJE7YN2ueiWw==", 169 | "bundleDependencies": [ 170 | "long" 171 | ], 172 | "dependencies": { 173 | "long": "^3.2.0" 174 | } 175 | }, 176 | "node_modules/binarykvparser/node_modules/long": { 177 | "version": "3.2.0", 178 | "inBundle": true, 179 | "license": "Apache-2.0", 180 | "engines": { 181 | "node": ">=0.6" 182 | } 183 | }, 184 | "node_modules/bluebird": { 185 | "version": "3.4.7", 186 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", 187 | "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" 188 | }, 189 | "node_modules/brace-expansion": { 190 | "version": "1.1.11", 191 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 192 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 193 | "dependencies": { 194 | "balanced-match": "^1.0.0", 195 | "concat-map": "0.0.1" 196 | } 197 | }, 198 | "node_modules/buffer-indexof-polyfill": { 199 | "version": "1.0.2", 200 | "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", 201 | "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", 202 | "engines": { 203 | "node": ">=0.10" 204 | } 205 | }, 206 | "node_modules/buffers": { 207 | "version": "0.1.1", 208 | "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", 209 | "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", 210 | "engines": { 211 | "node": ">=0.2.0" 212 | } 213 | }, 214 | "node_modules/bytebuffer": { 215 | "version": "5.0.1", 216 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", 217 | "integrity": "sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ==", 218 | "dependencies": { 219 | "long": "~3" 220 | }, 221 | "engines": { 222 | "node": ">=0.8" 223 | } 224 | }, 225 | "node_modules/chainsaw": { 226 | "version": "0.1.0", 227 | "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", 228 | "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", 229 | "dependencies": { 230 | "traverse": ">=0.3.0 <0.4" 231 | }, 232 | "engines": { 233 | "node": "*" 234 | } 235 | }, 236 | "node_modules/concat-map": { 237 | "version": "0.0.1", 238 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 239 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 240 | }, 241 | "node_modules/core-util-is": { 242 | "version": "1.0.3", 243 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 244 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 245 | }, 246 | "node_modules/cuint": { 247 | "version": "0.2.2", 248 | "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", 249 | "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==" 250 | }, 251 | "node_modules/debug": { 252 | "version": "4.3.4", 253 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 254 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 255 | "dependencies": { 256 | "ms": "2.1.2" 257 | }, 258 | "engines": { 259 | "node": ">=6.0" 260 | }, 261 | "peerDependenciesMeta": { 262 | "supports-color": { 263 | "optional": true 264 | } 265 | } 266 | }, 267 | "node_modules/duplexer2": { 268 | "version": "0.1.4", 269 | "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", 270 | "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", 271 | "dependencies": { 272 | "readable-stream": "^2.0.2" 273 | } 274 | }, 275 | "node_modules/file-manager": { 276 | "version": "2.0.1", 277 | "resolved": "https://registry.npmjs.org/file-manager/-/file-manager-2.0.1.tgz", 278 | "integrity": "sha512-y/K/1OCha04OXOxzo3cXJYtIzEk/CUMBb7Okipxueu0u+xCiuoocbwPyh1smUBasOobo4GAYmjgjD9Vh5zI51w==", 279 | "dependencies": { 280 | "@doctormckay/stdlib": "^1.14.1" 281 | }, 282 | "engines": { 283 | "node": ">=8.0.0" 284 | } 285 | }, 286 | "node_modules/file-manager/node_modules/@doctormckay/stdlib": { 287 | "version": "1.16.1", 288 | "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.16.1.tgz", 289 | "integrity": "sha512-XhuUOzElz6fnNdt70IYNKqhPAEpGaL4JHOhAvklRh0hAhVPW+/wLxaWT3DWUbaG5Dta5YvIp7+cZK3GhIpAuug==", 290 | "engines": { 291 | "node": ">=6.0.0" 292 | } 293 | }, 294 | "node_modules/fs.realpath": { 295 | "version": "1.0.0", 296 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 297 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 298 | }, 299 | "node_modules/fstream": { 300 | "version": "1.0.12", 301 | "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", 302 | "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", 303 | "dependencies": { 304 | "graceful-fs": "^4.1.2", 305 | "inherits": "~2.0.0", 306 | "mkdirp": ">=0.5 0", 307 | "rimraf": "2" 308 | }, 309 | "engines": { 310 | "node": ">=0.6" 311 | } 312 | }, 313 | "node_modules/glob": { 314 | "version": "7.2.3", 315 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 316 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 317 | "dependencies": { 318 | "fs.realpath": "^1.0.0", 319 | "inflight": "^1.0.4", 320 | "inherits": "2", 321 | "minimatch": "^3.1.1", 322 | "once": "^1.3.0", 323 | "path-is-absolute": "^1.0.0" 324 | }, 325 | "engines": { 326 | "node": "*" 327 | }, 328 | "funding": { 329 | "url": "https://github.com/sponsors/isaacs" 330 | } 331 | }, 332 | "node_modules/graceful-fs": { 333 | "version": "4.2.11", 334 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 335 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 336 | }, 337 | "node_modules/inflight": { 338 | "version": "1.0.6", 339 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 340 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 341 | "dependencies": { 342 | "once": "^1.3.0", 343 | "wrappy": "1" 344 | } 345 | }, 346 | "node_modules/inherits": { 347 | "version": "2.0.4", 348 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 349 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 350 | }, 351 | "node_modules/ip-address": { 352 | "version": "9.0.5", 353 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", 354 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", 355 | "dependencies": { 356 | "jsbn": "1.1.0", 357 | "sprintf-js": "^1.1.3" 358 | }, 359 | "engines": { 360 | "node": ">= 12" 361 | } 362 | }, 363 | "node_modules/isarray": { 364 | "version": "1.0.0", 365 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 366 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 367 | }, 368 | "node_modules/jsbn": { 369 | "version": "1.1.0", 370 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", 371 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" 372 | }, 373 | "node_modules/kvparser": { 374 | "version": "1.0.2", 375 | "resolved": "https://registry.npmjs.org/kvparser/-/kvparser-1.0.2.tgz", 376 | "integrity": "sha512-5P/5qpTAHjVYWqcI55B3yQwSY2FUrYYrJj5i65V1Wmg7/4W4OnBcaodaEvLyVuugeOnS+BAaKm9LbPazGJcRyA==", 377 | "engines": { 378 | "node": ">=4.0.0" 379 | } 380 | }, 381 | "node_modules/listenercount": { 382 | "version": "1.0.1", 383 | "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", 384 | "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" 385 | }, 386 | "node_modules/long": { 387 | "version": "3.2.0", 388 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", 389 | "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==", 390 | "engines": { 391 | "node": ">=0.6" 392 | } 393 | }, 394 | "node_modules/lzma": { 395 | "version": "2.3.2", 396 | "resolved": "https://registry.npmjs.org/lzma/-/lzma-2.3.2.tgz", 397 | "integrity": "sha512-DcfiawQ1avYbW+hsILhF38IKAlnguc/fjHrychs9hdxe4qLykvhT5VTGNs5YRWgaNePh7NTxGD4uv4gKsRomCQ==", 398 | "bin": { 399 | "lzma.js": "bin/lzma.js" 400 | } 401 | }, 402 | "node_modules/minimatch": { 403 | "version": "3.1.2", 404 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 405 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 406 | "dependencies": { 407 | "brace-expansion": "^1.1.7" 408 | }, 409 | "engines": { 410 | "node": "*" 411 | } 412 | }, 413 | "node_modules/minimist": { 414 | "version": "1.2.8", 415 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 416 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 417 | "funding": { 418 | "url": "https://github.com/sponsors/ljharb" 419 | } 420 | }, 421 | "node_modules/mkdirp": { 422 | "version": "0.5.6", 423 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 424 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 425 | "dependencies": { 426 | "minimist": "^1.2.6" 427 | }, 428 | "bin": { 429 | "mkdirp": "bin/cmd.js" 430 | } 431 | }, 432 | "node_modules/ms": { 433 | "version": "2.1.2", 434 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 435 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 436 | }, 437 | "node_modules/node-bignumber": { 438 | "version": "1.2.2", 439 | "resolved": "https://registry.npmjs.org/node-bignumber/-/node-bignumber-1.2.2.tgz", 440 | "integrity": "sha512-VoTZHmdFQpZH1+q1dz2qcHNCwTWsJg2T3PYwlAyDNFOfVhSYUKQBLFcCpCud+wJBGgCttGavZILaIggDIKqEQQ==", 441 | "engines": { 442 | "node": ">=0.4.0" 443 | } 444 | }, 445 | "node_modules/once": { 446 | "version": "1.4.0", 447 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 448 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 449 | "dependencies": { 450 | "wrappy": "1" 451 | } 452 | }, 453 | "node_modules/path-is-absolute": { 454 | "version": "1.0.1", 455 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 456 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 457 | "engines": { 458 | "node": ">=0.10.0" 459 | } 460 | }, 461 | "node_modules/permessage-deflate": { 462 | "version": "0.1.7", 463 | "resolved": "https://registry.npmjs.org/permessage-deflate/-/permessage-deflate-0.1.7.tgz", 464 | "integrity": "sha512-EUNi/RIsyJ1P1u9QHFwMOUWMYetqlE22ZgGbad7YP856WF4BFF0B7DuNy6vEGsgNNud6c/SkdWzkne71hH8MjA==", 465 | "dependencies": { 466 | "safe-buffer": "*" 467 | }, 468 | "engines": { 469 | "node": ">=0.8.0" 470 | } 471 | }, 472 | "node_modules/process-nextick-args": { 473 | "version": "2.0.1", 474 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 475 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 476 | }, 477 | "node_modules/protobufjs": { 478 | "version": "7.2.6", 479 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", 480 | "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", 481 | "hasInstallScript": true, 482 | "dependencies": { 483 | "@protobufjs/aspromise": "^1.1.2", 484 | "@protobufjs/base64": "^1.1.2", 485 | "@protobufjs/codegen": "^2.0.4", 486 | "@protobufjs/eventemitter": "^1.1.0", 487 | "@protobufjs/fetch": "^1.1.0", 488 | "@protobufjs/float": "^1.0.2", 489 | "@protobufjs/inquire": "^1.1.0", 490 | "@protobufjs/path": "^1.1.2", 491 | "@protobufjs/pool": "^1.1.0", 492 | "@protobufjs/utf8": "^1.1.0", 493 | "@types/node": ">=13.7.0", 494 | "long": "^5.0.0" 495 | }, 496 | "engines": { 497 | "node": ">=12.0.0" 498 | } 499 | }, 500 | "node_modules/protobufjs/node_modules/long": { 501 | "version": "5.2.3", 502 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", 503 | "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" 504 | }, 505 | "node_modules/psl": { 506 | "version": "1.9.0", 507 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", 508 | "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" 509 | }, 510 | "node_modules/readable-stream": { 511 | "version": "2.3.8", 512 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", 513 | "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", 514 | "dependencies": { 515 | "core-util-is": "~1.0.0", 516 | "inherits": "~2.0.3", 517 | "isarray": "~1.0.0", 518 | "process-nextick-args": "~2.0.0", 519 | "safe-buffer": "~5.1.1", 520 | "string_decoder": "~1.1.1", 521 | "util-deprecate": "~1.0.1" 522 | } 523 | }, 524 | "node_modules/rimraf": { 525 | "version": "2.7.1", 526 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 527 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 528 | "dependencies": { 529 | "glob": "^7.1.3" 530 | }, 531 | "bin": { 532 | "rimraf": "bin.js" 533 | } 534 | }, 535 | "node_modules/safe-buffer": { 536 | "version": "5.1.2", 537 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 538 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 539 | }, 540 | "node_modules/setimmediate": { 541 | "version": "1.0.5", 542 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", 543 | "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" 544 | }, 545 | "node_modules/smart-buffer": { 546 | "version": "4.2.0", 547 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 548 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", 549 | "engines": { 550 | "node": ">= 6.0.0", 551 | "npm": ">= 3.0.0" 552 | } 553 | }, 554 | "node_modules/socks": { 555 | "version": "2.8.1", 556 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", 557 | "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", 558 | "dependencies": { 559 | "ip-address": "^9.0.5", 560 | "smart-buffer": "^4.2.0" 561 | }, 562 | "engines": { 563 | "node": ">= 10.0.0", 564 | "npm": ">= 3.0.0" 565 | } 566 | }, 567 | "node_modules/socks-proxy-agent": { 568 | "version": "7.0.0", 569 | "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", 570 | "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", 571 | "dependencies": { 572 | "agent-base": "^6.0.2", 573 | "debug": "^4.3.3", 574 | "socks": "^2.6.2" 575 | }, 576 | "engines": { 577 | "node": ">= 10" 578 | } 579 | }, 580 | "node_modules/sprintf-js": { 581 | "version": "1.1.3", 582 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", 583 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" 584 | }, 585 | "node_modules/steam-appticket": { 586 | "version": "1.0.1", 587 | "resolved": "https://registry.npmjs.org/steam-appticket/-/steam-appticket-1.0.1.tgz", 588 | "integrity": "sha512-oYVInCvJlPPaQPYW1+iGcVP0N0ZvwtWiCDM1Z353XJ8l4DXQI/N+R5yyaRQcHRH5oQv3+BY6gPF40lu7gwEiJw==", 589 | "dependencies": { 590 | "@doctormckay/stdlib": "^1.6.0", 591 | "@doctormckay/steam-crypto": "^1.2.0", 592 | "bytebuffer": "^5.0.1", 593 | "protobufjs": "^6.8.8", 594 | "steamid": "^1.1.0" 595 | }, 596 | "engines": { 597 | "node": ">=4.0.0" 598 | } 599 | }, 600 | "node_modules/steam-appticket/node_modules/@doctormckay/stdlib": { 601 | "version": "1.16.1", 602 | "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.16.1.tgz", 603 | "integrity": "sha512-XhuUOzElz6fnNdt70IYNKqhPAEpGaL4JHOhAvklRh0hAhVPW+/wLxaWT3DWUbaG5Dta5YvIp7+cZK3GhIpAuug==", 604 | "engines": { 605 | "node": ">=6.0.0" 606 | } 607 | }, 608 | "node_modules/steam-appticket/node_modules/long": { 609 | "version": "4.0.0", 610 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", 611 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" 612 | }, 613 | "node_modules/steam-appticket/node_modules/protobufjs": { 614 | "version": "6.11.4", 615 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", 616 | "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", 617 | "hasInstallScript": true, 618 | "dependencies": { 619 | "@protobufjs/aspromise": "^1.1.2", 620 | "@protobufjs/base64": "^1.1.2", 621 | "@protobufjs/codegen": "^2.0.4", 622 | "@protobufjs/eventemitter": "^1.1.0", 623 | "@protobufjs/fetch": "^1.1.0", 624 | "@protobufjs/float": "^1.0.2", 625 | "@protobufjs/inquire": "^1.1.0", 626 | "@protobufjs/path": "^1.1.2", 627 | "@protobufjs/pool": "^1.1.0", 628 | "@protobufjs/utf8": "^1.1.0", 629 | "@types/long": "^4.0.1", 630 | "@types/node": ">=13.7.0", 631 | "long": "^4.0.0" 632 | }, 633 | "bin": { 634 | "pbjs": "bin/pbjs", 635 | "pbts": "bin/pbts" 636 | } 637 | }, 638 | "node_modules/steam-appticket/node_modules/steamid": { 639 | "version": "1.1.3", 640 | "resolved": "https://registry.npmjs.org/steamid/-/steamid-1.1.3.tgz", 641 | "integrity": "sha512-t86YjtP1LtPt8D+TaIARm6PtC9tBnF1FhxQeLFs6ohG7vDUfQuy/M8II14rx1TTUkVuYoWHP/7DlvTtoCGULcw==", 642 | "dependencies": { 643 | "cuint": "^0.2.1" 644 | } 645 | }, 646 | "node_modules/steam-session": { 647 | "version": "1.7.2", 648 | "resolved": "https://registry.npmjs.org/steam-session/-/steam-session-1.7.2.tgz", 649 | "integrity": "sha512-BfOhwKqrzuiX9xeZ0X9IUhd3cwsZYzfGSkk51Oah7FM7JqDCDlcMrdv/1Q+YT7pBWpPCnnrncVkxiZ5mrRToCg==", 650 | "dependencies": { 651 | "@doctormckay/stdlib": "^2.9.0", 652 | "@doctormckay/user-agents": "^1.0.0", 653 | "debug": "^4.3.4", 654 | "kvparser": "^1.0.1", 655 | "node-bignumber": "^1.2.2", 656 | "protobufjs": "^7.1.0", 657 | "socks-proxy-agent": "^7.0.0", 658 | "steamid": "^2.0.0", 659 | "tiny-typed-emitter": "^2.1.0", 660 | "websocket13": "^4.0.0" 661 | }, 662 | "engines": { 663 | "node": ">=12.22.0" 664 | } 665 | }, 666 | "node_modules/steam-totp": { 667 | "version": "2.1.2", 668 | "resolved": "https://registry.npmjs.org/steam-totp/-/steam-totp-2.1.2.tgz", 669 | "integrity": "sha512-bTKlc/NoIUQId+my+O556s55DDsNNXfVIPWFDNVu68beql7AJhV0c+GTjFxfwCDYfdc4NkAme+0WrDdnY2D2VA==", 670 | "engines": { 671 | "node": ">=6.0.0" 672 | } 673 | }, 674 | "node_modules/steam-user": { 675 | "version": "5.0.8", 676 | "resolved": "https://registry.npmjs.org/steam-user/-/steam-user-5.0.8.tgz", 677 | "integrity": "sha512-PPOgZr+YpiuqY2msvcxWoDpNm58E4HHs1yg0BS8w854qIn23jTqKk8U4wj9V1KS8pWs/JJ9BzgwIMLBzQ1g0zw==", 678 | "dependencies": { 679 | "@bbob/parser": "^2.2.0", 680 | "@doctormckay/stdlib": "^2.9.1", 681 | "@doctormckay/steam-crypto": "^1.2.0", 682 | "adm-zip": "^0.5.10", 683 | "binarykvparser": "^2.2.0", 684 | "bytebuffer": "^5.0.0", 685 | "file-manager": "^2.0.0", 686 | "kvparser": "^1.0.1", 687 | "lzma": "^2.3.2", 688 | "protobufjs": "^7.2.4", 689 | "socks-proxy-agent": "^7.0.0", 690 | "steam-appticket": "^1.0.1", 691 | "steam-session": "^1.7.0", 692 | "steam-totp": "^2.0.1", 693 | "steamid": "^2.0.0", 694 | "websocket13": "^4.0.0" 695 | }, 696 | "engines": { 697 | "node": ">=14.0.0" 698 | } 699 | }, 700 | "node_modules/steamid": { 701 | "version": "2.0.0", 702 | "resolved": "https://registry.npmjs.org/steamid/-/steamid-2.0.0.tgz", 703 | "integrity": "sha512-+BFJMbo+IxzyfovLR37E7APkaNfmrL3S+88T7wTMRHnQ6LBhzEawPnjfWNKM9eUL/dH45j+7vhSX4WaGXoa4/Q==", 704 | "engines": { 705 | "node": ">=12.0.0" 706 | } 707 | }, 708 | "node_modules/string_decoder": { 709 | "version": "1.1.1", 710 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 711 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 712 | "dependencies": { 713 | "safe-buffer": "~5.1.0" 714 | } 715 | }, 716 | "node_modules/tiny-typed-emitter": { 717 | "version": "2.1.0", 718 | "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", 719 | "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" 720 | }, 721 | "node_modules/traverse": { 722 | "version": "0.3.9", 723 | "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", 724 | "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", 725 | "engines": { 726 | "node": "*" 727 | } 728 | }, 729 | "node_modules/undici-types": { 730 | "version": "5.26.5", 731 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 732 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 733 | }, 734 | "node_modules/unzipper": { 735 | "version": "0.10.14", 736 | "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", 737 | "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", 738 | "dependencies": { 739 | "big-integer": "^1.6.17", 740 | "binary": "~0.3.0", 741 | "bluebird": "~3.4.1", 742 | "buffer-indexof-polyfill": "~1.0.0", 743 | "duplexer2": "~0.1.4", 744 | "fstream": "^1.0.12", 745 | "graceful-fs": "^4.2.2", 746 | "listenercount": "~1.0.1", 747 | "readable-stream": "~2.3.6", 748 | "setimmediate": "~1.0.4" 749 | } 750 | }, 751 | "node_modules/util-deprecate": { 752 | "version": "1.0.2", 753 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 754 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 755 | }, 756 | "node_modules/websocket-extensions": { 757 | "version": "0.1.4", 758 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", 759 | "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", 760 | "engines": { 761 | "node": ">=0.8.0" 762 | } 763 | }, 764 | "node_modules/websocket13": { 765 | "version": "4.0.0", 766 | "resolved": "https://registry.npmjs.org/websocket13/-/websocket13-4.0.0.tgz", 767 | "integrity": "sha512-/ujP9ZfihyAZIXKGxcYpoe7Gj4r5o3WYSfP93o9lVNhhqoBtYba4m1s3mxdjKZu/HOhX5Mcqrt89dv/gC3b06A==", 768 | "dependencies": { 769 | "@doctormckay/stdlib": "^2.7.1", 770 | "bytebuffer": "^5.0.1", 771 | "permessage-deflate": "^0.1.7", 772 | "tiny-typed-emitter": "^2.1.0", 773 | "websocket-extensions": "^0.1.4" 774 | }, 775 | "engines": { 776 | "node": ">=12.22.0" 777 | } 778 | }, 779 | "node_modules/wrappy": { 780 | "version": "1.0.2", 781 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 782 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 783 | } 784 | } 785 | } 786 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fake-stattrak", 3 | "version": "1.0.1", 4 | "description": "Apply fake kills to your stattrak and strange weapons in CSGO and TF2", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "BeepIsla", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@doctormckay/stdlib": "^2.7.0", 14 | "bytebuffer": "^5.0.1", 15 | "protobufjs": "^7.1.2", 16 | "steam-user": "^5.0.4", 17 | "steamid": "^2.0.0", 18 | "unzipper": "^0.10.11" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/BeepIsla/fake-stattrak.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/BeepIsla/fake-stattrak/issues" 26 | }, 27 | "homepage": "https://github.com/BeepIsla/fake-stattrak#readme" 28 | } 29 | --------------------------------------------------------------------------------