├── .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 |
--------------------------------------------------------------------------------