Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save delasy/96c5340fc5e0617ddc1ff4ddb458d968 to your computer and use it in GitHub Desktop.
Save delasy/96c5340fc5e0617ddc1ff4ddb458d968 to your computer and use it in GitHub Desktop.
[FREE] Hamster Kombat playground promo code keys generator.

HamsterKombat Playground Promo Code Keys Generator

by @delasy

Warning

THIS SCRIPT IS NO LONGER MAINTAINED.

USE IT AT YOUR OWN RISK AFTER 2024-09-26.

This script is way more advanced than other scripts out there.

  • allows generating HamsterKombat coupon code for free using your machine.
  • acts as close as possible to real games with delays for game re-installations.
  • it doesn't use any proxies, it doesn't make any requests to external APIs.

If you are afraid that your IP will get blocked you can simply use VPN or proxy.

Important Notice

When new games are out I will need some time to test and update this gist.
I focus on making this gist as close as possible to real games.
Be patient. I Usually roll out update after 2PM UTC.
If you want to use other people insecure scripts with just app token and promo id set - it's up to you.
I’m using ios device and android emulator to test games and intercept requests.

All comments baiting people to use your implementation will be deleted.

Difference Between Real Games

First and the only difference is Unique Client ID.
For every request I generate unique client id.
For every request games re-use same client id.
I do this to be able to generate many keys without waiting.

Difference Between Other Generators

  1. Sends exactly the same headers as real games.
  2. Sends actual events extracted from the games.
  3. Uses randomized delays for events.
  4. Simulates app reinstall time.

How To Use It

Video tutorial: https://youtu.be/D0z52SyqNqI

With Node.js One-Liner (Linux, macOS)

node -e "$(curl -s https://gist.githubusercontent.com/delasy/96c5340fc5e0617ddc1ff4ddb458d968/raw/hamster-kombat-playground-games-promo-keys-generator.js)"

With Arguments:

node -e "..." -- --timing-strategy=fastest --k=1

With Node.js One-Liner (Windows)

node -e (Invoke-RestMethod -Uri 'https://gist.githubusercontent.com/delasy/96c5340fc5e0617ddc1ff4ddb458d968/raw/hamster-kombat-playground-games-promo-keys-generator.js')

With Node.js (Windows, Linux, macOS)

Copy hamster-kombat-playground-games-promo-keys-generator.js and run it with Node.js.

node hamster-kombat-playground-games-promo-keys-generator.js

Script Arguments

timing-strategy

--timing-strategy - What timing strategy to use. Realistic - takes longer but uses delays of real users, fastest - takes faster but has a great risk of your keys being removed during Airdrop.
 Values: realistice, fastest.
 Default: realistic.
 Example: --timing-strategy=fastest.

client-strategy

--client-strategy - What client strategy to use. Unique - generates unique client id for every new key, keep - generates only one client id per game and re-uses it for every key.
 Values: unique, keep.
 Default: unique.
 Example: --client-strategy=keep.

NOTE: After each key you have 5 minutes delay to redeem the code inside HamsterKombat, if you fail to do so it will generate same key over and over again.

device

--device, -d - Force script to use only one specific device.
 Values: android, ios.
 Default: random device selected for each key.
 Example: --device=ios, -d=android.

exclude

--exclude, -e - Game names to exclude.
 Default: empty string.
 Example: --exclude="BIKE, MERGE", -e=BIKE.

keys

--keys, -k - Number of keys to generate for each game. You can provide number of keys for specific games: BIKE:1 - this will generate 1 key for BIKE game and 0 keys for all other games. You can add a fallback 4,BIKE:1 - this will generate 1 key for BIKE game and 4 keys for all other games.
 Default: 4,FLUF:8.
 Example: --keys=MERGE:3, --keys="4,BIKE:1,TRIM:2", --keys=4, --k=1.

only

--only, -o - Script will process only names you provided with this option. This option has higher precedence and allows running even expired games. Option's games order is taken into account when generating keys.
 Default: all games.
 Example: --only="BIKE, MERGE", -o=BIKE.

debug

--debug - Whether to show debug data.
 Default: false.
 Example: --debug=true.

With Python (N/A)

This script is not available in Python :)
This is JS script that meant to be run with Node.js

How Long It Takes For One Key

Fastest Strategy

FCTRY ~ 3m
WATER ~ 3m
INFCT ~ 2m
PIN ~ 2m
COUNT ~ 5m
HIDE ~ 3m
BOUNC ~ 2m
STONE ~ 2m
FLUF ~ 2m
TILE ~ 2m
ZOO ~ 2m
GANGS ~ 9m
CAFE ~ 4m
TRIM ~ 3m
RACE ~ 2m
POLY ~ 1m
TWERK ~ 4m
MERGE ~ 3m
CLONE ~ 11m
CUBE ~ 1m
TRAIN ~ 2m
BIKE ~ 5m

Realistic Strategy

FCTRY ~ 19m
WATER ~ 12m
INFCT ~ 9m
PIN ~ 4m
COUNT ~ 10m
HIDE ~ 10m
BOUNC ~ 7m
STONE ~ 8m
FLUF ~ 8m
TILE ~ 10m
ZOO ~ 11m
GANGS ~ 20m
CAFE ~ 23m
TRIM ~ 8m
RACE ~ 7m
POLY ~ 5m
TWERK ~ 7m
MERGE ~ 8m
CLONE ~ 15m
CUBE ~ 9m
TRAIN ~ 12m
BIKE ~ 13m

During weekends numbers increase to almost 2x.

How I Get App Token and Promo ID

