Minigame solver
Closed this issue · 122 comments
minigame was added to HamsterKombat, please add autosolver for if
Было бы неплохо, но думаю апи для мини игр нет:)
There are a base64 ciphered number passes for claim reward. One part is accountId, and other part i dont know what. Guess it somehow generateing at front-end, but i actualy cant understand where and how.
It is only one problem.
There are a base64 ciphered number passes for claim reward. One part is accountId, and other part i dont know what. Guess it somehow generateing at front-end, but i actualy cant understand where and how.
It is only one problem.
Он генерирует её где-то тут... /games/UnblockPuzzle/, но пока не понял каким образом. Так же попробовал изменить несколько цифр в числе и все равно получил ответ 200
There are a base64 ciphered number passes for claim reward. One part is accountId, and other part i dont know what. Guess it somehow generateing at front-end, but i actualy cant understand where and how.
It is only one problem.Он генерирует её где-то тут... /games/UnblockPuzzle/, но пока не понял каким образом. Так же попробовал изменить несколько цифр в числе и все равно получил ответ 200
Да я был там, залип на строчке eval(Zstd_DecStr()) со тсрокой зашифрованой. Оказалось там чисто код, который инициализирует движок игры. Даже через чатГпт пропустил - ничего интересного не нашел. Правда хз нафига шифровать это было...
Да я был там, залип на строчке eval(Zstd_DecStr()) со тсрокой зашифрованой. Оказалось там чисто код, который инициализирует движок игры. Даже через чатГпт пропустил - ничего интересного не нашел. Правда хз нафига шифровать это было...
А я застрял на функции
function OnGameFinished(e) {
parent.postMessage(JSON.stringify({
method: "OnGameFinished",
cipher: e
}))
}
Никак не пойму, откуда она вызывается. Но, боюсь, я слишком плох в js, нужно чтобы сюда посмотрел человек, который действительно разбирается
Складывается впечатление, что это число генерируется на основе низкоуровневых чисел связанных с количеством выделенной движку памяти (heap). ИЛи что то в этом роде.
Но в таком случае как сервак это проверяет? И проверяет ли?
и все равно получил ответ 200
стоп. Так оно ж работает. А зачем ты дальше паришся? Сейчас у меня кд откатился, я попробовал зарандомить значение между двумя значениями, которые записал при прохождении игры руками, и все ок. Резолвед.
let firstPart = await ut.getRandomInt(734792259, 883845570); //generate random value between two valid values
firstPart = "0" + firstPart //add zero, because it must be 10 digits string
let cipher = await btoa( firstPart + "|" + clickerUser.id) //to base64
Кароч авторы хомяка нас реально за хомяков держат, и на понт берут.
Я уже всерьез собирался "ии" писать, который будет решение находить и через селениум "честно" проходить игру)
Вот полный код если че
async function playMiniGame(){
if (isMiniGameClaimed) { //get it from config
ut.log("Key already claimed")
return
}
if (nextMiniGameSeconds > 0) { //get it from config
ut.log("Key timer not ready " + nextMiniGameSeconds)
return
}
await startMiniGame()
await ut.sleep(ut.getRandomInt(20000, 29000)) //simulate game played
await finishMiniGame()
}
async function startMiniGame(){
await ut.log("Start minigame")
let resp = await fetch("https://api.hamsterkombatgame.io/clicker/start-keys-minigame", {
"headers": {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9",
"authorization": "Bearer " + accessToken,
"priority": "u=1, i",
"sec-ch-ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Android WebView\";v=\"126\"",
"sec-ch-ua-mobile": "?1",
"sec-ch-ua-platform": "\"Android\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"x-requested-with": "org.telegram.messenger",
"Referer": "https://hamsterkombatgame.io/",
"Referrer-Policy": "strict-origin-when-cross-origin"
},
"body": null,
"method": "POST"
});
console.log(resp.status)
console.log(await resp.json())
}
async function finishMiniGame(){
await ut.log("Finish minigame")
// let cipher = await btoa(ut.getRandomInt(10) + "|" + clickerUser.id)
let firstPart = await ut.getRandomInt(734792259, 883845570);
firstPart = "0" + firstPart
let cipher = await btoa( firstPart + "|" + clickerUser.id)
console.log("Cipher = " + cipher)
let resp = await fetch("https://api.hamsterkombatgame.io/clicker/claim-daily-keys-minigame", {
"headers": {
"accept": "application/json",
"accept-language": "en-US,en;q=0.9",
"authorization": "Bearer " + accessToken,
"content-type": "application/json",
"priority": "u=1, i",
"sec-ch-ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Android WebView\";v=\"126\"",
"sec-ch-ua-mobile": "?1",
"sec-ch-ua-platform": "\"Android\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"x-requested-with": "org.telegram.messenger",
"Referer": "https://hamsterkombatgame.io/",
"Referrer-Policy": "strict-origin-when-cross-origin"
},
// "body": "{\"cipher\":\"" + cipher +"\"}",
"body": JSON.stringify({cipher: cipher}),
"method": "POST"
});
console.log(resp.status)
console.log(await resp.json())
}
Так оно ж работает. А зачем ты дальше паришся?
Парюсь, чтобы не посылать рандомные значения. Мы же не знаем, проверяет ли серверная сторона их или нет. Не хочется всю ферму в бан отправить из-за одного числа =)
Так оно ж работает. А зачем ты дальше паришся?
Парюсь, чтобы не посылать рандомные значения. Мы же не знаем, проверяет ли серверная сторона их или нет. Не хочется всю ферму в бан отправить из-за одного числа =)
они бы сразу их не принимали. Вряд ли они их куда то записывают, чтобы потом сверить.
Ну как знаешь)
Так оно ж работает. А зачем ты дальше паришся?
Парюсь, чтобы не посылать рандомные значения. Мы же не знаем, проверяет ли серверная сторона их или нет. Не хочется всю ферму в бан отправить из-за одного числа =)
они бы сразу их не принимали. Вряд ли они их куда то записывают, чтобы потом сверить. Ну как знаешь)
Так не просто же так Valve делают "волновые" баны пользователей.
Даёшь возможность бото-пользователям расшириться и потом массово их банишь.
Ибо если банить сразу, то крайне мало людей попадутся.
К слову. Я немного копнул.
Поэтапно.
Мини-игра запускается [POST] + body: null /clicker/start-keys-minigame
Успешное окончание игры [POST] + body: JSON.stringify({ cipher: btoa(${cipher}|${account_id}
) }) /clicker/claim-daily-keys-minigame
btoa(value) = base64encode(value)
Что за cipher пока неизвестно.
Знаю, что по началу игры подгружается страничка: https://hamsterkombatgame.io/games/UnblockPuzzle/?v
На ней есть такой кусочек кода:
function OnGameFinished(e) {
parent.postMessage(JSON.stringify({
method: "OnGameFinished",
cipher: e
}))
}
Но я не нашёл что вызывает эту функцию, чтобы найти откуда берётся аргумент для неё.
Тоже не могу найти где идёт вызов...
Такое ощущение что они используют WASM для какой-то части игры, тем более в том же самом эндпоинте
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
в самом конце какие-то зашифрованные файлы массивом лежат в переменной ARCHIVEJS_DATA
, попробую поискать где они расшифровываются
Да и консоль твердит об их расшифровке
archive/archive_files.json: decode 3 ms, onload() 1 ms
UnblockPuzzle/:11 archive/game0.projectc: decode 9 ms, onload() 0 ms
UnblockPuzzle/:11 archive/game0.arci: decode 1 ms, onload() 0 ms
UnblockPuzzle/:11 DefoldGames_wasm.js: decode 21 ms, onload() 10 ms
UnblockPuzzle/:11 archive/game0.arcd: decode 2000 ms, onload() 1 ms
UnblockPuzzle/:11 archive/game0.dmanifest: decode 990 ms, onload() 0 ms
UnblockPuzzle/:11 archive/game0.public.der: decode 1000 ms, onload() 1 ms
UnblockPuzzle/:11 DefoldGames.wasm: decode 14567 ms, onload() 1 ms
Да просто выложите запрос https://api.hamsterkombatgame.io/clicker/claim-daily-keys-minigame
с валидными данными
{"cipher": "тут_base64"}
Да и консоль твердит об их расшифровке
archive/archive_files.json: decode 3 ms, onload() 1 ms UnblockPuzzle/:11 archive/game0.projectc: decode 9 ms, onload() 0 ms UnblockPuzzle/:11 archive/game0.arci: decode 1 ms, onload() 0 ms UnblockPuzzle/:11 DefoldGames_wasm.js: decode 21 ms, onload() 10 ms UnblockPuzzle/:11 archive/game0.arcd: decode 2000 ms, onload() 1 ms UnblockPuzzle/:11 archive/game0.dmanifest: decode 990 ms, onload() 0 ms UnblockPuzzle/:11 archive/game0.public.der: decode 1000 ms, onload() 1 ms UnblockPuzzle/:11 DefoldGames.wasm: decode 14567 ms, onload() 1 ms
там ничего не шифровано, просто сжато fzstd
That's how it works. And why are you bothering further?
I worry so as not to send random values. We don't know if the server side checks them or not. I don't want to ban the whole farm because of one number =)
They wouldn't accept them right away. It is unlikely that they write them down somewhere in order to check them later. Well, as you know)
So it's not for nothing that Valve makes "wave" bans of users. You give bot users the opportunity to expand and then ban them en masse. Because if you ban immediately, then very few people will get caught.
By the way. I did a little digging.
Gradually. Mini-game starts [POST] + body: null Successful end of the game [POST] + body: JSON.stringify({ cipher: btoa(
/clicker/start-keys-minigame``${cipher}|${account_id}
) })/clicker/claim-daily-keys-minigame
btoa(value) = base64encode(value)
What kind of cipher is still unknown. I know that at the beginning of the game, the page loads: It has this piece of code:
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
function OnGameFinished(e) { parent.postMessage(JSON.stringify({ method: "OnGameFinished", cipher: e })) }But I haven't found what calls this function to find where the argument for it comes from.
called on this js: https://hamsterkombatgame.io/_nuxt/default.CXYCtMmd.js
Search/Find this function: function r(c)
function r(c) {
try {
const s = JSON.parse(c.data);
if (s.method)
switch (s.method) {
case "OnGameFinished":
t.cipher = window.btoa(s.cipher + "|" + Ke().account_id),
t.resetMatchMiniGameInterval(),
i();
break;
case "OnSceneLoaded":
t.sceneLoaded = !0;
break
}
} catch {}
}
you all need to write a lot of Tampermonkey scripts to log what c is. especially c.data
Hopefully, someone figures it out
Так не просто же так Valve делают "волновые" баны пользователей.
Даёшь возможность бото-пользователям расшириться и потом массово их банишь.
Ибо если банить сразу, то крайне мало людей попадутся.
в таком случае, эти данные должны как то передавать между сервером и клиентом, чтобы они могли на сервере сравнить правильность шифра, разве нет? Я искал, но не нашел ничего похожего. Правда только по XHR запросам прошелся.
вызванный на этом js: https://hamsterkombatgame.io/_nuxt/default.CXYCtMmd.js
Очень похоже, что по вашей ссылке находится приёмник этого сообщения
Да просто выложите запрос
У меня таки получилось решить сегодняшнее (после нескольких затупов):
Вышел такой шифр: MDYzOTU0NjU4MXwxNDU0NjM2MTA2
После расшифровки base64: 0639546581|1454636106
1454636106 - действительно мой id, но самое интересное, что 639546581 не особо подходит под getRandomInt(734792259, 883845570)
Да просто выложите запрос
У меня таки получилось решить сегодняшнее (после нескольких затупов): Вышел такой шифр: MDYzOTU0NjU4MXwxNDU0NjM2MTA2
После расшифровки base64: 0639546581|1454636106
1454636106 - действительно мой id, но самое интересное, что 639546581 не особо подходит под getRandomInt(734792259, 883845570)
Да, там более широкий диапазон. У меня выходило число начинающееся с единицы когда руками делал, но то значение не сохранил. А для рандома взял два значения которые у меня были записаны. Это не значит что это весь предел возможных значений
============
Я в своих раскопках по стактрейсу дошел до этой строки. Файл называется VM120 - я так понимаю это какой динамически создаваемый файл с кодом
С этой строки вызывается файл VM153 с только одной строкой, где уже есть готовая первая часть шифра
OnGameFinished('0976422942')
это уже вызов метода из UnblockPuzzle.
Хотя я и не понимаю как он может вызываться с полностью пустого файла без импортов.
Если идти глубже VM120 - то там начинается вообще какая то дичь, не похожая на джаваскрипт.
Да просто выложите запрос
У меня таки получилось решить сегодняшнее (после нескольких затупов): Вышел такой шифр: MDYzOTU0NjU4MXwxNDU0NjM2MTA2
После расшифровки base64: 0639546581|1454636106
1454636106 - действительно мой id, но самое интересное, что 639546581 не особо подходит под getRandomInt(734792259, 883845570)Да, там более широкий диапазон. У меня выходило число начинающееся с единицы когда руками делал, но то значение не сохранил. А для рандома взял два значения которые у меня были записаны. Это не значит что это весь предел возможных значений
============
Я в своих раскопках по стактрейсу дошел до этой строки. Файл называется VM120 - я так понимаю это какой динамически создаваемый файл с кодом
С этой строки вызывается файл VM153 с только одной строкой, где уже есть готовая первая часть шифра
OnGameFinished('0976422942')
это уже вызов метода из UnblockPuzzle. Хотя я и не понимаю как он может вызываться с полностью пустого файла без импортов.Если идти глубже VM120 - то там начинается вообще какая то дичь, не похожая на джаваскрипт.
OnGameFinished('0976422942') передастся из webasm (sdk defold) в виде utf8 строки и eval просто ее выполняет. собственно на константу 235305 есть ссылка внутри wasm. 0976422942 - очевидно ид игры. Но почему-то важно, что бы он начинался с 0...
/config теперь содержит levelConfig. например - a - - c -.- a b b c -.0 0 e - c -.f - e d d v.f g g x - v.f h h x z z
Если перевести его в таблицу - это будет начальное положение фигур.
- a - - c -
- a b b c -
0 0 e - c -
f - e d d v
f g g x - v
f h h x z z
Да просто выложите запрос
У меня таки получилось решить сегодняшнее (после нескольких затупов): Вышел такой шифр: MDYzOTU0NjU4MXwxNDU0NjM2MTA2
После расшифровки base64: 0639546581|1454636106
1454636106 - действительно мой id, но самое интересное, что 639546581 не особо подходит под getRandomInt(734792259, 883845570)Да, там более широкий диапазон. У меня выходило число начинающееся с единицы когда руками делал, но то значение не сохранил. А для рандома взял два значения которые у меня были записаны. Это не значит что это весь предел возможных значений
============
Я в своих раскопках по стактрейсу дошел до этой строки. Файл называется VM120 - я так понимаю это какой динамически создаваемый файл с кодом
С этой строки вызывается файл VM153 с только одной строкой, где уже есть готовая первая часть шифра
OnGameFinished('0976422942')
это уже вызов метода из UnblockPuzzle. Хотя я и не понимаю как он может вызываться с полностью пустого файла без импортов.Если идти глубже VM120 - то там начинается вообще какая то дичь, не похожая на джаваскрипт.
Тоже до туда дошел и дальше не продвинулся
Я предполагаю, что это число генерируется на основе расположения свечей
Cmmon!! Do lots of scripts local overrides on the devtools with lots of console.log
Но почему-то важно, что бы он начинался с 0...
Разве все числа начинаются с 0?
Но почему-то важно, что бы он начинался с 0
нет, бывают и с единицы
config теперь содержит levelConfig. например - a - - c -.- a b b c -.0 0 e - c -.f - e d d v.f g g x - v.f h h x z z
Если перевести его в таблицу - это будет начальное положение фигур.
Да, на это я первым делом обратил внимание, даже рисовал карты фигур на момент прохождения игры, думал может они как то в цифры трансформируются. но нет. Дрочился несколько часов с этим, чатГпт тоже дрючил.
Я предполагаю, что это число генерируется на основе расположения свечей
Тогда бы оно было бы у всех одинаковое. Если туда не примешивается что-то ещё
I assume that this number is generated based on the location of the candles
Then it would be the same for everyone. If something else is not mixed in there
Different ways to solve it and get the key through. Hence, different candle positions
Not sure but i feel like the first part has something to do with remaining time before losing the game
Nice idea, but as i see, hamster developers not so smart) //upd: or so? Need to check,
Different ways to solve it and get the key through. Hence, different candle positions
there are not many variations of it. So it must repeat sometimes. If we gather here our first parts of cipher, we can check this. On yesterday game i checked 4 ciphers, it not repeated at all,
We found the logic behind and tested it on two accounts, it worked without any issues, feel free to look at it and test if it works for you too. Commit Link
We found the logic behind and tested it on two accounts, it worked without any issues, feel free to look at it and test if it works for you too. Commit Link
cipher = (
("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10]
+ "|"
+ str(AccountID)
)
Но это же рандом...
We found the logic behind and tested it on two accounts, it worked without any issues, feel free to look at it and test if it works for you too. Commit Link
cipher = ( ("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10] + "|" + str(AccountID) )
But this is random...
First digits are not, they're based on waitTime, rest are random yes, but it works.
We found the logic behind and tested it on two accounts, it worked without any issues, feel free to look at it and test if it works for you too. Commit Link
This is a bad idea to use random numbers.
This might cause a ban of all bot users. Yes, currently there are no bans.
But they absolutely can collect data and then make a mass ban or prohibit withdrawal.
So we are looking for numbers in first part of cipher.
I found that this number is known as game starts:
{"method":"StartGame","level":"- b - c d d.a b - c…e -.h - f z z v.h x x - - v","number":1721505600}
caller function:
function onSandboxMessage(e) {
let a = "string" == typeof e.data ? JSON.parse(e.data) : e.data;
a && "StartGame" === a.method && JsToDef.send("__StartGame", a)
}
So it should be used in request.
The goal is to find where this number comes from.
We found the logic behind and tested it on two accounts, it worked without any issues, feel free to look at it and test if it works for you too. Commit Link
cipher = ( ("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10] + "|" + str(AccountID) )
But this is random...
First digits are not, they're based on waitTime, rest are random yes, but it works.
random works by now. Even without time. Look my code above. But people fear, that it can be banned in future, if cipher will be not accurate. ACtualy this is the only problem.
We found the logic behind and tested it on two accounts, it worked without any issues, feel free to look at it and test if it works for you too. Commit Link
cipher = ( ("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10] + "|" + str(AccountID) )
But this is random...
First digits are not, they're based on waitTime, rest are random yes, but it works.
random works by now. Even without time. Look my code above. But people fear, that it can be banned in future, if cipher will be not accurate. ACtualy this is the only problem.
We tried random before, it didn't work actually ~ this new method which is generating the first digits based on remaining time is working much better. Having pure randomness didn't work (your code)
We tried random before, it didn't work actually ~ this new method which is generating the first digits based on remaining time is working much better. Having pure randomness didn't work (your code)
hmm. I tried it right now on a new game. It worked for me again.
If you got multiple accounts, try to play normally and look at the first parameter of cipher code (before |AccountID
), the longer your game takes, the lower the number is.
If you got multiple accounts, try to play normally and look at the first parameter of cipher code (before
|AccountID
), the longer your game takes, the lower the number is.
If I am not wrong, then cipher is known at the game beginning.
Причем startDate статична и сегодня она "2024-07-20T20:00:00.000Z" - 1721505600000
1721505600000 / 1e3 = 1721505600
Завтра получается будет "2024-07-21T20:00:00.000Z" - 1721592000000
1721592000000 / 1e3 = 1721592000
If you got multiple accounts, try to play normally and look at the first parameter of cipher code (before
|AccountID
), the longer your game takes, the lower the number is.
cant approve it. Checking it just now, with video recording.
18sec left = 0571102420
21sec left = 0006424190
but yesterday numbers (from my code) 0734792259 and 0883845570 all was finished when less then 10 seconds was left. And both are bigger then today numbers.
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
Это можно прописать в консоль, чтобы локально бесплатно собрать пазл:
window.postMessage({"method":"StartGame","level":"- b - c d d.a b - c e -.a b 0 0 e -.g g f - e -.h - f z z v.h x x - - v","number":1721505600});
А это, чтобы отдебажить сообщения:
window.addEventListener("message", (event) => {
if(event?.data?.hello) return;
console.log("Message received:", event);
console.log("Data:", event.data);
console.log("Origin:", event.origin);
console.log("Source:", event.source);
}, false);
А как ты вызвал OnGameFinished?
Завершил пазл.
Для простоты пустой пазл с ключом: - - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -
cant approve it. Checking it just now, with video recording. 18sec left = 0571102420 21sec left = 0006424190
but yesterday numbers (from my code) 0734792259 and 0883845570 all was finished when less then 10 seconds was left. And both are bigger then today numbers.
Yes because today's time is 35 seconds.
Ну судя по тому, что мне всё время разные числа в cipher
попадаются (с одинаковым number
на входе), ощущение, будто они действительно случайные в каком-то диапазоне...
Завершил пазл.
Поиграл несколько раз, получил занимательные значения:
0612291910
0663935312
0763739863
0828511880
0218262748
0107944192
0641902159
0103925728
Генерятся рандомные числа. Никак это не проверяется. Нас снова всех надули - расходимся)
Завершил пазл.
Поиграл несколько раз, получил занимательные значения:
0612291910 0663935312 0763739863 0828511880 0218262748 0107944192 0641902159 0103925728
Генерятся рандомные числа. Никак это не проверяется. Нас снова всех надули - расходимся)
Разве что я бы перебдел и набрал массив этих чисел на случай, если они рандомные из списка.
Завершил пазл.
Поиграл несколько раз, получил занимательные значения:
0612291910 0663935312 0763739863 0828511880 0218262748 0107944192 0641902159 0103925728
Генерятся рандомные числа. Никак это не проверяется. Нас снова всех надули - расходимся)
Согласен. Я думаю нас на понт берут и расчитывают что мы будем бояться рандом юзать.
Разве что я бы перебдел и набрал массив этих чисел на случай, если они рандомные из списка.
А если они рандомные, а у тебя будут постоянно одни и те же числа? В ногу себе выстрелишь.
===========
Разрабы хомяка в принципе не отличаются каким то интересом к безопаности, в отличии от тапсвапа например.
У меня initData работают еще те, которые я больше месяца назад закинул, тогда как например в Musk Empire они меняются каждый час.
Так же свободно передают дельту апгрейдов, что максимально упрощает высчитываение самых выгодных апгрейдов.
одним словом - распиздяи (прям как я)).
почему вы думаете что это рандом, а не хэш-функция с солью в виде tg_id? не просто же так его добавляют к строке
почему вы думаете что это рандом, а не хэш-функция с солью в виде tg_id? не просто же так его добавляют к строке
я думаю, потому что рандом работает) но я открыт к аргументам. Потому я все еще мониторю это обсуждение и пока не запускаю скрипт на все аккаунты.
Это может быть хэш функция текущего времени, чтобы потом проверить дельту между "собрана в мини игре" и "зарегистрировано что собрано", но блин слишком сильный разброс значений
Это может быть хэш функция текущего времени, чтобы потом проверить дельту между "собрана в мини игре" и "зарегистрировано что собрано", но блин слишком сильный разброс значений
но тогда же придется учитывать пинг до сервера. Не будут же банить людей за то, что у них инет тормозной.
почему вы думаете что это рандом, а не хэш-функция с солью в виде tg_id? не просто же так его добавляют к строке
я думаю, потому что рандом работает) но я открыт к аргументам. Потому я все еще мониторю это обсуждение и пока не запускаю скрипт на все аккаунты.
ну такой себе аргумент, выше уже писали что провалидировать могут позже (перед эирдропом), чтобы самых ленивых отсеять. К тому же частичная валидация есть, как опять же выше писали, чтобы совсем уж чушь не присылали
почему вы думаете что это рандом, а не хэш-функция с солью в виде tg_id? не просто же так его добавляют к строке
я думаю, потому что рандом работает) но я открыт к аргументам. Потому я все еще мониторю это обсуждение и пока не запускаю скрипт на все аккаунты.
ну такой себе аргумент, выше уже писали что провалидировать могут позже (перед эирдропом), чтобы самых ленивых отсеять. К тому же частичная валидация есть, как опять же выше писали, чтобы совсем уж чушь не присылали
да, но тогда это что то должно быть и на сервере и на клиенте. То есть либо оно должно передаваться туда или сюда, либо должно быть там изначально - то есть время. Время старадает от пинга, и по моему опыту не подходит. Как вариант надо досконально все запросы прошерстить и убедиться, что там ничего непонятного больше не передается, что могло бы быть данными для валидирования шифра.
да, но тогда это что то должно быть и на сервере и на клиенте. То есть либо оно должно передаваться туда или сюда, либо должно быть там изначально - то есть время. Время старадает от пинга, и по моему опыту не подходит. Как вариант надо досконально все запросы прошерстить и убедиться, что там ничего непонятного больше не передается, что могло бы быть данными для валидирования шифра.
tg_id есть на клиенте и передается в на сервер вместе с шифром. Со временем всё сложно, перевёл часы на телефоне и всё сломал
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
Завершил игру 40 раз и абсолютно не вижу зависимости между временем и шифром:
const ciphers = [];
let lastAttempt = performance.now();
window.addEventListener(
"message",
(event) => {
if (event.data.hello) return;
if (
typeof event.data === "string" &&
event.data.includes("OnGameFinished")
) {
const data = JSON.parse(event.data);
const timeBetweenAttempts = `${Math.floor(performance.now() - lastAttempt) / 1000} seconds`;
lastAttempt = performance.now();
const { cipher } = data;
ciphers.push(cipher);
console.log({ cipher, timeBetweenAttempts });
window.postMessage({
method: "StartGame",
level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -",
number: Math.floor(new Date().getTime() / 1e3),
});
}
},
false
);
window.postMessage({
method: "StartGame",
level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -",
number: Math.floor(new Date().getTime() / 1e3),
});
И всё же я начал подставлять числа в хомяка и ему не нравятся большие числа.
Так что даже те числа, что генерируются кодом из предыдущего сообщения не очень то и подходят.
Наблюдение...
В самом кликере в мини-игре у них на бэкенде сначала стоит валидация шифра, а уже потом время отката.
Ошибка при неверном шифре:
Ошибка при верном шифре, но есть откат:
Пора спать🌛 4 часа утра...
И всё же я начал подставлять числа в хомяка и ему не нравятся большие числа.
Так что даже те числа, что генерируются кодом из предыдущего сообщения не очень то и подходят.
Наблюдение... В самом кликере в мини-игре у них на бэкенде сначала стоит валидация шифра, а уже потом время отката. Ошибка при неверном шифре: Ошибка при верном шифре, но есть откат:
Пора спать🌛 4 часа утра...
Не стоило так страдать.
- Ещё в начале ветки я писал, что 0 важен
- Значит код не полный рандом, иначе его не нужно было сверять
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
Завершил игру 40 раз и абсолютно не вижу зависимости между временем и шифром:
const ciphers = []; let lastAttempt = performance.now(); window.addEventListener( "message", (event) => { if (event.data.hello) return; if ( typeof event.data === "string" && event.data.includes("OnGameFinished") ) { const data = JSON.parse(event.data); const timeBetweenAttempts = `${Math.floor(performance.now() - lastAttempt) / 1000} seconds`; lastAttempt = performance.now(); const { cipher } = data; ciphers.push(cipher); console.log({ cipher, timeBetweenAttempts }); window.postMessage({ method: "StartGame", level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -", number: Math.floor(new Date().getTime() / 1e3), }); } }, false ); window.postMessage({ method: "StartGame", level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -", number: Math.floor(new Date().getTime() / 1e3), });
Результат зависит от ${number}. Я взял тот number
, который получился в самом кликере и подставил его вместо Math.floor(new Date().getTime() / 1e3)
и числа начали получаться нормальными.
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
Завершил игру 40 раз и абсолютно не вижу зависимости между временем и шифром:const ciphers = []; let lastAttempt = performance.now(); window.addEventListener( "message", (event) => { if (event.data.hello) return; if ( typeof event.data === "string" && event.data.includes("OnGameFinished") ) { const data = JSON.parse(event.data); const timeBetweenAttempts = `${Math.floor(performance.now() - lastAttempt) / 1000} seconds`; lastAttempt = performance.now(); const { cipher } = data; ciphers.push(cipher); console.log({ cipher, timeBetweenAttempts }); window.postMessage({ method: "StartGame", level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -", number: Math.floor(new Date().getTime() / 1e3), }); } }, false ); window.postMessage({ method: "StartGame", level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -", number: Math.floor(new Date().getTime() / 1e3), });Результат зависит от ${number}. Я взял тот
number
, который получился в самом кликере и подставил его вместоMath.floor(new Date().getTime() / 1e3)
и числа начали получаться нормальными.
там нет "нормальных" чисел. генерируй рандомный number и ты увидишь любой cipher еще и разной длины. number вообще может быть числом в виде строки
там нет "нормальных" чисел. генерируй рандомный number и ты увидишь любой cipher еще и разной длины. number вообще может быть числом в виде строки
К нормальным числам я приписал те, которые принимает бэкенд хомяка. В number
нельзя пихать случайные числа, иначе cipher
выйдет из диапазона допустимых бэкендом хомяка и он их просто не будет принимать.
There are no "normal" numbers. Generate a random number and you will see any Cipher of different lengths. number can generally be a number in the form of a string
To the normal numbers, I added those that the hamster backend accepts. You can't shove random numbers in, otherwise the hamster will go out of the range allowed by the backend and he will simply not accept them.
number``cipher
True
К нормальным числам я приписал те, которые принимает бэкенд хомяка. В
number
нельзя пихать случайные числа, иначеcipher
выйдет из диапазона допустимых бэкендом хомяка и он их просто не будет принимать.
хомяк проверяет только первую цифру. число не проверяется вообще. можешь хоть 0ABCDEFGHI отправить. Важнее, что такое - 0, если это контрольная цифра каких-то манипуляций с number - то это проблема, придётся создавать базу валидных cipher. тк проверить могут потом
Let's revisit this:
startGame() {
var e, t;
if (this.dailyKeysMiniGame != null) {
const r = document.getElementById(this.$id).contentWindow
, i = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
, o = (t = this.dailyKeysMiniGame) == null ? void 0 : t.levelConfig;
r.postMessage(JSON.stringify({
method: "StartGame",
level: o,
number: i
})),
this.startMatchMiniGameInterval(),
this.startReplayMiniGameInterval(),
this.isPlaying = !0,
this.showTutor = !1
}
}
}
i variable, which is number in this case:
i = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
number = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
e.startDate for example:
number = new Date("2024-07-20T20:00:00.000Z").getTime() / 1e3;
// Output: 1721505600
but:
number = Math.floor(new Date().getTime() / 1e3);
// Example output: 1721599200
Let's revisit this:
startGame() { var e, t; if (this.dailyKeysMiniGame != null) { const r = document.getElementById(this.$id).contentWindow , i = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3 , o = (t = this.dailyKeysMiniGame) == null ? void 0 : t.levelConfig; r.postMessage(JSON.stringify({ method: "StartGame", level: o, number: i })), this.startMatchMiniGameInterval(), this.startReplayMiniGameInterval(), this.isPlaying = !0, this.showTutor = !1 } } }
i variable, which is number in this case: i = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
number = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
e.startDate for example:
number = new Date("2024-07-20T20:00:00.000Z").getTime() / 1e3;
// Output: 1721505600but:
number = Math.floor(new Date().getTime() / 1e3);
// Example output: 1721599200
we know this... the only question is whether it is worth the risk and generating on your own or build a database
Let's revisit this:
startGame() { var e, t; if (this.dailyKeysMiniGame != null) { const r = document.getElementById(this.$id).contentWindow , i = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3 , o = (t = this.dailyKeysMiniGame) == null ? void 0 : t.levelConfig; r.postMessage(JSON.stringify({ method: "StartGame", level: o, number: i })), this.startMatchMiniGameInterval(), this.startReplayMiniGameInterval(), this.isPlaying = !0, this.showTutor = !1 } } }
i variable, which is number in this case: i = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
number = new Date((e = this.dailyKeysMiniGame) == null ? void 0 : e.startDate).getTime() / 1e3
e.startDate for example:number = new Date("2024-07-20T20:00:00.000Z").getTime() / 1e3;
// Output: 1721505600
but:
number = Math.floor(new Date().getTime() / 1e3);
// Example output: 1721599200we know this... the only question is whether it is worth the risk and generating on your own or build a database
No, you don't
https://hamsterkombatgame.io/games/UnblockPuzzle/?v
I finished the game 40 times and I absolutely do not see the relationship between time and cipher:const ciphers = []; let lastAttempt = performance.now(); window.addEventListener( "message", (event) => { if (event.data.hello) return; if ( typeof event.data === "string" && event.data.includes("OnGameFinished") ) { const data = JSON.parse(event.data); const timeBetweenAttempts = `${Math.floor(performance.now() - lastAttempt) / 1000} seconds`; lastAttempt = performance.now(); const { cipher } = data; ciphers.push(cipher); console.log({ cipher, timeBetweenAttempts }); window.postMessage({ method: "StartGame", level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -", number: Math.floor(new Date().getTime() / 1e3), }); } }, false ); window.postMessage({ method: "StartGame", level: "- - - - - -.0 0 - - - -.- - - - - -.- - - - - -.- - - - - -", number: Math.floor(new Date().getTime() / 1e3), });The result depends on ${number}. I took the one that turned out in the clicker itself and substituted it for and the numbers began to turn out normal.
number``Math.floor(new Date().getTime() / 1e3)
I AM RESPONDING TO THIS MESSAGE
When he uses: number: Math.floor(new Date().getTime() / 1e3) , he will get another value because his time and date is not exactly "2024-07-20T20:00:00.000Z" which is what the game explicitly sets.
See this:
import time
from datetime import datetime
#JAVASCRIPT
explicit_time = new Date("2024-07-20T20:00:00.000Z").getTime() / 1e3;
implicit_time = Math.floor(new Date().getTime() / 1e3);
console.log(explicit_time)
console.log(implicit_time)
#PYTHON
explicit_date = "2024-07-20T20:00:00.000Z"
explicit_time = int(datetime.fromisoformat(explicit_date.replace("Z", "+00:00")).timestamp())
implicit_time = int(time.time())
print("Explicit Time:", explicit_time)
print("Implicit Time:", implicit_time)
// Explicit Time: 1721505600
// Implicit Time: 1721567279
The time "2024-07-20T20:00:00.000Z" comes from the json response: https://api.hamsterkombatgame.io/clicker/start-keys-minigame
},
"dailyKeysMiniGame": {
"startDate": "2024-07-20T20:00:00.000Z",
"levelConfig": "- b - c d d.a b - c e -.a b 0 0 e -.g g f - e -.h - f z z v.h x x - - v",
"youtubeUrl": "https://youtube.com/shorts/hlFGgSAN5bo",
"bonusKeys": 1,
"isClaimed": false,
"totalSecondsToNextAttempt": 5435,
"remainSecondsToGuess": 34.999,
"remainSeconds": 25322.331,
"remainSecondsToNextAttempt": 5434.999
}
}
You can get the daily mini game reward on a different bot with this code.
`def StartMiniGame(self, AccountConfigData, AccountID):
if "dailyKeysMiniGame" not in AccountConfigData:
log.error(f"[{self.account_name}] Unable to get daily keys mini game.")
self.SendTelegramLog(
f"[{self.account_name}] Unable to get daily keys mini game.",
"other_errors",
)
return
if AccountConfigData["dailyKeysMiniGame"]["isClaimed"] == True:
log.info(f"[{self.account_name}] Daily keys mini game already claimed.")
return
if AccountConfigData["dailyKeysMiniGame"]["remainSecondsToNextAttempt"] > 0:
log.info(f"[{self.account_name}] Daily keys mini game is on cooldown...")
return
## check timer.
url = "https://api.hamsterkombatgame.io/clicker/start-keys-minigame"
headers = {
"Access-Control-Request-Headers": "authorization",
"Access-Control-Request-Method": "POST",
}
# Send OPTIONS request
self.HttpRequest(url, headers, "OPTIONS", 204)
headers = {
"Authorization": self.Authorization,
}
# Send POST request
response = self.HttpRequest(url, headers, "POST", 200)
if response is None:
log.error(f"[{self.account_name}] Unable to start mini game.")
self.SendTelegramLog(
f"[{self.account_name}] Unable to start mini game.", "other_errors"
)
return
if "dailyKeysMiniGame" not in response:
log.error(f"[{self.account_name}] Unable to get daily keys mini game.")
self.SendTelegramLog(
f"[{self.account_name}] Unable to get daily keys mini game.",
"other_errors",
)
return
if response["dailyKeysMiniGame"]["isClaimed"] == True:
log.info(f"[{self.account_name}] Daily keys mini game already claimed.")
return
if "remainSecondsToGuess" not in response["dailyKeysMiniGame"]:
log.error(f"[{self.account_name}] Unable to get daily keys mini game.")
self.SendTelegramLog(
f"[{self.account_name}] Unable to get daily keys mini game.",
"other_errors",
)
return
waitTime = int(
response["dailyKeysMiniGame"]["remainSecondsToGuess"]
- random.randint(8, 15)
)
if waitTime < 0:
log.error(f"[{self.account_name}] Unable to claim mini game.")
self.SendTelegramLog(
f"[{self.account_name}] Unable to claim mini game.", "other_errors"
)
return
log.info(
f"[{self.account_name}] Waiting for {waitTime} seconds, Mini-game will be completed in {waitTime} seconds..."
)
time.sleep(waitTime)
url = "https://api.hamsterkombatgame.io/clicker/claim-daily-keys-minigame"
headers = {
"Access-Control-Request-Headers": "authorization,content-type",
"Access-Control-Request-Method": "POST",
}
# Send OPTIONS request
self.HttpRequest(url, headers, "OPTIONS", 204)
headers = {
"Accept": "application/json",
"Authorization": self.Authorization,
"Content-Type": "application/json",
}
cipher = (
("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10]
+ "|"
+ str(AccountID)
)
cipher_base64 = base64.b64encode(cipher.encode()).decode()
payload = json.dumps(
{
"cipher": cipher_base64,
}
)
# Send POST request
response = self.HttpRequest(url, headers, "POST", 200, payload)
if response is None:
log.error(f"[{self.account_name}] Unable to claim mini game.")
self.SendTelegramLog(
f"[{self.account_name}] Unable to claim mini game.", "other_errors"
)
return
log.info(f"[{self.account_name}] Mini game claimed successfully.")
`
You can get the daily mini game reward on a different bot with this code.
`def StartMiniGame(self, AccountConfigData, AccountID): if "dailyKeysMiniGame" not in AccountConfigData: log.error(f"[{self.account_name}] Unable to get daily keys mini game.") self. SendTelegramLog( f"[{self.account_name}] Unable to get daily keys mini game.", "other_errors", ) return
if AccountConfigData["dailyKeysMiniGame"]["isClaimed"] == True: log.info(f"[{self.account_name}] Daily keys mini game already claimed.") return if AccountConfigData["dailyKeysMiniGame"]["remainSecondsToNextAttempt"] > 0: log.info(f"[{self.account_name}] Daily keys mini game is on cooldown...") return ## check timer. url = "https://api.hamsterkombatgame.io/clicker/start-keys-minigame" headers = { "Access-Control-Request-Headers": "authorization", "Access-Control-Request-Method": "POST", } # Send OPTIONS request self.HttpRequest(url, headers, "OPTIONS", 204) headers = { "Authorization": self.Authorization, } # Send POST request response = self.HttpRequest(url, headers, "POST", 200) if response is None: log.error(f"[{self.account_name}] Unable to start mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to start mini game.", "other_errors" ) return if "dailyKeysMiniGame" not in response: log.error(f"[{self.account_name}] Unable to get daily keys mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to get daily keys mini game.", "other_errors", ) return if response["dailyKeysMiniGame"]["isClaimed"] == True: log.info(f"[{self.account_name}] Daily keys mini game already claimed.") return if "remainSecondsToGuess" not in response["dailyKeysMiniGame"]: log.error(f"[{self.account_name}] Unable to get daily keys mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to get daily keys mini game.", "other_errors", ) return waitTime = int( response["dailyKeysMiniGame"]["remainSecondsToGuess"] - random.randint(8, 15) ) if waitTime < 0: log.error(f"[{self.account_name}] Unable to claim mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to claim mini game.", "other_errors" ) return log.info( f"[{self.account_name}] Waiting for {waitTime} seconds, Mini-game will be completed in {waitTime} seconds..." ) time.sleep(waitTime) url = "https://api.hamsterkombatgame.io/clicker/claim-daily-keys-minigame" headers = { "Access-Control-Request-Headers": "authorization,content-type", "Access-Control-Request-Method": "POST", } # Send OPTIONS request self.HttpRequest(url, headers, "OPTIONS", 204) headers = { "Accept": "application/json", "Authorization": self.Authorization, "Content-Type": "application/json", } cipher = ( ("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10] + "|" + str(AccountID) ) cipher_base64 = base64.b64encode(cipher.encode()).decode() payload = json.dumps( { "cipher": cipher_base64, } ) # Send POST request response = self.HttpRequest(url, headers, "POST", 200, payload) if response is None: log.error(f"[{self.account_name}] Unable to claim mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to claim mini game.", "other_errors" ) return log.info(f"[{self.account_name}] Mini game claimed successfully.")
`
This is just #1622 (comment) again
daily_mini_game = game_config.get('dailyKeysMiniGame')
if daily_mini_game:
is_claimed = daily_mini_game['isClaimed']
seconds_to_next_attempt = daily_mini_game['remainSecondsToNextAttempt']
cipher = ''
if not is_claimed and seconds_to_next_attempt <= 0:
await start_daily_mini_game(http_client=http_client)
waitTime = randint(18, 26)
logger.info(f"{self.session_name} | "
f"<lr>Start claim daily mini game</lr> | Wait <ly>{waitTime}</ly> seconds")
await asyncio.sleep(delay=waitTime)
cipher = (
("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10]
+ "|"
+ str(profile_data['id'])
)
cipher_base64 = base64.b64encode(cipher.encode()).decode()
await claim_daily_mini_game(http_client=http_client, cipher=cipher_base64)
logger.info(f"{self.session_name} | "
f"Successfully claim daily mini game")
Работает. Не забудье про имморт рандома и басе64.
daily_mini_game = game_config.get('dailyKeysMiniGame') if daily_mini_game: is_claimed = daily_mini_game['isClaimed'] seconds_to_next_attempt = daily_mini_game['remainSecondsToNextAttempt'] cipher = '' if not is_claimed and seconds_to_next_attempt <= 0: await start_daily_mini_game(http_client=http_client) waitTime = randint(18, 26) logger.info(f"{self.session_name} | " f"<lr>Start claim daily mini game</lr> | Wait <ly>{waitTime}</ly> seconds") await asyncio.sleep(delay=waitTime) cipher = ( ("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10] + "|" + str(profile_data['id']) ) cipher_base64 = base64.b64encode(cipher.encode()).decode() await claim_daily_mini_game(http_client=http_client, cipher=cipher_base64) logger.info(f"{self.session_name} | " f"Successfully claim daily mini game")
Если это и работает, явно плохое решение. waitTime принимает значение от 18 до 26, а значит начала шифров будут от 018...
до 026...
, а уже было получено, что иногда попадаются 057...
шифры
Решение не элегантное, но @dmitryGolikov спасибо. Применил у себя в форке и жду более элегантное решение от автора данной ветки.
иногда попадаются 057... шифры
Просто первая часть шифра это 10 цифр random.randint(0, 9)
, но в одном из индексов, в зависимости от значения dailyKeysMiniGame.startDate
, записывается 0
. Сейчас это индекс 0.
На бекенде как раз должны проверять расположение 0
в строке.
cipher[number % 8] == 0
от 1721635800 до 1721635899
cipher[(parseInt(num_str[8]) * 2 + parseInt(num_str[9])) % 8] == 0
Если нужен индекс 0
, то тут даже проще, cipher[number % 8] == 0
Тут DefoldGames.wasm и конвертированная в C версия (Я не шарю, но похоже обфускацирована) DefoldGames.zip
это просто движок Defold, сама логика игры в lua скриптах
Тут все файлы игры: DefoldGames.zip
это просто движок Defold, сама логика игры в lua скриптах
А где хранятся Lua скрипты
я уже писал в ветке - поищи
Решение не элегантное, но @dmitryGolikov спасибо. Применил у себя в форке и жду более элегантное решение от автора данной ветки.
@hardim26 ваш форк не запустился, внес правки в текущий скрипт заработало спасибо
You can get the daily mini game reward on a different bot with this code.
`def StartMiniGame(self, AccountConfigData, AccountID): if "dailyKeysMiniGame" not in AccountConfigData: log.error(f"[{self.account_name}] Unable to get daily keys mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to get daily keys mini game.", "other_errors", ) return
if AccountConfigData["dailyKeysMiniGame"]["isClaimed"] == True: log.info(f"[{self.account_name}] Daily keys mini game already claimed.") return if AccountConfigData["dailyKeysMiniGame"]["remainSecondsToNextAttempt"] > 0: log.info(f"[{self.account_name}] Daily keys mini game is on cooldown...") return ## check timer. url = "https://api.hamsterkombatgame.io/clicker/start-keys-minigame" headers = { "Access-Control-Request-Headers": "authorization", "Access-Control-Request-Method": "POST", } # Send OPTIONS request self.HttpRequest(url, headers, "OPTIONS", 204) headers = { "Authorization": self.Authorization, } # Send POST request response = self.HttpRequest(url, headers, "POST", 200) if response is None: log.error(f"[{self.account_name}] Unable to start mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to start mini game.", "other_errors" ) return if "dailyKeysMiniGame" not in response: log.error(f"[{self.account_name}] Unable to get daily keys mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to get daily keys mini game.", "other_errors", ) return if response["dailyKeysMiniGame"]["isClaimed"] == True: log.info(f"[{self.account_name}] Daily keys mini game already claimed.") return if "remainSecondsToGuess" not in response["dailyKeysMiniGame"]: log.error(f"[{self.account_name}] Unable to get daily keys mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to get daily keys mini game.", "other_errors", ) return waitTime = int( response["dailyKeysMiniGame"]["remainSecondsToGuess"] - random.randint(8, 15) ) if waitTime < 0: log.error(f"[{self.account_name}] Unable to claim mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to claim mini game.", "other_errors" ) return log.info( f"[{self.account_name}] Waiting for {waitTime} seconds, Mini-game will be completed in {waitTime} seconds..." ) time.sleep(waitTime) url = "https://api.hamsterkombatgame.io/clicker/claim-daily-keys-minigame" headers = { "Access-Control-Request-Headers": "authorization,content-type", "Access-Control-Request-Method": "POST", } # Send OPTIONS request self.HttpRequest(url, headers, "OPTIONS", 204) headers = { "Accept": "application/json", "Authorization": self.Authorization, "Content-Type": "application/json", } cipher = ( ("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10] + "|" + str(AccountID) ) cipher_base64 = base64.b64encode(cipher.encode()).decode() payload = json.dumps( { "cipher": cipher_base64, } ) # Send POST request response = self.HttpRequest(url, headers, "POST", 200, payload) if response is None: log.error(f"[{self.account_name}] Unable to claim mini game.") self.SendTelegramLog( f"[{self.account_name}] Unable to claim mini game.", "other_errors" ) return log.info(f"[{self.account_name}] Mini game claimed successfully.")
`
Please how do i run this code???
Guys, I'm too stupid to enter this code somewhere, I entered it in mini_game.py, but when I run the script, the window does not show me at all that the game has started or ended. Where should I enter this code?`async def claim_daily_mini_game(
http_client,
cipher: str
):
daily_mini_game = game_config.get('dailyKeysMiniGame')
if daily_mini_game:
is_claimed = daily_mini_game['isClaimed']
seconds_to_next_attempt = daily_mini_game['remainSecondsToNextAttempt']
cipher = ''
if not is_claimed and seconds_to_next_attempt <= 0:
await start_daily_mini_game(http_client=http_client)
waitTime = randint(18, 26)
logger.info(f"{self.session_name} | "
f"<lr>Start claim daily mini game</lr> | Wait <ly>{waitTime}</ly> seconds")
await asyncio.sleep(delay=waitTime)
cipher = (
("0" + str(waitTime) + str(random.randint(10000000000, 99999999999)))[:10]
+ "|"
+ str(profile_data['id'])
)
cipher_base64 = base64.b64encode(cipher.encode()).decode()
response_json = await claim_daily_mini_game(http_client=http_client, cipher=cipher_base64)
logger.info(f"{self.session_name} | "
f"Successfully claim daily mini game")
return response_json`
Дай туториал, куда че написать, для тех кто вообще не шарит
Для тех кто не шарит, сидите ждите, пока вам в проект добавят. Иначе на вас туториалов не напасешься, каждый кусок кода описывать что и как.
В итоге таки расшифровал LUA скрипты ))
Скажем так, пока что вам не о чем беспокоится ))
ЗЫ. Координаты, кстати, у игры перевёрнуты, видимо разраб ее в портретном режиме писал
@lolyinseo, если не трудно можешь скинуть?
ЗЫ. Координаты, кстати, у игры перевёрнуты, видимо разраб ее в портретном режиме писал
Тоже не сразу осознал, что limit.y
это по горизонтали))
Give me a tutorial on where to write what, for those who don't know anything at all
For those who don't know, sit and wait until they add it to your project. Otherwise, there won't be enough tutorials for you, describing every piece of code what and how.
file bot\core\tapper.py
+
import asyncio import random import base64
Thank you)
I couldn't get it to run, I keep getting indentation error, can you just post tapper.py script so that I can just copy it?
не знаю что вы гадаете с этим шифром, но такой вариант у меня работает. Ну сбреют и пох - еще 20 таких ботов крутится) тем более я так вижу что эти хомяки-нищеброды и раздадут только сосательные конфеты)
daily_mini_game = game_config.get('dailyKeysMiniGame')
if daily_mini_game:
is_claimed = daily_mini_game['isClaimed']
isnt_claimed_str = 'not ' if not is_claimed else ''
logger.info(f'{self.session_name} | '
f'Daily Keys MiniGame is available and {isnt_claimed_str}claimed')
seconds_to_next_attempt = daily_mini_game['remainSecondsToNextAttempt']
waitTime = int(30 * (randint(7, 8)/10))
if not is_claimed and seconds_to_next_attempt <= 0:
logger.info(f'{self.session_name} | Starting Daily Keys MiniGame')
await start_daily_mini_game(http_client=http_client)
logger.info(f'{self.session_name} | Solving MiniGame wait {waitTime} seconds')
await asyncio.sleep(delay=waitTime)
cipher_str = (
("0" + str(waitTime) + str(randint(10000000000, 99999999999)))[:10]
+ "|"
+ str(account_info['accountInfo']['id'])
)
cipher = base64.b64encode(cipher_str.encode()).decode()
game_result = await claim_daily_mini_game(http_client=http_client, cipher=cipher)
if game_result['dailyKeysMiniGame']['isClaimed']:
logger.success(f'{self.session_name} | '
f'Daily Keys MiniGame is successfully solved! '
f'You earned {game_result["dailyKeysMiniGame"]["bonusKeys"]} key(s)')
В итоге таки расшифровал LUA скрипты )) Скажем так, пока что вам не о чем беспокоится ))
ЗЫ. Координаты, кстати, у игры перевёрнуты, видимо разраб ее в портретном режиме писал
Да, интересно, что там в скриптах. Они лежат отдельно от wasm файлов?
В итоге таки расшифровал LUA скрипты )) Скажем так, пока что вам не о чем беспокоится ))
ЗЫ. Координаты, кстати, у игры перевёрнуты, видимо разраб ее в портретном режиме писалДа, интересно, что там в скриптах. Они лежат отдельно от wasm файлов?
вся инфа есть в исходниках defold https://github.com/defold/defold
вся инфа есть в исходниках defold https://github.com/defold/defold
Дык интересны луа скрипты, в чем проблема их выложить?
Добавление playwright убивает ферму. запускает на каждый тапер хром. Переделайте пожалуйста на использование одной вкладки. Либо какой-то distributed lock между сессиями. В общем в данный момент 100ботов вешают комп с Core I9 наглухо.
Умники-разумники раскопали исходники и не хотят их публиковать, и при это для получения кода-отгадки подсовывают в функцию пустой уровень и эмулируют движения мышки чтобы его решить, вместо того чтобы скопировать код из исходников, который отгадку генерит 🤔
вот функция из исходников lua
function get_game_cipher(self, red_bar_limit_y)
self.value = math.max(0, self.matrix_size - red_bar_limit_y)
local str_len = string.len(self.number_str)
local index = (tonumber(self.number_str) % (str_len - 2)) + 1
local res = ""
for i = 1, str_len do
if i == index then
res = res .. self.value
else
res = res .. math.random(0, 9)
end
end
pprint("��������� �����: " .. self.number_str)
pprint("����� �����: " .. str_len)
pprint("������: " .. index)
pprint("���������: " .. res)
return res
end
self.matrix_size = 6
Ранее есть условие что
red_bar_limit_y >= self.matrix_size - 1
Соответственно код
self.value = math.max(0, self.matrix_size - red_bar_limit_y)
Будет выдавать либо 0 либо 1, скорей всего зависит от того как сильно ключ смахнуть