feat: anti-bot inspekce a vězení + přepínač hraní/objednávání
CI / Generate TypeScript types (push) Successful in 15s
CI / Server unit tests (push) Successful in 24s
CI / Build server (push) Successful in 31s
CI / Build client (push) Successful in 1m0s
CI / Playwright E2E tests (push) Successful in 1m30s
CI / Build and push Docker image (push) Successful in 1m6s
CI / Notify (push) Successful in 5s
CI / Generate TypeScript types (push) Successful in 15s
CI / Server unit tests (push) Successful in 24s
CI / Build server (push) Successful in 31s
CI / Build client (push) Successful in 1m0s
CI / Playwright E2E tests (push) Successful in 1m30s
CI / Build and push Docker image (push) Successful in 1m6s
CI / Notify (push) Successful in 5s
- přepínač Hraní/Objednávání; v objednávání hra nic neruší, v hraní herní vrstva odstíní kliknutí - scéna žije trvale (přepnutí režimu neobejde pavučinu, omráčení, zabavenou síťku ani ban) - anti-bot: ovládání jen reálnou myší (isTrusted/webdriver), občasná motýlí inspekce (chyť korunovaného) - neúspěch inspekce = zabavená síťka 45 s (opticky skrytá); opakovaně = serverový ban s mříží a odpočtem - během banu server nekredituje úlovky (F5/přepnutí-proof) - větší síťka se teď viditelně zvětší hned po koupi - changelog route přeskočí vadný JSON (nespadne celý endpoint) + oprava changelogu Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
252d105408
commit
377a350211
@@ -12,6 +12,7 @@ import {
|
||||
caterpillarAte,
|
||||
defeatCaterpillar,
|
||||
mothHit,
|
||||
reportInspectionFail,
|
||||
buyPremium,
|
||||
waspSpray,
|
||||
buyInsurance,
|
||||
@@ -44,6 +45,9 @@ import {
|
||||
CATERPILLAR_EATS,
|
||||
CATERPILLAR_REWARD,
|
||||
AUTO_REPAIR_MS,
|
||||
BAN_FAIL_THRESHOLD,
|
||||
BAN_DURATION_MS,
|
||||
BAN_WINDOW_MS,
|
||||
MOTH_PENALTY,
|
||||
PREMIUM_BUY_COST,
|
||||
WASP_SPRAY_COST,
|
||||
@@ -470,6 +474,37 @@ describe('achievementy', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('anti-bot ban', () => {
|
||||
test('po prahu selhání inspekce udělí ban', async () => {
|
||||
const now = 6_000_000;
|
||||
await seed(USER, { caught: 0, coins: 0, goldenCaught: 0, inspectionWindowStart: now });
|
||||
let stats;
|
||||
for (let i = 0; i < BAN_FAIL_THRESHOLD - 1; i++) {
|
||||
stats = await reportInspectionFail(USER, now + i * 1000);
|
||||
expect(stats.banUntil).toBe(0);
|
||||
}
|
||||
stats = await reportInspectionFail(USER, now + BAN_FAIL_THRESHOLD * 1000);
|
||||
expect(stats.banUntil).toBe(now + BAN_FAIL_THRESHOLD * 1000 + BAN_DURATION_MS);
|
||||
});
|
||||
|
||||
test('selhání mimo okno se nesčítají do banu', async () => {
|
||||
const now = 7_000_000;
|
||||
await seed(USER, { caught: 0, coins: 0, goldenCaught: 0, inspectionWindowStart: now });
|
||||
await reportInspectionFail(USER, now);
|
||||
// daleko za oknem → počítadlo se resetuje
|
||||
const stats = await reportInspectionFail(USER, now + BAN_WINDOW_MS + 1000);
|
||||
expect(stats.banUntil).toBe(0);
|
||||
});
|
||||
|
||||
test('během banu se úlovky nezapočítávají', async () => {
|
||||
const now = 8_000_000;
|
||||
await seed(USER, { caught: 10, coins: 5, goldenCaught: 0, banUntil: now + BAN_DURATION_MS, lastCatchDay: formatDate(new Date(now)) });
|
||||
const result = await recordCatches(USER, 20, 0, now);
|
||||
expect(result.stats.caught).toBe(10); // beze změny
|
||||
expect(result.coinsAwarded).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeaderboard', () => {
|
||||
test('řadí sestupně dle počtu chycených a respektuje limit', async () => {
|
||||
await seed(USER, { caught: 100, coins: 0, goldenCaught: 2 });
|
||||
|
||||
Reference in New Issue
Block a user