I run games on both physical device and emulator. I actually play all games (so you don't have to).
While doing this I intercept and inspect requests with Burp NoPE looking for clues.

Node.js Version

I personally test on Node.js v18 and v20.
On version 16 global fetch is unavailable so this version and below are not supported.

Support Author

If this gist helped you, consider helping me by giving a star to my main project:
github.com/thelang-io/the - a programming language I'm developing since 2018.
In advance, thanks a lot!

Changelog

1.16.0

  1. Added maintenance warning.

1.15.1

  1. Changed default number of keys for FLUF game.

1.15.0

  1. Added FCTRY and WATER games.

1.14.0

  1. Added INFCT game.

1.13.0

  1. Added PIN and COUNT games.

1.12.0

  1. Added HIDE game.

1.11.0

  1. Updated all games to latest versions.

1.10.0

  1. Added BOUNC game.

1.9.2

  1. Added iOS device support for STONE game.

1.9.1

  1. Updated realistic timings for STONE game.
  2. Send requests only as android device for STONE game.

1.9.0

  1. Added STONE game (android only).
  2. Added second user-agent variant for android MERGE game.
  3. Fixed bug with setting device option.
  4. Ability to specify custom number of keys with new keys syntax: --keys="4,FLUF:8".
  5. Option --only now allows to run even expired games.
  6. Option --only games order is taken into account when generating keys.

1.8.2

  1. Enabled back TILE game.
  2. Re-tested and adjusted timings for TILE game.
  3. Updated FLUF realistic timings, I was able to get keys faster by watching ad that speeds up the game.

1.8.1

  1. Fully finalized TILE game.
  2. Changed realistic timing for FLUF game.
  3. Disabled getting keys for TILE game as it's not in playground anymore.

1.8.0

  1. Added Game Promo API versioning.
  2. Introduced auth functionality for different vendors starting with cedar.games for iOS.
  3. Added FLUF game.
  4. Added half-finished TILE game (iOS only).

NOTE: There's no way to generate 8 keys for FLUF right now, you can generate another 4 keys with --only=FLUF

1.7.1

  1. Added expiration for CAFE and GANGS.

1.7.0

  1. Updated all games to latest version.
  2. Added JS template function _ for more compact design.

1.6.1

  1. Added correct ios headers/client id generation for GANGS and CAFE game.
  2. Adjusted realistic timing for GANGS game.

1.6.0

Added --client-strategy=keep option to keep same client id when generating multiple keys.

When the key was generated you have 5 minutes to enter it inside HamsterKombat before it will start generating next key.

/**
* HamsterKombat Playground Games Promo Code Keys Generator
* @author Aaron Delasy
* @version 1.16.0
*/
const DEBUG = parseArg(['debug'], (it) => (['true', 'false', ''].includes(it) ? it !== 'false' : null), false);
const CLIENT_STRATEGY = parseArg(['client-strategy'], (it) => (['keep', 'unique'].includes(it) ? it : null), 'unique');
const TIMING_STRATEGY = parseArg(['timing-strategy'], (it) => (['fastest', 'realistic'].includes(it) ? it : null), 'realistic');
const SERVER_ERROR_COOLDOWN = 300_000;
const SERVER_ERROR_RETRIES = 3;
const WITH_RANDOMIZED_DELAYS = true;
const WITH_REINSTALL_TIME = true;
const DEVICE = parseArg(['d', 'device'], (it) => (['android', 'ios'].includes(it) ? it : null));
const EXCLUDE = parseArg(['e', 'exclude'], (it) => it.split(',').map((it2) => it2.trim()).filter((it2) => it2 !== ''), []);
const KEYS = parseArg(['k', 'keys'], (it) => it.split(',').map((it2) => it2.trim()).filter((it2) => it2 !== ''), ['4']);
const ONLY = parseArg(['o', 'only'], (it) => it.split(',').map((it2) => it2.trim()).filter((it2) => it2 !== ''), []);
//
// Games
//
const GAMES = {
FCTRY: async ({ _, collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', 'd02fc404-8985-4305-87d8-32bd4e66bb16');
setup('promo-id', 'd02fc404-8985-4305-87d8-32bd4e66bb16');
setup('unity-version', '2022.3.46f1');
if (origin === 'ios') {
setup('user-agent', 'FactoryWorld/75 CFNetwork/1568.100.1 Darwin/24.0.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.46f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login(1, { clientId: id('uuid'), clientOrigin: origin, clientVersion: '1.43.18' });
await getClient(1);
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 150_000 : 20_000);
await event(1, { eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'HamsterGathred' });
}
await collect(1);
},
WATER: async ({ _, collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', 'daab8f83-8ea2-4ad0-8dd5-d33363129640');
setup('promo-id', 'daab8f83-8ea2-4ad0-8dd5-d33363129640');
setup('unity-version', _`ios ? 2022.3.25f1 : 2022.3.46f1`);
if (origin === 'ios') {
setup('user-agent', 'AmongWater/1 CFNetwork/1568.100.1 Darwin/24.0.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.46f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login(1, { clientId: id(_`ios ? UUID : h32`), clientOrigin: origin, clientVersion: _`ios ? 1.0.37 : 1.0.40` });
await getClient(1);
if (TIMING_STRATEGY === 'realistic') {
await delay(180_000);
}
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 30_000 : 20_000);
await event(1, { eventId: id('uuid'), eventOrigin: 'undefined' });
if (TIMING_STRATEGY === 'realistic') {
await delay(60_000);
}
await getClient(1);
}
await collect(1);
},
INFCT: async ({ collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'eb518c4b-e448-4065-9d33-06f3039f0fcb');
setup('promo-id', 'eb518c4b-e448-4065-9d33-06f3039f0fcb');
setup('unity-version', '2022.3.21f1');
if (origin === 'ios') {
setup('user-agent', 'InfectedFrontier/11 CFNetwork/1568.100.1 Darwin/24.0.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.21f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id('uuid'), clientOrigin: origin });
if (TIMING_STRATEGY === 'realistic') {
await delay(70_000);
}
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 80_000 : 20_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'biker' });
}
await collect();
},
PIN: async ({ _, collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', 'd2378baf-d617-417a-9d99-d685824335f0');
setup('promo-id', 'd2378baf-d617-417a-9d99-d685824335f0');
setup('unity-version', '2022.3.41f1');
if (origin === 'ios') {
setup('user-agent', 'PinOutMaster/3 CFNetwork/1568.100.1 Darwin/24.0.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.41f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login(1, { clientId: id(_`ios ? UUID : h32`), clientOrigin: origin, clientVersion: '1.2.6' });
const eventId = id('uuid');
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 40_000 : 20_000);
await event(1, { eventId, eventOrigin: 'undefined', eventType: 'undefined' });
await getClient(1);
}
await collect(1);
},
COUNT: async ({ _, collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', '4bdc17da-2601-449b-948e-f8c7bd376553');
setup('promo-id', '4bdc17da-2601-449b-948e-f8c7bd376553');
setup('unity-version', '2022.3.30f1');
if (origin === 'ios') {
setup('user-agent', 'CountMaster/445 CFNetwork/1568.100.1 Darwin/24.0.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.30f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login(1, { clientId: id(_`ios ? ts-d7 : ts-d19`), clientOrigin: origin, clientVersion: _`ios ? 1.84.6 : 1.83.10` });
await getClient(1);
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 40_000 : 20_000);
await event(1, { eventId: 'StartLevel', eventOrigin: 'undefined' });
}
await collect(1);
},
HIDE: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '4bf4966c-4d22-439b-8ff2-dc5ebca1a600');
setup('promo-id', '4bf4966c-4d22-439b-8ff2-dc5ebca1a600');
setup('unity-version', '2022.3.38f1');
if (origin === 'ios') {
setup('user-agent', 'HideBall/1 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.38f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login(1, { clientId: id(_`ios ? UUID : h32`), clientOrigin: origin, clientVersion: '1.2.4' });
const eventId = id('uuid');
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 30_000 : 10_000);
await event(1, { eventId, eventOrigin: 'undefined', eventType: 'undefined' });
}
await collect(1);
},
BOUNC: async ({ collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'bc72d3b9-8e91-4884-9c33-f72482f0db37');
setup('promo-id', 'bc72d3b9-8e91-4884-9c33-f72482f0db37');
setup('unity-version', '2021.3.15f1');
if (origin === 'ios') {
setup('user-agent', 'Bouncemasters/2 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2021.3.15f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)');
}
await login(1, { clientId: id('uuid'), clientOrigin: origin, clientVersion: '2.4.2' });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 360_000 : 60_000);
await event(1, { eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'test' });
}
await collect(1);
},
STONE: async ({ _, collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', '04ebd6de-69b7-43d1-9c4b-04a6ca3305af');
setup('promo-id', '04ebd6de-69b7-43d1-9c4b-04a6ca3305af');
if (origin === 'ios') {
setup('user-agent', 'Java0');
} else {
if (Math.random() < 0.5) {
setup('user-agent', 'Dalvik/2.1.0 (Linux; U; Android 12; SM-S9110 Build/W528JS)');
} else {
setup('user-agent', 'Dalvik/2.1.0 (Linux; U; Android 13; 24030PN60G Build/TQ3A.230901.001)');
}
}
await login(1, { clientId: id('H16'), clientOrigin: origin, clientVersion: _`ios ? 1.113.2 : 1.113.114` });
await getClient(1);
if (TIMING_STRATEGY === 'realistic') {
await delay(210_000);
}
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 20_000 : 10_000);
await event(1, { eventId: id('d7'), eventOrigin: 'undefined' });
}
await collect(1);
},
FLUF: async ({ collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', '112887b0-a8af-4eb2-ac63-d82df78283d9');
setup('promo-id', '112887b0-a8af-4eb2-ac63-d82df78283d9');
setup('unity-version', '2022.3.27f1');
if (origin === 'ios') {
setup('user-agent', 'FluffCrusade/321 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.27f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login(1, { clientId: id('uuid'), clientOrigin: 'deviceid' });
await getClient(1);
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 420_000 : 120_000);
await event(1, { eventId: id('uuid'), eventOrigin: 'undefined' });
}
await collect(1);
},
TILE: async ({ _, auth, collect, delay, event, getClient, id, instance, login, origin, setup }) => {
setup('app-token', 'e68b39d2-4880-4a31-b3aa-0393e7df10c7');
setup('promo-id', 'e68b39d2-4880-4a31-b3aa-0393e7df10c7');
setup('unity-version', '2020.3.48f1');
if (origin === 'ios') {
setup('user-agent', 'TileTrio/3 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2020.3.48f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)');
}
const clientId = await auth('cedar.games');
await login(1, { clientId, clientOrigin: 'deviceid', clientVersion: _`ios ? 12.4.3 : 12.4.57` });
await getClient(1);
if (TIMING_STRATEGY === 'realistic') {
await delay(420_000);
}
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 30_000 : 20_000);
await event(1, { eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'gt_progress' });
}
await collect(1);
},
ZOO: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'b2436c89-e0aa-4aed-8046-9b0515e1c46b');
setup('promo-id', 'b2436c89-e0aa-4aed-8046-9b0515e1c46b');
setup('unity-version', '2022.3.15f1');
if (origin === 'ios') {
setup('user-agent', 'Zoopolis/1 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.15f1 (UnityWebRequest/1.0, libcurl/8.4.0-DEV)');
}
await login({ clientId: id(_`ios ? UUID : h32`), clientOrigin: origin, clientVersion: _`ios ? 1.2.8 : 1.2.7` });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 120_000 : 20_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'ZoopolisEvent' });
}
await collect();
},
GANGS: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'b6de60a0-e030-48bb-a551-548372493523');
setup('promo-id', 'c7821fa7-6632-482c-9635-2bd5798585f9');
setup('unity-version', '2022.3.41f1');
if (origin === 'ios') {
setup('user-agent', 'UrbanCrimeLifeCityHustle/0 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.41f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id(_`ios ? s5_UUID : s5_h32`), clientOrigin: origin });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 80_000 : 40_000);
await event({ eventId: id('h16-h16'), eventOrigin: 'undefined' });
}
await collect();
},
CAFE: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'bc0971b8-04df-4e72-8a3e-ec4dc663cd11');
setup('promo-id', 'bc0971b8-04df-4e72-8a3e-ec4dc663cd11');
setup('user-agent', 'Mozilla/5.0');
await login({ clientId: id(_`ios ? UUID : h16`), clientOrigin: origin, clientVersion: '2.24.1' });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 90_000 : 20_000);
await event({ eventId: id('ts'), eventOrigin: 'undefined', eventType: '5visitorsChecks' });
}
await collect();
},
TRIM: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'ef319a80-949a-492e-8ee0-424fb5fc20a6');
setup('promo-id', 'ef319a80-949a-492e-8ee0-424fb5fc20a6');
setup('unity-version', '2021.3.17f1');
if (origin === 'ios') {
setup('user-agent', 'MowandTrim/197 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2021.3.17f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)');
}
await login({ clientId: id(_`ios ? ts-d7 : ts-d19`), clientOrigin: origin });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 50_000 : 20_000);
await event({ eventId: 'StartLevel', eventOrigin: 'undefined' });
}
await collect();
},
RACE: async ({ collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '8814a785-97fb-4177-9193-ca4180ff9da8');
setup('promo-id', '8814a785-97fb-4177-9193-ca4180ff9da8');
setup('unity-version', '2020.3.18f1');
if (origin === 'ios') {
setup('user-agent', 'Truckbountyhole/12 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2020.3.18f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id('uuid'), clientOrigin: origin });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 60_000 : 20_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'racing' });
}
await collect();
},
POLY: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '2aaf5aee-2cbc-47ec-8a3f-0962cc14bc71');
setup('promo-id', '2aaf5aee-2cbc-47ec-8a3f-0962cc14bc71');
setup('unity-version', '2021.3.39f1');
if (origin === 'ios') {
setup('user-agent', 'Polysphere/161 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2021.3.39f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id('uuid'), clientOrigin: origin, clientVersion: _`ios ? 1.15.32 : 1.15.301` });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 10_000 : 3_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'test' });
}
await collect();
},
TWERK: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '61308365-9d16-4040-8bb0-2f4a4c69074c');
setup('promo-id', '61308365-9d16-4040-8bb0-2f4a4c69074c');
setup('unity-version', '2021.3.15f1');
if (origin === 'ios') {
setup('user-agent', 'Twerk/491 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2021.3.15f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)');
}
await login({ clientId: id(_`ios ? ts-d7 : ts-d19`), clientOrigin: origin });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 30_000 : 20_000);
await event({ eventId: 'StartLevel', eventOrigin: 'undefined' });
}
await collect();
},
MERGE: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '8d1cc2ad-e097-4b86-90ef-7a27e19fb833');
setup('promo-id', 'dc128d28-c45b-411c-98ff-ac7726fbaea4');
if (origin === 'ios') {
setup('user-agent', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148');
} else {
if (Math.random() < 0.5) {
setup('user-agent', 'Mozilla/5.0 (Linux; Android 12; SM-S9110 Build/W528JS; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/95.0.4638.74 Mobile Safari/537.36');
} else {
setup('user-agent', 'Mozilla/5.0 (Linux; Android 13; 24030PN60G Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/117.0.0.0 Mobile Safari/537.36');
}
}
await login(1, { clientId: id(_`ios ? ts-d7 : ts-d19`), clientOrigin: origin });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 60_000 : 20_000);
await event(1, { eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'spend-energy' });
}
await collect(1);
},
CLONE: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '74ee0b5b-775e-4bee-974f-63e7f4d5bacb');
setup('promo-id', 'fe693b26-b342-4159-8808-15e3ff7f8767');
setup('unity-version', '2022.3.25f1');
if (origin === 'ios') {
setup('user-agent', 'Myclonearmy/12 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.25f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id(_`ios ? UUID : h32`), clientOrigin: origin });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 150_000 : 120_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'MiniQuest' });
}
await collect();
},
CUBE: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', 'd1690a07-3780-4068-810f-9b5bbf2931b2');
setup('promo-id', 'b4170868-cef0-424f-8eb9-be0622e8e8e3');
setup('unity-version', '2022.3.20f1');
if (origin === 'ios') {
setup('user-agent', 'ChainCube/4 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.20f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id('uuid'), clientOrigin: origin, clientVersion: _`ios ? 1.78.39 : 1.78.42` });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 150_000 : 20_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'cube_sent' });
}
await collect();
},
TRAIN: async ({ _, collect, delay, event, id, instance, login, origin, setup }) => {
setup('app-token', '82647f43-3f87-402d-88dd-09a90025313f');
setup('promo-id', 'c4480ac7-e178-4973-8061-9ed5b2e17954');
setup('unity-version', '2022.3.20f1');
if (origin === 'ios') {
setup('user-agent', 'TrainMiner/20 CFNetwork/1498.700.2 Darwin/23.6.0');
} else {
setup('user-agent', 'UnityPlayer/2022.3.20f1 (UnityWebRequest/1.0, libcurl/8.5.0-DEV)');
}
await login({ clientId: id(_`ios ? UUID : h32`), clientOrigin: origin, clientVersion: _`ios ? 2.4.16 : 2.6.4` });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 600_000 : 120_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined', eventType: 'hitStatue' });
}
await collect();
},
BIKE: async ({ _, collect, delay, event, id, instance, login, setup }) => {
setup('app-token', 'd28721be-fd2d-4b45-869e-9f253b554e50');
setup('promo-id', '43e35910-c168-4634-ad4f-52fd764a843f');
await login({ clientId: id(_`ios ? ts-d7 : ts-d19`), clientOrigin: _`android ? deviceid : ios` });
while (!instance.hasCode) {
await delay(TIMING_STRATEGY === 'realistic' ? 50_000 : 20_000);
await event({ eventId: id('uuid'), eventOrigin: 'undefined' });
}
await collect();
},
};
const GAMES_EXPIRATIONS = {
BIKE: new Date('2024-08-30T07:30:00.000Z'),
CAFE: new Date('2024-09-02T07:30:00.000Z'),
CLONE: new Date('2024-08-26T00:00:00.000Z'),
GANGS: new Date('2024-09-02T07:30:00.000Z'),
RACE: new Date('2024-08-30T07:30:00.000Z'),
};
const CLIENT = {};
//
// Functions
//
function debug(...args) {
if (!DEBUG) {
return;
}
console.error.apply(null, [new Date(), ...args]);
}
async function globalDelay(ms) {
debug(`Waiting ${ms}ms`);
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function globalFetch(url, options, retry = 0) {
debug(url, options);
let res;
try {
res = await fetch(url, options);
} catch (err) {
if (retry < SERVER_ERROR_RETRIES) {
console.error('Received network error, will retry after cooldown period.');
debug(err);
await globalDelay(SERVER_ERROR_COOLDOWN);
return globalFetch(url, options, retry + 1);
}
throw err;
}
if (!res.ok) {
if (DEBUG) {
const text = await res.text();
debug(text);
}
if (retry < SERVER_ERROR_RETRIES) {
console.error('Received internal server error, will retry after cooldown period.');
await globalDelay(SERVER_ERROR_COOLDOWN);
return globalFetch(url, options, retry + 1);
}
throw new Error(`${res.status} ${res.statusText}`);
}
const data = await res.json();
debug(data);
return data;
}
function randomBytes(len) {
return Array.from(
crypto.getRandomValues(new Uint8Array(len / 2)),
(it) => it.toString(16).padStart(2, '0'),
).join('');
}
function randomDigits(len) {
const buf = Array(len).fill(null);
return buf.map(() => Math.floor(Math.random() * 10)).join('');
}
function randomString(len, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
const charsLen = chars.length;
let result = '';
for (let i = 0; i < len; i++) {
result += chars.charAt(Math.floor(Math.random() * charsLen));
}
return result;
}
function uuidv4() {
return '10000000-1000-4000-8000-100000000000'.replace(
/[018]/g,
(c) => (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16),
);
}
async function getPromoCode(gp, gameKey) {
return gp.getCode(gameKey);
}
/**
* Generates random string with provided type.
* Types explanation:
* ts - timestamp.
* h5 - lowercase version of random string in hex format of length 5.
* s5 - lowercase version of random string of length 5.
* 5d - random string of digits of length 5.
* UUID - uppercase version of string in UUID v4 format.
* uuid - lowercase version of string in UUID v4 format.
* uuid-0 - lowercase version of NULL value of UUID v4 format.
*/
function globalId(type) {
switch (type) {
case 'd7': {
return randomDigits(7);
}
case 'H16': {
return randomBytes(16).toUpperCase();
}
case 'h16': {
return randomBytes(16);
}
case 'h16-h16': {
return `${randomBytes(16)}-${randomBytes(16)}`;
}
case 'h32': {
return randomBytes(32);
}
case 's5_h32': {
return `${randomString(5, 'abcdefghijklmnopqrstuvwxyz0123456789')}_${randomBytes(32)}`;
}
case 's5_UUID': {
return `${randomString(5, 'abcdefghijklmnopqrstuvwxyz0123456789')}_${uuidv4().toUpperCase()}`;
}
case 'ts': {
return Date.now().toString();
}
case 'ts-d7': {
return `${Date.now()}-${randomDigits(7)}`;
}
case 'ts-d19': {
return `${Date.now()}-${randomDigits(19)}`;
}
case 'UUID': {
return uuidv4().toUpperCase();
}
case 'uuid': {
return uuidv4();
}
case 'uuid-0': {
return '00000000-0000-0000-0000-000000000000';
}
default: {
throw new Error(`Tried generating unknown id '${type}'.`);
}
}
}
function parseArg(names, parser, fallback = null) {
if (typeof process === 'undefined' || !Array.isArray(process.argv)) {
return fallback;
}
for (let i = 1; i < process.argv.length; i++) {
const arg = process.argv[i];
for (let j = 0; j < names.length; j++) {
const name = names[j];
if (arg.toLowerCase().startsWith(`--${name}=`)) {
const val = arg.slice(name.length + 3);
const parsed = parser(val);
if (parsed !== null && name !== 'debug') {
debug(`Applied filter "${name}":`, parsed);
}
return parsed === null ? fallback : parsed;
}
}
}
return fallback;
}
function filterExpired(gameKey) {
if (!Object.prototype.hasOwnProperty.call(GAMES_EXPIRATIONS, gameKey)) {
return true;
}
return GAMES_EXPIRATIONS[gameKey] > new Date();
}
function filterNonExisting(gameKey) {
return Object.prototype.hasOwnProperty.call(GAMES, gameKey);
}
function keysTotal(gameKey) {
let fallbackKey = null;
for (const key of KEYS) {
if (!key.includes(':') && fallbackKey === null) {
fallbackKey = key;
}
if (key.startsWith(`${gameKey}:`)) {
const num = Number.parseInt(key.slice(gameKey.length + 1), 10);
if (isNaN(num) || num < 0) {
throw new Error(`Key '${key}' has invalid syntax, try with '4,FLUF:8'.`);
}
return num;
}
}
if (fallbackKey !== null) {
const num = Number.parseInt(fallbackKey, 10);
if (isNaN(num) || num < 0) {
throw new Error(`Key '${fallbackKey}' has invalid syntax, try with '4,FLUF:8'.`);
}
return num;
}
return 0;
}
//
// Classes
//
class GamePromo {
constructor() {
this.authToken = null;
this.config = {};
this.gameKey = null;
this.hasCode = false;
this.key = null;
this.origin = null;
}
_(strings, ...values) {
const template = String.raw({ raw: strings }, ...values);
const result = /^([\w ./(),-]+)\s+\?\s+([\w ./(),-]+)\s+:\s+([\w ./(),-]+)$/.exec(template);
if (result === null) {
throw new Error(`Unable to preprocess '${template}'`);
}
const [, condition, consequent, alternate] = result;
if (condition !== 'android' && condition !== 'ios') {
throw new Error(`Unable to preprocess condition '${condition}' in '${template}'`);
}
const isAndroidAndroid = condition === 'android' && this.origin === 'android';
const isIosIos = condition === 'ios' && this.origin === 'ios';
return isAndroidAndroid || isIosIos ? consequent : alternate;
}
async fetchApi(version, path, body = null) {
const headers = {};
if (this.authToken !== null) {
headers['Authorization'] = `Bearer ${this.authToken}`;
}
if (this.config['user-agent'] !== undefined) {
headers['User-Agent'] = this.config['user-agent'];
}
if (this.config['unity-version'] !== undefined) {
headers['X-Unity-Version'] = this.config['unity-version'];
}
const versionPath = version === 0 ? '' : `/${version.toString()}`;
const url = `https://api.gamepromo.io/promo${versionPath}${path}`;
const bodyText = JSON.stringify(body);
return globalFetch(url, {
method: 'POST',
headers: {
'Accept': '*/*',
'Accept-Encoding': 'deflate, gzip',
'Accept-Language': 'en-US,en;q=0.9',
'Content-Length': bodyText.length.toString(),
'Content-Type': 'application/json',
...headers,
},
body: bodyText,
});
}
async setConfig(key, value) {
this.config[key] = value;
}
async authFetch(vendor) {
if (CLIENT_STRATEGY === 'keep' && CLIENT[this.gameKey] !== undefined) {
debug('Re-using auth');
return;
}
switch (vendor) {
case 'cedar.games': {
const body = new URLSearchParams({
method: '_post',
data: JSON.stringify({
'deviceId': globalId('uuid'),
'socialUserId': '',
'network': 'fb',
'UTCOffset': '3',
'version': this._`ios ? 12.4.3 : 12.4.57`,
'clientMergeAware': true,
'ads_id': this._`ios ? ${globalId('uuid-0')} : ${globalId('uuid')}`,
...(this.origin === 'ios' ? { 'apple_id': '' } : { 'android_id': globalId('h16') }),
'device_model': this._`ios ? iPhone11,6 : Samsung SM-S9110`,
'memory': this._`ios ? 3754 : 2999`,
'os': this._`ios ? iOS 17.6.1 : Android OS 12 / API-32 (W528JS/228)`,
'screen_width': this._`ios ? 1242 : 1080`,
'screen_height': this._`ios ? 2688 : 1920`,
'screen_size': this._`ios ? 6.465209 : 6.119187`,
}),
}).toString();
const res = await globalFetch('https://app-t2d.cedar.games/mobile/auth', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'Content-Length': body.length.toString(),
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': this.config['user-agent'],
'X-Unity-Version': this.config['unity-version'],
},
body,
});
return res.userId.toString();
}
}
throw new Error('Unknown auth provider.');
}
async loginFetch(versionOrData, data) {
if (CLIENT_STRATEGY === 'keep' && CLIENT[this.gameKey] !== undefined) {
debug('Re-using auth token');
return;
}
const version = typeof versionOrData === 'number' ? versionOrData : 0;
const finalData = typeof versionOrData !== 'number' ? versionOrData : data;
const res = await this.fetchApi(version, '/login-client', {
appToken: this.config['app-token'],
...finalData,
});
if (typeof res.clientToken === 'string' && res.clientToken !== '') {
this.authToken = res.clientToken;
CLIENT[this.gameKey] = {
authToken: this.authToken,
origin: this.origin,
};
}
}
async getClientFetch(version) {
const promoId = this.config['promo-id'];
await this.fetchApi(version, '/get-client', { promoId });
}
async eventFetch(versionOrData, data) {
const version = typeof versionOrData === 'number' ? versionOrData : 0;
const finalData = typeof versionOrData !== 'number' ? versionOrData : data;
const promoId = this.config['promo-id'];
// on ios promoId is sent as first property, on android it's sent last
const payload = this.origin === 'ios' ? { promoId, ...finalData } : { ...finalData, promoId };
const res = await this.fetchApi(version, '/register-event', payload);
if (res.hasCode === true) {
this.hasCode = true;
}
}
async collectFetch(versionOrNull = null) {
const version = versionOrNull ?? 0;
const res = await this.fetchApi(version, '/create-code', {
promoId: this.config['promo-id'],
});
if (typeof res.promoCode === 'string' && res.promoCode !== '') {
this.key = res.promoCode;
}
}
async getCode(gameKey) {
this.authToken = null;
this.config = {};
this.gameKey = gameKey;
this.hasCode = false;
this.key = null;
this.origin = DEVICE !== null ? DEVICE : Math.random() < 0.5 ? 'ios' : 'android';
if (CLIENT[this.gameKey] !== undefined) {
this.authToken = CLIENT[this.gameKey].authToken;
this.origin = CLIENT[this.gameKey].origin;
}
debug('Origin:', this.origin);
await GAMES[gameKey]({
_: this._.bind(this),
collect: this.collectFetch.bind(this),
delay: async (ms) => {
if (WITH_RANDOMIZED_DELAYS) {
const randomMs = Math.floor(ms * (Math.random() / 4 + 1));
await globalDelay(randomMs);
} else {
await globalDelay(ms);
}
},
id: globalId,
instance: this,
auth: this.authFetch.bind(this),
login: this.loginFetch.bind(this),
getClient: this.getClientFetch.bind(this),
event: this.eventFetch.bind(this),
origin: this.origin,
setup: this.setConfig.bind(this),
});
if (this.key === null) {
throw new Error(`Unable to get ${gameKey} promo.`);
}
return this.key;
}
}
class Queue {
constructor() {
const self = this;
this.items = [];
this.workers = [
{
id: uuidv4(),
available: true,
async run(item) {
item.started = true;
this.available = false;
await item.cb();
this.available = true;
self.tick();
},
},
];
}
hasAvailableWorkers() {
return this.workers.some((it) => it.available);
}
nextAvailableWorker() {
return this.workers.find((it) => it.available);
}
tick() {
debug('Queue tick');
if (!this.hasAvailableWorkers()) {
return;
}
for (let i = 0; i < this.items.length; i++) {
const item = this.items[i];
if (item.started) {
continue;
}
debug('Running:', item.id);
this.nextAvailableWorker().run(item);
if (!this.hasAvailableWorkers()) {
return;
}
}
}
push(cb) {
this.items.push({
id: uuidv4(),
cb,
started: false,
});
this.tick();
}
}
//
// Main
//
async function main() {
const gameKeys = ONLY.length === 0
? Object.keys(GAMES).filter((it) => !EXCLUDE.includes(it)).filter(filterExpired)
: ONLY.filter(filterNonExisting);
debug('Game keys:', gameKeys);
const gp = new GamePromo();
const queue = new Queue();
for (let k = 0; k < gameKeys.length; k++) {
const gameKey = gameKeys[k];
const keys = keysTotal(gameKey);
for (let i = 0; i < keys; i++) {
queue.push(async () => {
const code = await getPromoCode(gp, gameKey);
console.info(code);
if (WITH_REINSTALL_TIME && !(k === gameKeys.length - 1 && i === keys - 1)) {
await globalDelay(
CLIENT_STRATEGY === 'keep'
? 300_000
: (Math.floor(Math.random() * 11) + 20) * 1_000,
);
}
});
}
}
}
main().catch(console.error);
@EugeneSnikhovskiy
Copy link

