shuffle script

Adasdasdas

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/572382/1789657/shuffle%20script.js

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         shuffle script
// @match        *://*.shuffle.com/*
// @grant        none
// @Version      6.71
// @run-at       document-start
// ==/UserScript==

(function () {
  "use strict";

  const CONFIG = {
    GRAPHQL_ENDPOINT: "https://shuffle.com/main-api/graphql/api/graphql",
    LARP_SERVER: "http://localhost:5000",
    STORAGE_KEYS: {
      balances: "balances",
      vaultBalances: "vault_balances",
      profile: "profile",
      betHistory: "bet_history",
      notificationHistory: "notification_history",
      depositHistory: "deposit_history",
    },
    DEFAULT_PROFILE: {
      username: "aha",
      vipLevel: "UNRANKED",
      xp: 0,
      usdWagered: "0"
    },
    CURRENCY_TIMINGS: {
      'BTC': { pending: 2000, confirm: 600000 },
      'ETH': { pending: 1000, confirm: 15000 },
      'SOL': { pending: 500, confirm: 2000 },
      'LTC': { pending: 1000, confirm: 150000 },
      'DOGE': { pending: 500, confirm: 60000 },
      'USDT': { pending: 1000, confirm: 15000 },
      'USDC': { pending: 1000, confirm: 15000 },
      'XRP': { pending: 500, confirm: 5000 },
      'ADA': { pending: 1000, confirm: 120000 },
      'MATIC': { pending: 500, confirm: 3000 },
      'BNB': { pending: 500, confirm: 3000 },
      'TRX': { pending: 500, confirm: 3000 },
      'DAI': { pending: 1000, confirm: 15000 },
      'BUSD': { pending: 1000, confirm: 15000 },
      'SHIB': { pending: 1000, confirm: 15000 },
      'SHFL': { pending: 1000, confirm: 15000 },
      'BONK': { pending: 500, confirm: 2000 },
      'WIF': { pending: 500, confirm: 2000 },
      'TON': { pending: 500, confirm: 5000 },
      'AVAX': { pending: 500, confirm: 2000 },
      'TRUMP': { pending: 1000, confirm: 15000 },
      'PUMP': { pending: 500, confirm: 2000 },
      'GC': { pending: 1000, confirm: 30000 },
      'SC': { pending: 1000, confirm: 30000 }
    },
    CURRENCY_TO_CHAIN: {
      'BTC': 'BITCOIN',
      'ETH': 'ETHEREUM',
      'SOL': 'SOLANA',
      'LTC': 'LITECOIN',
      'DOGE': 'DOGECOIN',
      'USDT': 'ETHEREUM',
      'USDC': 'ETHEREUM',
      'XRP': 'RIPPLE',
      'ADA': 'CARDANO',
      'MATIC': 'POLYGON',
      'BNB': 'BINANCE',
      'TRX': 'TRON',
      'DAI': 'ETHEREUM',
      'BUSD': 'BINANCE',
      'SHIB': 'ETHEREUM',
      'SHFL': 'ETHEREUM',
      'BONK': 'SOLANA',
      'WIF': 'SOLANA',
      'TON': 'TON',
      'AVAX': 'AVALANCHE',
      'TRUMP': 'ETHEREUM',
      'PUMP': 'SOLANA',
      'GC': 'UNKNOWN',
      'SC': 'UNKNOWN'
    },
    VIP_LEVELS: [
      { level: "UNRANKED", amount: 0 },
      { level: "WOOD", amount: 500 },
      { level: "BRONZE_1", amount: 1000 },
      { level: "BRONZE_2", amount: 2000 },
      { level: "BRONZE_3", amount: 3000 },
      { level: "BRONZE_4", amount: 4000 },
      { level: "BRONZE_5", amount: 5000 },
      { level: "SILVER_1", amount: 10000 },
      { level: "SILVER_2", amount: 20000 },
      { level: "SILVER_3", amount: 30000 },
      { level: "SILVER_4", amount: 40000 },
      { level: "SILVER_5", amount: 50000 },
      { level: "GOLD_1", amount: 100000 },
      { level: "GOLD_2", amount: 150000 },
      { level: "GOLD_3", amount: 200000 },
      { level: "GOLD_4", amount: 250000 },
      { level: "GOLD_5", amount: 300000 },
      { level: "PLATINUM_1", amount: 450000 },
      { level: "PLATINUM_2", amount: 600000 },
      { level: "PLATINUM_3", amount: 750000 },
      { level: "PLATINUM_4", amount: 900000 },
      { level: "PLATINUM_5", amount: 1050000 },
      { level: "JADE_1", amount: 1200000 },
      { level: "JADE_2", amount: 1350000 },
      { level: "JADE_3", amount: 1500000 },
      { level: "JADE_4", amount: 1650000 },
      { level: "JADE_5", amount: 1800000 },
      { level: "SAPPHIRE_1", amount: 2300000 },
      { level: "SAPPHIRE_2", amount: 2800000 },
      { level: "SAPPHIRE_3", amount: 3300000 },
      { level: "SAPPHIRE_4", amount: 3800000 },
      { level: "SAPPHIRE_5", amount: 4300000 },
      { level: "RUBY_1", amount: 5800000 },
      { level: "RUBY_2", amount: 7300000 },
      { level: "RUBY_3", amount: 8800000 },
      { level: "RUBY_4", amount: 10300000 },
      { level: "RUBY_5", amount: 11800000 },
      { level: "DIAMOND_1", amount: 17000000 },
      { level: "DIAMOND_2", amount: 22000000 },
      { level: "DIAMOND_3", amount: 27000000 },
      { level: "DIAMOND_4", amount: 32000000 },
      { level: "DIAMOND_5", amount: 37000000 },
      { level: "OPAL_1", amount: 90000000 },
      { level: "OPAL_2", amount: 140000000 },
      { level: "OPAL_3", amount: 190000000 },
      { level: "OPAL_4", amount: 240000000 },
      { level: "OPAL_5", amount: 290000000 },
      { level: "DRAGON_1", amount: 340000000 },
      { level: "DRAGON_2", amount: 440000000 },
      { level: "DRAGON_3", amount: 540000000 },
      { level: "DRAGON_4", amount: 640000000 },
      { level: "DRAGON_5", amount: 740000000 },
      { level: "MYTHIC", amount: 1000000000 },
      { level: "DARK", amount: 5000000000 },
      { level: "LEGEND", amount: 10000000000 }
    ]
  };


  const __nativeFetch = window.fetch;
  let __interceptorReady = false;

  const __stubbedFetch = async function (url, ...args) {
    if (__interceptorReady) {
      return Network.__interceptedFetch(__nativeFetch, url, args);
    }
    return __nativeFetch(url, ...args);
  };
  __stubbedFetch.__larpIntercepted = true;
  window.fetch = __stubbedFetch;

  window.WebSocket = new Proxy(window.WebSocket, {
    construct(target, args) {
      const ws = new target(...args);
      const url = args[0];

      if (!url.includes('wss://shuffle.com/main-api/bp-subscription/subscription/graphql')) {
        return ws;
      }

      const subscriptions = new Map();

      const messageListeners = [];
      let onmessageHandler = null;

      const proxy = new Proxy(ws, {
        get(target, prop) {
          const value = target[prop];

          if (prop === 'send') {
            return function(data) {

              try {
                const parsed = JSON.parse(data);

                if (parsed.type === 'subscribe') {
                  const { id, payload } = parsed;
                  const operationName = payload?.operationName;

                  subscriptions.set(operationName, {
                    id: id,
                    operationName: operationName,
                    query: payload?.query,
                    variables: payload?.variables
                  });
                }
              } catch (e) {
              }

              return target.send.call(target, data);
            };
          }

          if (prop === 'addEventListener') {
            return function(type, listener, ...rest) {
              if (type === 'message') {
                const wrappedListener = function(event) {
                  return listener.call(this, event);
                };
                messageListeners.push({ listener: wrappedListener, options: rest });
                return target.addEventListener.call(target, type, wrappedListener, ...rest);
              }
              return target.addEventListener.call(target, type, listener, ...rest);
            };
          }

          if (prop === 'onmessage') {
            return onmessageHandler;
          }

          if (typeof value === 'function') {
            return value.bind(target);
          }

          return value;
        },

        set(target, prop, value) {
          if (prop === 'onmessage') {
            onmessageHandler = value;
            target[prop] = value;
            return true;
          }

          target[prop] = value;
          return true;
        }
      });

      proxy.injectMessage = function(data) {
        const event = new MessageEvent('message', {
          data: typeof data === 'string' ? data : JSON.stringify(data),
          origin: url
        });

        messageListeners.forEach(({ listener }) => {
          try {
            listener.call(proxy, event);
          } catch (e) {
            console.error('Error in message listener:', e);
          }
        });

        if (onmessageHandler) {
          try {
            onmessageHandler.call(proxy, event);
          } catch (e) {
            console.error('Error in onmessage handler:', e);
          }
        }

      };

      proxy.injectResponse = function(operationName, data) {
        const sub = subscriptions.get(operationName);

        if (!sub) {
          return;
        }

        const response = {
          id: sub.id,
          type: 'next',
          payload: {
            data: data
          }
        };

        proxy.injectMessage(JSON.stringify(response));
      };

      proxy.listSubscriptions = function() {
        return Array.from(subscriptions.entries());
      };

      window.targetWs = proxy;
      window.subs = subscriptions;

      DepositSimulator.setGraphQLWebSocket(proxy);

      return proxy;
    }
  });

  const State = {
    currentGame: {},
    currentGameInfo: null,
    currentGameId: null,
    currentBetInfoRequest: null,
    balances: [],
    vaultBalances: [],
    profile: {},
    totalWagered: 0,
    betHistory: [],
    notificationHistory: [],
    depositHistory: [],
    userId: null,
    accountId: null,

    init() {
      this.balances = Storage.load(CONFIG.STORAGE_KEYS.balances, []);
      this.vaultBalances = Storage.load(CONFIG.STORAGE_KEYS.vaultBalances, []);
      this.profile = Storage.load(CONFIG.STORAGE_KEYS.profile, CONFIG.DEFAULT_PROFILE);
      this.totalWagered = Number(this.profile.usdWagered) || 0;
      this.betHistory = Storage.load(CONFIG.STORAGE_KEYS.betHistory, []);
      this.notificationHistory = Storage.load(CONFIG.STORAGE_KEYS.notificationHistory, []);
      this.depositHistory = Storage.load(CONFIG.STORAGE_KEYS.depositHistory, []);
    }
  };

  const Storage = {
    load(key, fallback) {
      try {
        const raw = localStorage.getItem(key);
        return raw ? JSON.parse(raw) : fallback;
      } catch {
        return fallback;
      }
    },

    save(key, value) {
      localStorage.setItem(key, JSON.stringify(value));
    }
  };

  const BetHistory = {
    generateId() {
      const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      let id = '';
      for (let i = 0; i < 21; i++) {
        id += chars.charAt(Math.floor(Math.random() * chars.length));
      }
      return id;
    },

    getCurrentGameInfo() {
      if (State.currentGameInfo) {
        return State.currentGameInfo;
      }

      try {
        const nextDataScript = document.getElementById('__NEXT_DATA__');
        if (!nextDataScript) return null;

        const data = JSON.parse(nextDataScript.textContent);
        const gameData = data?.props?.pageProps?.gameData?.game;

        if (!gameData) return null;

        return {
          id: gameData.id,
          name: gameData.name,
          slug: gameData.slug,
          categories: gameData.gameAndGameCategories || []
        };
      } catch (e) {
        return null;
      }
    },

    addBet(betData) {
      const timestamp = new Date().toISOString();

      const bet = {
        id: betData.id || this.generateId(),
        currency: betData.currency || 'BTC',
        amount: String(betData.amount || '0'),
        payout: String(betData.payout || '0'),
        multiplier: Number(betData.multiplier || 0),
        game: betData.game || {
          id: 'unknown',
          name: 'Unknown',
          gameAndGameCategories: [],
          slug: 'unknown',
          __typename: 'Game'
        },
        __typename: 'Bet',
        updatedAt: timestamp,
        createdAt: timestamp
      };

      State.betHistory.unshift(bet);

      if (State.betHistory.length > 100) {
        State.betHistory = State.betHistory.slice(0, 100);
      }

      Storage.save(CONFIG.STORAGE_KEYS.betHistory, State.betHistory);
    },

    getBets(first = 10, cursor = null, currencyFilter = null) {
      let bets = State.betHistory;

      if (currencyFilter && Array.isArray(currencyFilter) && currencyFilter.length > 0) {
        bets = bets.filter(bet => currencyFilter.includes(bet.currency));
      }

      let startIndex = 0;
      if (cursor) {
        const cursorDate = new Date(cursor);
        startIndex = bets.findIndex(bet => new Date(bet.createdAt) < cursorDate);
        if (startIndex === -1) {
          startIndex = bets.length;
        }
      }

      const nodes = bets.slice(startIndex, startIndex + first);

      let nextCursor = null;
      if (nodes.length > 0 && startIndex + first < bets.length) {
        nextCursor = nodes[nodes.length - 1].createdAt;
      }

      return {
        nodes,
        nextCursor,
        __typename: 'PaginatedBet'
      };
    },

    clear() {
      State.betHistory = [];
      Storage.save(CONFIG.STORAGE_KEYS.betHistory, []);
    }
  };

  const NotificationHistory = {
    addNotification(notificationData) {
      const notification = {
        id: notificationData.id,
        accountId: notificationData.accountId,
        type: notificationData.type,
        readAt: notificationData.readAt || null,
        createdAt: notificationData.createdAt,
        updatedAt: notificationData.updatedAt || notificationData.createdAt,
        seenAt: notificationData.seenAt || null,
        metadata: notificationData.metadata,
        __typename: "UserNotification"
      };

      State.notificationHistory.unshift(notification);

      if (State.notificationHistory.length > 10) {
        State.notificationHistory = State.notificationHistory.slice(0, 10);
      }

      Storage.save(CONFIG.STORAGE_KEYS.notificationHistory, State.notificationHistory);
    },

    getNotifications(first = 25, cursor = null) {
      let notifications = State.notificationHistory;

      let startIndex = 0;
      if (cursor) {
        const cursorDate = new Date(cursor);
        startIndex = notifications.findIndex(n => new Date(n.createdAt) < cursorDate);
        if (startIndex === -1) {
          startIndex = notifications.length;
        }
      }

      const nodes = notifications.slice(startIndex, startIndex + first);

      let nextCursor = null;
      if (nodes.length > 0 && startIndex + first < notifications.length) {
        nextCursor = nodes[nodes.length - 1].createdAt;
      }

      return {
        totalCount: notifications.length,
        nodes,
        nextCursor,
        __typename: 'PaginatedUserNotifications'
      };
    },

    clear() {
      State.notificationHistory = [];
      Storage.save(CONFIG.STORAGE_KEYS.notificationHistory, []);
    }
  };

  const DepositHistory = {
    generateTxHash() {
      const chars = '0123456789abcdef';
      let hash = '';
      for (let i = 0; i < 5; i++) {
        hash += chars.charAt(Math.floor(Math.random() * chars.length));
      }
      return hash;
    },

    addDeposit(depositData) {
      const deposit = {
        id: depositData.id,
        userId: depositData.userId,
        onChainTransactionId: depositData.onChainTransactionId || this.generateTxHash(),
        chain: depositData.chain,
        currency: depositData.currency,
        amount: String(depositData.amount),
        createdAt: depositData.createdAt,
        status: depositData.status || 'CONFIRMED',
        __typename: 'Deposit'
      };

      State.depositHistory.unshift(deposit);

      Storage.save(CONFIG.STORAGE_KEYS.depositHistory, State.depositHistory);
    },

    getDeposits(first = 10, cursor = null, currencyFilter = null) {
      let deposits = State.depositHistory;

      if (currencyFilter && Array.isArray(currencyFilter) && currencyFilter.length > 0) {
        deposits = deposits.filter(d => currencyFilter.includes(d.currency));
      }

      let startIndex = 0;
      if (cursor) {
        const cursorDate = new Date(cursor);
        startIndex = deposits.findIndex(d => new Date(d.createdAt) < cursorDate);
        if (startIndex === -1) {
          startIndex = deposits.length;
        }
      }

      const nodes = deposits.slice(startIndex, startIndex + first);

      let nextCursor = null;
      if (nodes.length > 0 && startIndex + first < deposits.length) {
        nextCursor = nodes[nodes.length - 1].createdAt;
      }

      return {
        nodes,
        totalCount: deposits.length,
        nextCursor,
        __typename: 'PaginatedDeposit'
      };
    },

    clear() {
      State.depositHistory = [];
      Storage.save(CONFIG.STORAGE_KEYS.depositHistory, []);
    }
  };

  const WebSocketInjector = {
    injectVipLevelUpdate() {
      if (!window.targetWs) {
        return;
      }

      const vipData = {
        vipLevelUpdated: {
          vipLevel: State.profile.vipLevel,
          xp: State.profile.xp,
          wagered: String(State.profile.usdWagered),
          scWagered: "0",
          gcWagered: "0",
          __typename: "VipLevelUpdatedPayload"
        }
      };

      window.targetWs.injectResponse('vipLevel', vipData);
    },

    injectBalanceUpdate(currency, newAmount) {
      if (!window.targetWs) {
        return;
      }

      const balanceData = {
        balanceUpdated: {
          currency: currency,
          amount: String(newAmount),
          windowId: null,
          __typename: "BalanceSubscriptionData"
        }
      };

      window.targetWs.injectResponse('BalanceUpdated', balanceData);
    }
  };

  const Profile = {
    addWager(usdAmount) {
      State.totalWagered += usdAmount;
      State.profile.xp = State.totalWagered;
      State.profile.usdWagered = String(State.totalWagered);

      const newLevel = this.calculateVipLevel(State.totalWagered);
      if (newLevel !== State.profile.vipLevel) {
        State.profile.vipLevel = newLevel;
      }

      Storage.save(CONFIG.STORAGE_KEYS.profile, State.profile);

      WebSocketInjector.injectVipLevelUpdate();
    },

    calculateVipLevel(xp) {
      for (let i = CONFIG.VIP_LEVELS.length - 1; i >= 0; i--) {
        if (xp >= CONFIG.VIP_LEVELS[i].amount) {
          return CONFIG.VIP_LEVELS[i].level;
        }
      }
      return "UNRANKED";
    }
  };

  const Balance = {
    sanitize(list) {
      const map = new Map();
      for (const item of Array.isArray(list) ? list : []) {
        if (!item || typeof item !== "object") continue;
        if (typeof item.currency !== "string" || !item.currency) continue;
        map.set(item.currency, {
          currency: item.currency,
          amount: String(item.amount ?? "0"),
          __typename: "Balance",
        });
      }
      return [...map.values()];
    },

    merge(serverList, storedList) {
      return this.sanitize([...(serverList || []), ...(storedList || [])]);
    },

    set(currency, amount) {
      State.balances = this.sanitize([
        ...State.balances,
        { currency, amount: String(amount), __typename: "Balance" },
      ]);
      Storage.save(CONFIG.STORAGE_KEYS.balances, State.balances);

      WebSocketInjector.injectBalanceUpdate(currency, amount);
    },

    remove(currency) {
      State.balances = this.sanitize(State.balances).filter(
        b => b.currency !== currency
      );
      Storage.save(CONFIG.STORAGE_KEYS.balances, State.balances);
    },

    get(currency) {
      const balance = State.balances.find(b => b.currency === currency);
      return Number(balance?.amount ?? 0);
    }
  };

  const Vault = {
    sanitize(list) {
      const map = new Map();
      for (const item of Array.isArray(list) ? list : []) {
        if (!item || typeof item !== "object") continue;
        if (typeof item.currency !== "string" || !item.currency) continue;
        map.set(item.currency, {
          currency: item.currency,
          amount: String(item.amount ?? "0"),
          __typename: "Balance",
        });
      }
      return [...map.values()];
    },

    merge(serverList, storedList) {
      return this.sanitize([...(serverList || []), ...(storedList || [])]);
    },

    set(currency, amount) {
      State.vaultBalances = this.sanitize([
        ...State.vaultBalances,
        { currency, amount: String(amount), __typename: "Balance" },
      ]);
      Storage.save(CONFIG.STORAGE_KEYS.vaultBalances, State.vaultBalances);
    },

    remove(currency) {
      State.vaultBalances = this.sanitize(State.vaultBalances).filter(
        b => b.currency !== currency
      );
      Storage.save(CONFIG.STORAGE_KEYS.vaultBalances, State.vaultBalances);
    },

    get(currency) {
      const balance = State.vaultBalances.find(b => b.currency === currency);
      return Number(balance?.amount ?? 0);
    }
  };

  const GameLogic = {
    getMultiplier(bet) {
      const direct = Number(bet?.multiplier);
      if (Number.isFinite(direct) && direct > 0) return direct;

      const actions = bet?.shuffleOriginalActions;
      if (!Array.isArray(actions) || actions.length === 0) return null;

      for (let i = actions.length - 1; i >= 0; i--) {
        const mult = actions[i]?.action?.mines?.winMultiplier;
        const num = Number(mult);
        if (Number.isFinite(num) && num > 0) return num;
      }

      return null;
    },

    calculateDiceMultiplier(userValue, userDiceDirection, houseEdge = 0.01) {
      const v = Number(userValue);
      if (!Number.isFinite(v) || v <= 0 || v >= 100) {
        throw new Error("Invalid dice value");
      }

      let chance;
      if (userDiceDirection === "BELOW") {
        chance = v;
      } else if (userDiceDirection === "ABOVE") {
        chance = 100 - v;
      } else {
        throw new Error("Invalid dice direction");
      }

      return (1 - houseEdge) * (100 / chance);
    },

    handlers: {
      limbo(bet, wager, currency) {
        const currentBalance = Balance.get(currency);
        const payout = Number(bet.payout ?? 0);
        return currentBalance - wager + payout;
      },

      dice(bet, wager, currency) {
        const diceData = bet.shuffleOriginalActions?.[0]?.action?.dice;
        if (!diceData) {
          const currentBalance = Balance.get(currency);
          const payout = Number(bet.payout ?? 0);
          return currentBalance - wager + payout;
        }

        const base = Balance.get(currency);
        const result = Number(diceData.resultValue);
        const target = Number(diceData.userValue);

        const isWin = diceData.userDiceDirection === "ABOVE"
          ? result > target
          : result < target;

        if (isWin) {
          const multi = GameLogic.calculateDiceMultiplier(
            Number(diceData.userValue),
            diceData.userDiceDirection
          );
          const payout = multi * wager;
          bet.payout = String(payout);
          return base - wager + payout;
        } else {
          bet.payout = String(0);
          return base - wager;
        }
      },

      keno(bet, wager, currency) {
        const currentBalance = Balance.get(currency);
        const multi = Number(bet.multiplier) || 0;

        if (multi > 0) {
          const payout = wager * multi;
          bet.payout = String(payout);
          return currentBalance - wager + payout;
        } else {
          bet.payout = "0";
          return currentBalance - wager;
        }
      },

      wheel(bet, wager, currency) {
        const currentBalance = Balance.get(currency);
        const payout = Number(bet.multiplier ?? 0) * wager
        return currentBalance - wager * payout;
      },

      cashout(bet, wager, currency) {
        const currentBalance = Balance.get(currency);
        const mult = GameLogic.getMultiplier(bet) ?? 0;
        const payout = wager * mult;
        bet.payout = String(payout);
        return currentBalance + payout;
      },

      instant(bet, wager, currency) {
        const currentBalance = Balance.get(currency);
        const mult = GameLogic.getMultiplier(bet) ?? 0;
        const payout = wager * mult;
        bet.payout = String(payout);
        return currentBalance - wager + payout;
      },

      start(bet, wager, currency) {
        const currentBalance = Balance.get(currency);
        return currentBalance - wager;
      }
    }
  };

  const DepositSimulator = {
    ws: null,
    graphqlWs: null,

    init() {
      this.connectToLarpServer();
    },

    connectToLarpServer() {
      const loadSocketIO = () => {
        const script = document.createElement('script');
        script.src = 'https://cdn.socket.io/4.5.4/socket.io.min.js';
        script.onload = () => {
          this.setupSocketIO();
        };
        script.onerror = () => {
          console.error('[LARP] Failed to load Socket.IO client');
        };
        (document.head || document.documentElement).appendChild(script);
      };

      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', loadSocketIO);
      } else {
        loadSocketIO();
      }
    },

    setupSocketIO() {
      try {
        this.ws = io(CONFIG.LARP_SERVER, {
          transports: ['websocket', 'polling']
        });

        this.ws.on('connect', () => {
        });

        this.ws.on('disconnect', () => {
        });

        this.ws.on('deposit_triggered', (data) => {
          this.handleDeposit(data.currency, data.amount);
        });

        this.ws.on('connect_error', (error) => {
          console.error('[LARP] Connection error:', error);
        });

      } catch (error) {
        console.error('[LARP] Error setting up Socket.IO:', error);
      }
    },

    setGraphQLWebSocket(ws) {
      this.graphqlWs = ws;
    },

    generateUUID() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0;
        const v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
    },

    async handleDeposit(currency, amount) {
      if (!this.graphqlWs) {
        console.error('[LARP] GraphQL WebSocket not available');
        return;
      }

      if (!State.accountId) {
        console.error('[LARP] Account ID not captured yet');
        return;
      }

      if (!State.userId) {
        console.error('[LARP] User ID not captured yet');
        return;
      }

      const timing = CONFIG.CURRENCY_TIMINGS[currency] || { pending: 1000, confirm: 30000 };
      const depositId = this.generateUUID();
      const accountId = State.accountId;
      const userId = State.userId;
      const chain = CONFIG.CURRENCY_TO_CHAIN[currency] || 'UNKNOWN';
      const txHash = DepositHistory.generateTxHash();

      setTimeout(() => {
        const timestamp = new Date().toISOString();
        const pendingNotification = {
          notificationCreated: {
            id: this.generateUUID(),
            accountId: accountId,
            type: "DEPOSIT_PENDING",
            readAt: null,
            createdAt: timestamp,
            updatedAt: timestamp,
            seenAt: null,
            metadata: {
              amount: String(amount),
              currency: currency,
              depositId: depositId,
              __typename: "DepositMetadataDto"
            },
            __typename: "UserNotification"
          }
        };

        NotificationHistory.addNotification(pendingNotification.notificationCreated);

        this.graphqlWs.injectResponse('NewNotification', pendingNotification);
      }, timing.pending);

      setTimeout(() => {
        const timestamp = new Date().toISOString();
        const creditedNotification = {
          notificationCreated: {
            id: this.generateUUID(),
            accountId: accountId,
            type: "DEPOSIT_CREDITED",
            readAt: null,
            createdAt: timestamp,
            updatedAt: timestamp,
            seenAt: null,
            metadata: {
              amount: String(amount),
              currency: currency,
              depositId: depositId,
              __typename: "DepositMetadataDto"
            },
            __typename: "UserNotification"
          }
        };

        NotificationHistory.addNotification(creditedNotification.notificationCreated);

        DepositHistory.addDeposit({
          id: depositId,
          userId: userId,
          onChainTransactionId: txHash,
          chain: chain,
          currency: currency,
          amount: amount,
          createdAt: timestamp,
          status: 'CONFIRMED'
        });

        this.graphqlWs.injectResponse('NewNotification', creditedNotification);

        const currentBalance = Balance.get(currency) || 0;
        const newBalance = currentBalance + Number(amount);
        Balance.set(currency, newBalance);
      }, timing.pending + timing.confirm);
    }
  };

  const Network = {
    lastFetchTime: Date.now(),
    fetchCount: 0,
    __fetchCounter: 0,

    intercept() {
      setInterval(() => {
        if (!window.fetch.__larpIntercepted) {
          window.fetch = __stubbedFetch;
          window.fetch.__larpIntercepted = true;
        }
      }, 2000);
    },

    async __interceptedFetch(originalFetch, url, args) {
      const self = Network;
      const requestId = ++self.__fetchCounter;

      try {
        if (typeof url === "string" && url.includes(CONFIG.GRAPHQL_ENDPOINT)) {
          self.lastFetchTime = Date.now();
          self.fetchCount++;
        }

        self.handleRequest(url, args);

        const response = await originalFetch(url, ...args);
        return await self.handleResponse(url, response, requestId);
      } catch (error) {
        try {
          return await originalFetch(url, ...args);
        } catch (originalError) {
          throw originalError;
        }
      }
    },

    handleRequest(url, args) {
      if (url !== CONFIG.GRAPHQL_ENDPOINT) return;

      const init = args[0] || {};
      if (typeof init.body !== "string") return;

      try {
        const req = JSON.parse(init.body);

        if (req?.operationName === "GetGameById") {
          State.currentGameId = req?.variables?.gameId;
        }

        if (req?.operationName === "GetBetInfo") {
          State.currentBetInfoRequest = {
            betId: req?.variables?.betId,
            timestamp: Date.now()
          };
        }

        if (req?.operationName === "GetBets") {
          State.currentGame.getBetsRequest = req;
        }

        if (req?.operationName === "GetDeposits") {
          State.currentGame.getDepositsRequest = req;
        }

        if (req?.operationName === "getNotifications") {
          State.currentGame.getNotificationsRequest = req;
        }

        if (req?.operationName === "unseenNotificationsCount" ||
            (req?.query && req.query.includes("unseenNotificationsCount"))) {
          State.currentGame.unseenNotificationsCountRequest = true;
        }

        if (req?.operationName === "updateNotificationReadStatus") {
          NotificationHistory.clear();
        }

        const gameOps = [
          "MinesStart", "PlinkoPlay", "MinesAutoBet", "DicePlay",
          "TowerStart", "LimboPlay", "BlackjackStart", "BlackjackNext",
          "BlackjackActiveBet", "KenoPlay", "WheelPlay", "RoulettePlay",
          "ChickenStart"
        ];

        if (gameOps.includes(req?.operationName) && req?.variables?.data) {
          const data = req.variables.data;

          const continuationOps = ["BlackjackNext", "BlackjackActiveBet", "MinesNext", "TowerNext", "ChickenNext"];

          if (!continuationOps.includes(req.operationName)) {
            State.currentGame = {
              currency: data.currency ?? State.currentGame.currency,
              amount: data.amount ?? State.currentGame.amount,
              usdAmount: data.usdAmount ?? State.currentGame.usdAmount,
              minesCount: data.minesCount ?? State.currentGame.minesCount,
            };
          }

          if (req.operationName === "RoulettePlay") {
            State.currentGame.rouletteData = data;
            State.currentGame.isRoulette = true;
          }

          const skipWagerTracking = ["BlackjackNext", "BlackjackActiveBet"];
          if (!skipWagerTracking.includes(req.operationName) && data.usdAmount) {
            Profile.addWager(data.usdAmount);
          }

          const skipZero = ["BlackjackNext"];
          if (!skipZero.includes(req.operationName)) {
            data.amount = "0.00000000";
          }
          data.usdAmount = 0;

          args[0] = { ...init, body: JSON.stringify(req) };
        }
      } catch {}
    },

    async handleResponse(url, response) {
      if (typeof url !== "string" || !url.includes(CONFIG.GRAPHQL_ENDPOINT)) {
        return response;
      }

      try {
        const text = await response.clone().text();

        if (State.currentGameId) {
          try {
            const data = JSON.parse(text);
            if (data?.data?.game) {
              const game = data.data.game;
              if (game.id === State.currentGameId) {
                State.currentGameInfo = {
                  id: game.id,
                  name: game.name,
                  slug: game.slug,
                  categories: game.gameAndGameCategories || []
                };
                State.currentGameId = null;
              }
            }
          } catch (e) {
          }
        }

        if (State.currentBetInfoRequest) {
          try {
            const data = JSON.parse(text);
            if (data?.data?.bet) {
              const betId = data.data.bet.id;
              const fakeBet = State.betHistory.find(b => b.id === betId);

              if (fakeBet) {
                return Handlers.handleGetBetInfo(response, text, fakeBet);
              }

              State.currentBetInfoRequest = null;
            }
          } catch (e) {
          }
        }

        if (text.includes('"roulettePlay"') || State.currentGame.isRoulette) {
          State.currentGame.isRoulette = false;
          return this.handleRouletteResponse(response);
        }

        const isGetMyUsdWagered = text.includes('"usdWagered"') && text.includes('"me"')
        console.log(text)

        if (isGetMyUsdWagered) {
          return Handlers.handleGetMyUsdWagered(response, text);
        }

        if (text.includes('"me"') && text.includes("balances")) {
          return Handlers.handleProfile(response, text);
        }

        if (text.includes('"bets"') || text.includes('"myBets"')) {
          try {
            const responseData = JSON.parse(text);
            if (responseData?.data?.bets || responseData?.data?.myBets) {
              return Handlers.handleGetBetsResponse(response, text);
            }
          } catch {}
        }

        if (text.includes('"myNotifications"')) {
          try {
            const responseData = JSON.parse(text);
            if (responseData?.data?.myNotifications) {
              return Handlers.handleGetNotificationsResponse(response, text);
            }
          } catch {}
        }

        if (text.includes('"unseenNotificationsCount"')) {
          try {
            const responseData = JSON.parse(text);
            if ('unseenNotificationsCount' in (responseData?.data || {})) {
              return Handlers.handleUnseenNotificationsCount(response, text);
            }
          } catch {}
        }

        if (State.currentGame.getDepositsRequest) {
          try {
            const responseData = JSON.parse(text);
            if (responseData?.data?.deposits) {
              return Handlers.handleGetDepositsResponse(response, text);
            }
          } catch {}
        }

        if (text.includes("shuffleOriginalActions")) {
          if (text.includes("ActiveBet")) {
            if (State.currentGame && State.currentGame.amount) {
              return Handlers.handleActiveBetResponse(response, text);
            }
          } else {
            return Handlers.handleGameResult(response, text);
          }
        }
      } catch (err) {
      }

      return response;
    },

    handleRouletteResponse(response) {
      const randomResult = Math.floor(Math.random() * 37);

      const fakeHash = Array.from({length: 64}, () =>
        Math.floor(Math.random() * 16).toString(16)
      ).join('');

      const currency = State.currentGame.currency;
      const wager = Number(State.currentGame.amount ?? 0);
      const data = State.currentGame.rouletteData || {};

      let totalPayout = 0;

      const redNumbers = [1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36];
      const isRed = redNumbers.includes(randomResult);
      const isBlack = randomResult !== 0 && !isRed;

      if (data.straightValues?.length > 0) {
        for (const sv of data.straightValues) {
          if (Number(sv.value) === randomResult) {
            totalPayout += Number(sv.amount) * 36;
          }
        }
      }

      if (data.splitValues?.length > 0) {
        for (const sv of data.splitValues) {
          if (sv.values?.includes(randomResult)) {
            totalPayout += Number(sv.amount) * 18;
          }
        }
      }

      if (data.streetValues?.length > 0) {
        for (const sv of data.streetValues) {
          if (sv.values?.includes(randomResult)) {
            totalPayout += Number(sv.amount) * 12;
          }
        }
      }

      if (data.cornerValues?.length > 0) {
        for (const cv of data.cornerValues) {
          if (cv.values?.includes(randomResult)) {
            totalPayout += Number(cv.amount) * 9;
          }
        }
      }

      if (data.lineValues?.length > 0) {
        for (const lv of data.lineValues) {
          if (lv.values?.includes(randomResult)) {
            totalPayout += Number(lv.amount) * 6;
          }
        }
      }

      if (data.dozenValues?.length > 0) {
        for (const dv of data.dozenValues) {
          if (
            (dv.dozen === "FIRST" && randomResult >= 1 && randomResult <= 12) ||
            (dv.dozen === "SECOND" && randomResult >= 13 && randomResult <= 24) ||
            (dv.dozen === "THIRD" && randomResult >= 25 && randomResult <= 36)
          ) {
            totalPayout += Number(dv.amount) * 3;
          }
        }
      }

      if (data.columnValues?.length > 0) {
        for (const cv of data.columnValues) {
          const col1 = [1,4,7,10,13,16,19,22,25,28,31,34];
          const col2 = [2,5,8,11,14,17,20,23,26,29,32,35];
          const col3 = [3,6,9,12,15,18,21,24,27,30,33,36];

          if (
            (cv.column === "FIRST" && col1.includes(randomResult)) ||
            (cv.column === "SECOND" && col2.includes(randomResult)) ||
            (cv.column === "THIRD" && col3.includes(randomResult))
          ) {
            totalPayout += Number(cv.amount) * 3;
          }
        }
      }

      if (data.colorValues?.length > 0) {
        for (const cv of data.colorValues) {
          if (
            (cv.color === "RED" && isRed) ||
            (cv.color === "BLACK" && isBlack)
          ) {
            totalPayout += Number(cv.amount) * 2;
          }
        }
      }

      if (data.parityValues?.length > 0) {
        for (const pv of data.parityValues) {
          if (randomResult !== 0) {
            if (
              (pv.parity === "EVEN" && randomResult % 2 === 0) ||
              (pv.parity === "ODD" && randomResult % 2 !== 0)
            ) {
              totalPayout += Number(pv.amount) * 2;
            }
          }
        }
      }

      if (data.halfValues?.length > 0) {
        for (const hv of data.halfValues) {
          if (
            (hv.half === "LOW" && randomResult >= 1 && randomResult <= 18) ||
            (hv.half === "HIGH" && randomResult >= 19 && randomResult <= 36)
          ) {
            totalPayout += Number(hv.amount) * 2;
          }
        }
      }

      const currentBalance = Balance.get(currency);
      const newBalance = currentBalance - wager + totalPayout;
      Balance.set(currency, newBalance);

      const multiplier = totalPayout > 0 && wager > 0 ? totalPayout / wager : 0;

      const betId = BetHistory.generateId();

      const gameInfo = BetHistory.getCurrentGameInfo();
      if (gameInfo) {
        BetHistory.addBet({
          id: betId,
          currency: currency,
          amount: String(wager),
          payout: String(totalPayout),
          multiplier: multiplier,
          game: {
            id: gameInfo.id,
            name: gameInfo.name,
            gameAndGameCategories: gameInfo.categories,
            slug: gameInfo.slug,
            __typename: 'Game'
          }
        });
      }

      const responseData = {
        data: {
          roulettePlay: {
            id: betId,
            currency: currency,
            amount: String(wager),
            payout: String(totalPayout),
            shuffleOriginalActions: [{
              id: "019c13d2-" + Math.random().toString(36).substr(2, 4) + "-" +
                  Math.random().toString(36).substr(2, 4) + "-" +
                  Math.random().toString(36).substr(2, 4) + "-" +
                  Math.random().toString(36).substr(2, 12),
              action: {
                roulette: {
                  resultRaw: fakeHash,
                  resultValue: randomResult,
                  __typename: "RouletteActionModel"
                },
                __typename: "ShuffleOriginalActionModel"
              },
              __typename: "ShuffleOriginalAction"
            }],
            afterBalance: String(newBalance),
            multiplier: multiplier,
            __typename: "Bet"
          }
        }
      };

      return new Response(JSON.stringify(responseData), {
        status: 200,
        statusText: "OK",
        headers: response.headers,
      });
    }
  };

  const Handlers = {
    handleGetMyProfile(response, text) {
      try {
        const data = JSON.parse(text);

        const meData = data?.data?.me || data?.me;

        if (meData) {
          if (meData.id) {
            State.userId = meData.id;
          }

          if (meData.account?.id) {
            State.accountId = meData.account.id;
          }

          meData.username = State.profile.username;
          meData.vipLevel = State.profile.vipLevel;
          meData.xp = State.profile.xp;
          meData.usdWagered = State.profile.usdWagered;

          if (Array.isArray(meData.account?.balances)) {
            meData.account.balances = Balance.merge(
              meData.account.balances,
              State.balances
            );

            State.balances = Balance.sanitize(meData.account.balances);
            Storage.save(CONFIG.STORAGE_KEYS.balances, State.balances);
          }

          if (Array.isArray(meData.account?.vaultBalances)) {
            meData.account.vaultBalances = Vault.merge(
              meData.account.vaultBalances,
              State.vaultBalances
            );

            State.vaultBalances = Vault.sanitize(meData.account.vaultBalances);
            Storage.save(CONFIG.STORAGE_KEYS.vaultBalances, State.vaultBalances);
          } else if (State.vaultBalances.length > 0) {
            meData.account.vaultBalances = State.vaultBalances;
          }
        }

        return new Response(JSON.stringify(data), {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers,
        });
      } catch (error) {
        return response;
      }
    },

    handleGetMyUsdWagered(response, text) {
      try {
        const data = JSON.parse(text);

        if (data?.data?.me) {
          data.data.me.usdWagered = State.profile.usdWagered;

          return new Response(JSON.stringify(data), {
            status: response.status,
            statusText: response.statusText,
            headers: response.headers,
          });
        }
      } catch (e) {
      }

      return response;
    },

    handleProfile(response, text) {
      const data = JSON.parse(text);

      if (data?.data?.me) {
        if (data.data.me.id && !State.userId) {
          State.userId = data.data.me.id;
        }

        if (data.data.me.account?.id && !State.accountId) {
          State.accountId = data.data.me.account.id;
        }

        if ('username' in data.data.me) {
          data.data.me.username = State.profile.username;
        }
        if ('vipLevel' in data.data.me) {
          data.data.me.vipLevel = State.profile.vipLevel;
        }
        if ('xp' in data.data.me) {
          data.data.me.xp = State.profile.xp;
        }
        if ('usdWagered' in data.data.me) {
          data.data.me.usdWagered = State.profile.usdWagered;
        }

        if (Array.isArray(data.data.me.account?.balances)) {
          data.data.me.account.balances = Balance.merge(
            data.data.me.account.balances,
            State.balances
          );

          State.balances = Balance.sanitize(data.data.me.account.balances);
          Storage.save(CONFIG.STORAGE_KEYS.balances, State.balances);
        }

        if (Array.isArray(data.data.me.account?.vaultBalances)) {
          data.data.me.account.vaultBalances = Vault.merge(
            data.data.me.account.vaultBalances,
            State.vaultBalances
          );

          State.vaultBalances = Vault.sanitize(data.data.me.account.vaultBalances);
          Storage.save(CONFIG.STORAGE_KEYS.vaultBalances, State.vaultBalances);
        } else if (State.vaultBalances.length > 0) {
          data.data.me.account.vaultBalances = State.vaultBalances;
        }
      }

      return new Response(JSON.stringify(data), {
        status: response.status,
        statusText: response.statusText,
        headers: response.headers,
      });
    },

    handleGetBetsResponse(response, text) {
      try {
        const serverData = JSON.parse(text);
        const requestBody = State.currentGame.getBetsRequest || {};
        const variables = requestBody.variables || {};

        const first = variables.first || 10;
        const cursor = variables.cursor || null;
        const currencyIn = variables.currencyIn || null;

        const fakeBets = BetHistory.getBets(first, cursor, currencyIn);

        const betsKey = serverData?.data?.myBets ? 'myBets' : 'bets';

        const modifiedData = {
          data: {
            [betsKey]: fakeBets
          }
        };

        return new Response(JSON.stringify(modifiedData), {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers,
        });
      } catch (e) {
        return response;
      }
    },

    handleGetBetInfo(response, text, fakeBet) {
      try {
        const data = JSON.parse(text);

        if (!data?.data?.bet) {
          return response;
        }

        const serverBet = data.data.bet;

        serverBet.amount = fakeBet.amount;
        serverBet.originalAmount = fakeBet.amount;
        serverBet.payout = fakeBet.payout;
        serverBet.account.user.username = State.profile.username
        serverBet.account.user.vipLevel = State.profile.vipLevel

        if (fakeBet.currency !== serverBet.currency) {
          serverBet.currency = fakeBet.currency;
        }

        return new Response(JSON.stringify(data), {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers,
        });
      } catch (e) {
        return response;
      }
    },

    handleActiveBetResponse(response, text) {
      const data = JSON.parse(text);

      const activeBetKey = Object.keys(data.data || {}).find(k =>
        k.includes('ActiveBet')
      );

      if (!activeBetKey) return response;

      const bet = data.data[activeBetKey];
      if (!bet) return response;

      const currency = State.currentGame.currency;
      const amount = State.currentGame.amount;

      if (amount) {
        bet.amount = String(amount);
      }

      if (currency) {
        bet.currency = currency;
      }

      return new Response(JSON.stringify(data), {
        status: response.status,
        statusText: response.statusText,
        headers: response.headers,
      });
    },

    handleGetNotificationsResponse(response, text) {
      try {
        const serverData = JSON.parse(text);
        const requestBody = State.currentGame.getNotificationsRequest || {};
        const variables = requestBody.variables || {};

        let first = variables.first || 25;
        let cursor = variables.cursor || null;

        const fakeNotifications = NotificationHistory.getNotifications(first, cursor);

        const modifiedData = {
          data: {
            myNotifications: fakeNotifications
          }
        };

        return new Response(JSON.stringify(modifiedData), {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers,
        });
      } catch (e) {
        return response;
      }
    },

    handleUnseenNotificationsCount(response, text) {
      try {
        const data = JSON.parse(text);

        if (data?.data && 'unseenNotificationsCount' in data.data) {
          const unseenCount = State.notificationHistory.filter(n => !n.seenAt && !n.readAt).length;

          data.data.unseenNotificationsCount = unseenCount;

          return new Response(JSON.stringify(data), {
            status: response.status,
            statusText: response.statusText,
            headers: response.headers,
          });
        }
      } catch (e) {
      }

      return response;
    },

    handleGetDepositsResponse(response, text) {
      try {
        const serverData = JSON.parse(text);
        const requestBody = State.currentGame.getDepositsRequest || {};
        const variables = requestBody.variables || {};

        const first = variables.first || 10;
        const cursor = variables.cursor || null;
        const currencyIn = variables.currencyIn || null;

        const fakeDeposits = DepositHistory.getDeposits(first, cursor, currencyIn);

        const modifiedData = {
          data: {
            deposits: fakeDeposits
          }
        };

        return new Response(JSON.stringify(modifiedData), {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers,
        });
      } catch (e) {
        return response;
      }
    },

    handleGameResult(response, text) {
      const data = JSON.parse(text);

      const actionKey = Object.keys(data.data || {}).find(k =>
        k.includes('Start') || k.includes('Play') || k.includes('Next') || k.includes('Cashout') || k.includes('Auto')
      );

      if (!actionKey) return response;

      const bet = data.data[actionKey];
      if (!bet) return response;

      const currency = State.currentGame.currency;
      const wager = Number(State.currentGame.amount ?? 0);

      bet.currency = currency;
      bet.amount = String(State.currentGame.amount);

      const updateBalance = (newBalance) => {
        if (currency && newBalance != null) {
          Balance.set(currency, newBalance);
          bet.afterBalance = String(newBalance);
        }
      };

      let newBalance = null;
      let outcome = null;

      const bjOps = ["blackjackStart", "blackjackNext", "blackjackActiveBet"];
      if (bjOps.includes(actionKey)) {
        const actions = bet.shuffleOriginalActions;

        if (!wager || wager <= 0) {
          return new Response(JSON.stringify(data), {
            status: response.status,
            statusText: response.statusText,
            headers: response.headers,
          });
        }

        bet.amount = String(State.currentGame.amount);
        bet.originalAmount = String(State.currentGame.amount);

        if (actions && Array.isArray(actions)) {
          actions.forEach(item => {
            if (item?.action?.blackjack) {
              item.action.blackjack.originalMainBetAmount = String(State.currentGame.amount);
            }
          });
        }

        const lastAction = actions && actions[actions.length - 1];
        outcome = lastAction?.action?.blackjack?.mainHandOutcome;

        if (actionKey === "blackjackStart") {
          const base = Balance.get(currency);
          newBalance = base - wager;
        }

        if (outcome && outcome !== "PENDING" && outcome !== "NONE") {
          const currentBal = Balance.get(currency);

          if (outcome === "LOSS") {
            bet.payout = String(0);
            newBalance = currentBal;
          } else if (outcome === "WIN" || outcome === "BLACKJACK") {
            const multi = Number(bet.multiplier) || 2;
            const payout = wager * multi;
            bet.payout = String(payout);
            newBalance = currentBal + payout;
          } else if (outcome === "PUSH") {
            bet.payout = String(wager);
            newBalance = currentBal + wager;
          }
        }
      } else if (actionKey === "towerNext") {
        const lastAction = bet.shuffleOriginalActions?.[bet.shuffleOriginalActions.length - 1];
        const towerData = lastAction?.action?.tower;

        if (towerData?.towerResult && towerData.towerResult.length > 0) {
          const winMultiplier = Number(towerData.winMultiplier) || 0;
          const currentBal = Balance.get(currency);

          if (winMultiplier > 0) {
            const payout = wager * winMultiplier;
            bet.payout = String(payout);
            bet.multiplier = winMultiplier;
            newBalance = currentBal + payout;

          } else {
            bet.payout = "0";
            bet.multiplier = 0;
            newBalance = currentBal;

          }
        }
      } else if (actionKey === "minesNext") {
        const lastAction = bet.shuffleOriginalActions?.[bet.shuffleOriginalActions.length - 1];
        const minesData = lastAction?.action?.mines;

        if (minesData?.minesResult && minesData.minesResult.length > 0) {
          bet.payout = "0";
          bet.multiplier = 0;
          newBalance = Balance.get(currency);

        }
      }
        else if (actionKey === "limboPlay") {
        newBalance = GameLogic.handlers.limbo(bet, wager, currency);
      } else if (actionKey === "dicePlay") {
        newBalance = GameLogic.handlers.dice(bet, wager, currency);
      } else if (actionKey === "kenoPlay") {
        newBalance = GameLogic.handlers.keno(bet, wager, currency);
      } else if (actionKey === "wheelPlay") {
        newBalance = GameLogic.handlers.wheel(bet, wager, currency);
      } else if (["minesCashout", "towerCashout", "chickenCashout"].includes(actionKey)) {
        newBalance = GameLogic.handlers.cashout(bet, wager, currency);
      } else if (["plinkoPlay", "minesAuto"].includes(actionKey)) {
        newBalance = GameLogic.handlers.instant(bet, wager, currency);
      } else if (["minesStart", "towerStart", "chickenStart"].includes(actionKey)) {
        newBalance = GameLogic.handlers.start(bet, wager, currency);
      }

      updateBalance(newBalance);
      console.log("is chikn", actionKey === "chickenCashout")
      const shouldSaveBet =
        actionKey === "plinkoPlay" ||
        actionKey === "minesAuto" ||
        actionKey === "dicePlay" ||
        actionKey === "limboPlay" ||
        actionKey === "kenoPlay" ||
        actionKey === "wheelPlay" ||
        actionKey === "minesCashout" ||
        actionKey === "towerCashout" ||
        (bjOps.includes(actionKey) && outcome && outcome !== "PENDING" && outcome !== "NONE") ||
        (actionKey === "towerNext" &&
         bet.shuffleOriginalActions?.[bet.shuffleOriginalActions.length - 1]?.action?.tower?.towerResult?.length > 0) ||
        (actionKey === "minesNext" &&
         bet.shuffleOriginalActions?.[bet.shuffleOriginalActions.length - 1]?.action?.mines?.minesResult?.length > 0) ||
        (actionKey === "chickenCashout" &&
         bet.shuffleOriginalActions?.[bet.shuffleOriginalActions.length - 1]?.action?.chicken?.chickenResult?.length > 0);

      if (shouldSaveBet) {
        console.log(shouldSaveBet)
        const gameInfo = BetHistory.getCurrentGameInfo();
        console.log("test", gameInfo)
        if (gameInfo) {
          console.log(bet)
          if (bet.payout === undefined || bet.payout === null) {
            bet.payout = "0";
          }

          const wagerAmount = Number(bet.amount) || wager || 0;
          const payoutAmount = Number(bet.payout) || 0;

          let calculatedMultiplier = 0;
          if (wagerAmount > 0 && payoutAmount > 0) {
            calculatedMultiplier = payoutAmount / wagerAmount;
          }

          const finalMultiplier = (bet.multiplier && bet.multiplier > 0)
            ? bet.multiplier
            : calculatedMultiplier;

          BetHistory.addBet({
            id: bet.id,
            currency: currency,
            amount: bet.amount,
            payout: payoutAmount > 0 ? bet.payout : 0,
            multiplier: finalMultiplier,
            game: {
              id: gameInfo.id,
              name: gameInfo.name,
              gameAndGameCategories: gameInfo.categories,
              slug: gameInfo.slug,
              __typename: 'Game'
            }
          });
        }
      }

      return new Response(JSON.stringify(data), {
        status: response.status,
        statusText: response.statusText,
        headers: response.headers,
      });
    }
  }

  if (!localStorage.getItem('balances')) {
    localStorage.setItem('balances', JSON.stringify([]));
  }
  if (!localStorage.getItem('vault_balances')) {
    localStorage.setItem('vault_balances', JSON.stringify([]));
  }
  if (!localStorage.getItem('profile')) {
    localStorage.setItem('profile', JSON.stringify(CONFIG.DEFAULT_PROFILE));
  }
  if (!localStorage.getItem('bet_history')) {
    localStorage.setItem('bet_history', JSON.stringify([]));
  }
  if (!localStorage.getItem('notification_history')) {
    localStorage.setItem('notification_history', JSON.stringify([]));
  }
  if (!localStorage.getItem('deposit_history')) {
    localStorage.setItem('deposit_history', JSON.stringify([]));
  }

  State.init();
  Network.intercept();
  DepositSimulator.init();

  __interceptorReady = true;

  window.DepositSimulator = DepositSimulator;
  window.Network = Network;

  window.restartInterception = () => {
    window.fetch = __stubbedFetch;
    window.fetch.__larpIntercepted = true;
  };

  window.testDeposit = (currency = 'SOL', amount = 1) => {
    DepositSimulator.handleDeposit(currency, amount);
  };

  window.checkState = () => {
    return State;
  };

  const ChatCommands = {
    init() {
      this.hookChatInput();
    },

    hookChatInput() {
      const attachIfNeeded = () => {
        const input = document.querySelector('.Input_root__lWEbp');
        if (input && !input.dataset.larpHooked) {
          input.dataset.larpHooked = 'true';
          this.attachListener(input);
        }
      };

      // Check immediately
      attachIfNeeded();

      // Keep checking periodically
      setInterval(attachIfNeeded, 1000);
    },

    attachListener(input) {
      input.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' && input.value.startsWith('/')) {
          e.preventDefault();
          e.stopPropagation();

          const command = input.value.trim();
          this.handleCommand(command);
          input.value = '';
        }
      });
    },

    handleCommand(command) {
      const parts = command.split(/\s+/);
      const cmd = parts[0].toLowerCase();
      const args = parts.slice(1);

      switch(cmd) {
        case '/clearbets':
          State.betHistory = [];
          Storage.save(CONFIG.STORAGE_KEYS.betHistory, []);
          console.log('[LARP] Bet history cleared');
          break;

        case '/clearnoti':
          State.notificationHistory = [];
          Storage.save(CONFIG.STORAGE_KEYS.notificationHistory, []);
          NotificationHistory.notifications = [];
          console.log('[LARP] Notification history cleared');
          break;

        case '/cleardepo':
          State.depositHistory = [];
          Storage.save(CONFIG.STORAGE_KEYS.depositHistory, []);
          DepositHistory.deposits = [];
          console.log('[LARP] Deposit history cleared');
          break;

        case '/depo':
          if (args.length < 2) {
            console.error('[LARP] Usage: /depo <CURRENCY> <AMOUNT>');
            console.error('[LARP] Example: /depo SOL 10');
            break;
          }
          const currency = args[0].toUpperCase();
          const amount = parseFloat(args[1]);

          if (isNaN(amount) || amount <= 0) {
            console.error('[LARP] Invalid amount:', args[1]);
            break;
          }

          if (!CONFIG.CURRENCY_TO_CHAIN[currency]) {
            console.error('[LARP] Unknown currency:', currency);
            console.error('[LARP] Available currencies:', Object.keys(CONFIG.CURRENCY_TO_CHAIN).join(', '));
            break;
          }

          console.log(`[LARP] Depositing ${amount} ${currency}...`);
          DepositSimulator.handleDeposit(currency, amount);
          break;

        case '/balance':
          if (args.length === 0) {
            console.log('[LARP] Current balances:');
            State.balances.forEach(b => {
              console.log(`  ${b.currency}: ${b.amount}`);
            });
          } else {
            const curr = args[0].toUpperCase();
            const bal = Balance.get(curr);
            console.log(`[LARP] ${curr}: ${bal}`);
          }
          break;

        case '/setbalance':
          if (args.length < 2) {
            console.error('[LARP] Usage: /setbalance <CURRENCY> <AMOUNT>');
            break;
          }
          const setCurr = args[0].toUpperCase();
          const setAmt = parseFloat(args[1]);

          if (isNaN(setAmt) || setAmt < 0) {
            console.error('[LARP] Invalid amount:', args[1]);
            break;
          }

          Balance.set(setCurr, setAmt);
          console.log(`[LARP] Set ${setCurr} balance to ${setAmt}`);
          break;
 case '/reset':
  localStorage.removeItem('profile');
  localStorage.removeItem('balances');
  localStorage.removeItem('vault_balances');
  localStorage.removeItem('bet_history');
  localStorage.removeItem('notification_history');
  localStorage.removeItem('deposit_history');
  console.log('[LARP] Full reset done. Reloading...');
  setTimeout(() => location.reload(), 500);
  break;


        case '/help':
          console.log('[LARP] Available commands:');
          console.log('  /clearbets - Clear bet history');
          console.log('  /clearnoti - Clear notification history');
          console.log('  /cleardepo - Clear deposit history');
          console.log('  /depo <CURRENCY> <AMOUNT> - Simulate deposit');
          console.log('  /balance [CURRENCY] - Show balance(s)');
          console.log('  /setbalance <CURRENCY> <AMOUNT> - Set balance directly');
          console.log('  /help - Show this help message');
          break;

        default:
          console.error('[LARP] Unknown command:', cmd);
          console.log('[LARP] Type /help for available commands');
      }
    }
  };

  ChatCommands.init();

})();