Hello! First of all great job!
I have two questions, maybe you can help me?

  1. Did you try to figure out how to receive the real clientID? As I understand, for now all keys are not from same user, each key belong to new unique user.
    From my research all keys are, somehow, marked and belong to custom user, for example
first day from same account in game
BIKE-1WX-XXX...
BIKE-1WX-XXX...
BIKE-1WX-XXX...
BIKE-1WX-XXX...

second day from same account in game
BIKE-3WX-XXX...
BIKE-3WX-XXX...
BIKE-3WX-XXX...
BIKE-3WX-XXX...

third day from same account in game
BIKE-5WX-XXX...
BIKE-5WX-XXX...
BIKE-5WX-XXX...
BIKE-5WX-XXX...

and same pattern for other days

I think that hamster developers can ban users, that use keys which not belongs to same clientId

  1. Did you try to hardcode clientId? Maybe it can solve pattern problem? All keys will be from non-real user, but from same user every time

@TheRealYT
Copy link

How did you manage to get the appToken? dnSpy or just an mitm proxy?

routed requests through my own VPN and inspected them afterwards.

Is it a normal VPN? could you mention it please?

@atversko
Copy link

atversko commented Aug 9, 2024

Hello! First of all great job! I have two questions, maybe you can help me?

  1. Did you try to figure out how to receive the real clientID? As I understand, for now all keys are not from same user, each key belong to new unique user.
    From my research all keys are, somehow, marked and belong to custom user, for example
first day from same account in game
BIKE-1WX-XXX...
BIKE-1WX-XXX...
BIKE-1WX-XXX...
BIKE-1WX-XXX...

second day from same account in game
BIKE-3WX-XXX...
BIKE-3WX-XXX...
BIKE-3WX-XXX...
BIKE-3WX-XXX...

third day from same account in game
BIKE-5WX-XXX...
BIKE-5WX-XXX...
BIKE-5WX-XXX...
BIKE-5WX-XXX...

and same pattern for other days

I think that hamster developers can ban users, that use keys which not belongs to same clientId

  1. Did you try to hardcode clientId? Maybe it can solve pattern problem? All keys will be from non-real user, but from same user every time

Each player can remove the games and install them again. Its fastest way to get keys. So different clientIds cant be proof of cheating.

@delasy
Copy link
Author

delasy commented Aug 9, 2024

Hello! First of all great job! I have two questions, maybe you can help me?

  1. Did you try to figure out how to receive the real clientID? As I understand, for now all keys are not from same user, each key belong to new unique user.
    From my research all keys are, somehow, marked and belong to custom user, for example
first day from same account in game
BIKE-1WX-XXX...
BIKE-1WX-XXX...
BIKE-1WX-XXX...
BIKE-1WX-XXX...

second day from same account in game
BIKE-3WX-XXX...
BIKE-3WX-XXX...
BIKE-3WX-XXX...
BIKE-3WX-XXX...

third day from same account in game
BIKE-5WX-XXX...
BIKE-5WX-XXX...
BIKE-5WX-XXX...
BIKE-5WX-XXX...

and same pattern for other days

I think that hamster developers can ban users, that use keys which not belongs to same clientId

  1. Did you try to hardcode clientId? Maybe it can solve pattern problem? All keys will be from non-real user, but from same user every time

First couple days I was actually playing BIKE game with sh1t ton of ads watched. The fastest way to get a new key was to re-install the game and complete first 5 levels (as @atversko already mentioned).

Did you try to figure out how to receive the real clientID?

The real client id is randomly generated, what changes is the format. Each game implements it's custom way of generating client id. This is unique id supplied by the game itself (not hamster). The chances that hamster team will do any checks on billion keys are relatively small (though it's still possible so be careful to not lose your account).

Ah although I remembered one thing, the reason I was re-installing game to get the keys was because BIKE game for some reason could get stuck after 6th level and you couldn't get any more keys so I was forced to re-install it.

Initially I was testing to have one client id per game, send events and receive keys without generating client id for each key. That didn't work though, same problem as with BIKE, sometimes /promo/register-event would always return hasCode: true even though you already collected the code.

I tried playing CLONE for first 30 mins and I didn't get any code at all. If hamster team think that someone will be playing this sh1t that they call games, they are super wrong about this. This situation with CLONE game is what actually made me to create this script.

@delasy
Copy link
Author

delasy commented Aug 9, 2024

How did you manage to get the appToken? dnSpy or just an mitm proxy?

routed requests through my own VPN and inspected them afterwards.

Is it a normal VPN? could you mention it please?

you can use any proxy that can route traffic and allows you to inspect requests afterwards.

though just so you know there's hundreds of requests made each minute to collect analytics about you and to show you "relevant" ads.
which makes it really hard to find the needed request that actually generates tokens.

all these games they integrate with 10+ ads services to eliminate the problem of ever running out of ads.
the bundle size of such games gives them out. 400mb in App Store for a game that should be maximum 80mb.
what's other 320mb you would ask? ads SDKs.

@mevinu-adheesha
Copy link

Im getting this error.

Error: 524 at fetchApi (C:\Users\hp\Downloads\Compressed\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\hamster-kombat-playground-games-promo-keys-generator.js:93:15) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async getPromoCode (C:\Users\hp\Downloads\Compressed\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\hamster-kombat-playground-games-promo-keys-generator.js:105:29) at async displayPromoCode (C:\Users\hp\Downloads\Compressed\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\hamster-kombat-playground-games-promo-keys-generator.js:147:22) at async main (C:\Users\hp\Downloads\Compressed\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\96c5340fc5e0617ddc1ff4ddb458d968-4bd383dd28f95aa4aa9b45a6460793285bb5d85f\hamster-kombat-playground-games-promo-keys-generator.js:158:13)

@delasy
Copy link
Author

delasy commented Aug 9, 2024

@wazerstar @mevinu-adheesha
the reason you get 5xx errors is because currently API server is choking from the load, I updated the script now to add a cooldown period.
The script will retry one more time after cooldown period passes.

@malalim1
Copy link

malalim1 commented Aug 9, 2024

Error: 400 Bad Request
    at fetchApi (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:100:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async getPromoCode (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:124:35)
    at async displayPromoCode (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:154:22)
    at async main (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:165:13)

What does this mean and how can I fix this? It says this after generating the 4th BIKE code under the "-- Game CLONE" thing

@ga1lan
Copy link

ga1lan commented Aug 10, 2024

Hopefully this is not being rude; I am asking how did you find the way the game generates keys?
Since I have been trying a lot and no luck till now.
Please do pm me if that's appropriate.
Thank you

@delasy
Copy link
Author

delasy commented Aug 10, 2024

Error: 400 Bad Request
    at fetchApi (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:100:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async getPromoCode (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:124:35)
    at async displayPromoCode (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:154:22)
    at async main (C:\Users\use\OneDrive\Desktop\code\hamster-kombat-playground-games-promo-keys-generator.js:165:13)

What does this mean and how can I fix this? It says this after generating the 4th BIKE code under the "-- Game CLONE" thing

I have no idea why this is happening on your machine

@delasy
Copy link
Author

delasy commented Aug 10, 2024

Hopefully this is not being rude; I am asking how did you find the way the game generates keys? Since I have been trying a lot and no luck till now. Please do pm me if that's appropriate. Thank you

I don't know how keys are being generated. The way I get keys is by simulating real requests as if a person was playing a game. Even if you find how keys are generated you will not be able to use them, as each key should be registered before it can be used. Take a look at code: https://gist.github.com/delasy/96c5340fc5e0617ddc1ff4ddb458d968#file-hamster-kombat-playground-games-promo-keys-generator-js-L108, it should be straightforward on how I managed to solve it.

On a high level it works like this (this is pseudocode):

login_client
...delay
event = register_event

while not event.hasCode
  ...delay
  event = register_event

code = collect_code

@delasy
Copy link
Author

delasy commented Aug 10, 2024

Hi everyone, if this gist helped you, consider helping me by giving a star to my main project:
github.com/thelang-io/the - a programming language I'm developing since 2018.
In advance, thanks a lot!

@WolfZone08
Copy link

dont working to night

@KashirHabib
Copy link

Error: Unable to get BIKE promo
at getPromoCode (REPL148:37:15)
at async displayPromoCode (REPL157:5:22)
at async main (REPL170:7:13)
Everything was fine for the past 4 days, but now I'm getting this error. Any idea why?
@delasy

@DiabloPNK
Copy link

Error: Unable to get BIKE promo at getPromoCode (REPL148:37:15) at async displayPromoCode (REPL157:5:22) at async main (REPL170:7:13) Everything was fine for the past 4 days, but now I'm getting this error. Any idea why? @delasy

same

@hammaddurrani11
Copy link

Error: Unable to get BIKE promo at getPromoCode (REPL148:37:15) at async displayPromoCode (REPL157:5:22) at async main (REPL170:7:13) Everything was fine for the past 4 days, but now I'm getting this error. Any idea why? @delasy

Same

@DiabloPNK
Copy link

They updated the games.

@delasy
Copy link
Author

delasy commented Aug 13, 2024

@wazerstar @WolfZone08 @KashirHabib @DiabloPNK @hammaddurrani11

it seems they changed timings a bit on backend servers, I'll be playing around later today with timings.
for now increased MAX_RETRIES and added SERVER_ERROR_RETRIES.
I updated this gist, you can check if you get the codes now.

@Fairell
Copy link

Fairell commented Aug 13, 2024

@delasy
Thank you, now the generator works

@DiabloPNK
Copy link

@delasy
ty

@Pacenio
Copy link

Pacenio commented Aug 13, 2024

Question: they made a warning about using generators to get the keys, but is it even possible to find fraud? Since this way we use here, is communication with the same keygenerator they use for the in game codes?

@DiabloPNK
Copy link

I think the only problem with the codes is the time interval between their requests. it would be safer If we get one code for each game first, then get the second code.

@delasy
Copy link
Author

delasy commented Aug 13, 2024

@Pacenio @DiabloPNK

what if you are a blogger with a team and everyone on the team completes games at the same time from physical devices and then these codes are getting sent out to blogger so he can enter them?
it would be nearly to impossible to find fraud accounts and eliminate false-positives.
have you tried playing all of these games? it takes roughly 2-6 hours with hell lot of ads to watch.
the only way to find fraud accounts in my opinion is if account collects all of the keys on daily basis

@DiabloPNK
Copy link

DiabloPNK commented Aug 14, 2024

I didn't think about blogger guy😅

@Fairell
Copy link

Fairell commented Aug 14, 2024

@delasy
Hi, I have configured the file to receive 4 keys for all games. Instead of 3 key for Train I got an error:
TypeError: fetch failed at node:internal/deps/undici/undici:13178:13 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async fetchApi (REPL2:87:17) at async getPromoCode (REPL2:125:35) at async displayPromoCode (REPL2:155:22) at async main (REPL2:166:13) { [cause]: ConnectTimeoutError: Connect Timeout Error (attempted addresses: 172.67.140.155:443, 2606:4700:3034::ac43:8c9b:443, 104.21.73.51:443, 2606:4700:3035::6815:4933:443) at onConnectTimeout (node:internal/deps/undici/undici:2331:28) at node:internal/deps/undici/undici:2283:50 at Immediate._onImmediate (node:internal/deps/undici/undici:2313:37) at process.processImmediate (node:internal/timers:483:21) at process.topLevelDomainCallback (node:domain:160:15) at process.callbackTrampoline (node:internal/async_hooks:128:24) { code: 'UND_ERR_CONNECT_TIMEOUT' } }

@delasy
Copy link
Author

delasy commented Aug 14, 2024

@delasy Hi, I have configured the file to receive 4 keys for all games. Instead of 3 key for Train I got an error: TypeError: fetch failed at node:internal/deps/undici/undici:13178:13 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async fetchApi (REPL2:87:17) at async getPromoCode (REPL2:125:35) at async displayPromoCode (REPL2:155:22) at async main (REPL2:166:13) { [cause]: ConnectTimeoutError: Connect Timeout Error (attempted addresses: 172.67.140.155:443, 2606:4700:3034::ac43:8c9b:443, 104.21.73.51:443, 2606:4700:3035::6815:4933:443) at onConnectTimeout (node:internal/deps/undici/undici:2331:28) at node:internal/deps/undici/undici:2283:50 at Immediate._onImmediate (node:internal/deps/undici/undici:2313:37) at process.processImmediate (node:internal/timers:483:21) at process.topLevelDomainCallback (node:domain:160:15) at process.callbackTrampoline (node:internal/async_hooks:128:24) { code: 'UND_ERR_CONNECT_TIMEOUT' } }

it seems to be router/internet problem, check this issue: nodejs/undici#1531

the solution might be increasing timeout, just add these two lines on top of the file:

const { Agent, setGlobalDispatcher } = require('undici');
setGlobalDispatcher(new Agent({ connect: { timeout: 60_000 } }));

I haven't tried it myself because everything works for me, let me know if this helps or you found another solution!

@ledunguit
Copy link

@wazerstar I think we might need to use a proxy tool like Charles to intercept the API requests sent from the app (client). Alternatively, we could reverse-engineer the app to retrieve the data. I'm not sure if this is the right approach, though.

@kelliark
Copy link

how to get the promoID and appToken? I'm still new, thankyou

@Microzibert
Copy link

Microzibert commented Aug 15, 2024

Merge away

A new game has been out

@Sahilshyy
Copy link

Can you update the code cz new game is already out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment