diff --git a/RUNEWAGER_FUNCTIONALITY_MAP.md b/RUNEWAGER_FUNCTIONALITY_MAP.md index e197dad..e1e05f3 100644 --- a/RUNEWAGER_FUNCTIONALITY_MAP.md +++ b/RUNEWAGER_FUNCTIONALITY_MAP.md @@ -47,6 +47,7 @@ Navigation is driven by inline menus plus command aliases. Persistent user/admin - Menus use callback “back” paths (`to_main_menu`, `open_admin_dashboard`, `pamenu_back_*`). - Many menu-style responses use `replaceCallbackPanel(...)` to avoid stale stacked cards. - Single active menu rule: `clearOldMenus(ctx)` + menu send helpers keep only one active transient menu card per user/chat. +- `to_main_menu` clears transient menu cards via `clearOldMenus(...)` before rendering persistent menu headers. ## 4. User Menu Tree @@ -57,6 +58,7 @@ Navigation is driven by inline menus plus command aliases. Persistent user/admin ### Key user branches - Referral system: Main Menu exposes **👥 Refer a Friend** submenu (`Your Referral Code`, `Share Your Code`, `How Referral Boosts Work`, Back/Cancel). - Play-link routing is deterministic: browser mode always returns external `https://gamble-codez.com/...`; telegram mode always returns `startapp` mini-app deep links. +- Persistent user menu play button is settings-aware (`Play Mode`) and uses browser label/url when browser mode is enabled. - Discord links are browser-only via `getDiscordLink(...)` sanitation (no WebAppInfo/startapp wrappers). - **Profile & status**: `/profile`, quick status callbacks. - **Promo**: `menu_claim_bonus` → filtered eligible promos only. diff --git a/index.js b/index.js index c9314d7..41cd0b2 100644 --- a/index.js +++ b/index.js @@ -3755,9 +3755,11 @@ async function sendMainMenu(ctx, user, page = 1) { // ── Persistent Menu System ────────────────────────────────────────────────── /** Keyboard for the new persistent USER MAIN MENU */ -function userMainMenuKeyboard(isAdminUser) { +function userMainMenuKeyboard(isAdminUser, user = null) { + const playUrl = getPlayLink(user, 'play'); + const browserMode = user && user.settings && user.settings.playMode === 'browser'; const rows = [ - [Markup.button.url('🎮 Play Runewager', LINKS.miniAppPlay)], + [Markup.button.url(browserMode ? '🌐 Play Runewager (Browser)' : '🎮 Play Runewager', playUrl)], [ Markup.button.callback('🎁 Bonuses & Rewards', 'pmenu_claim_bonus'), Markup.button.callback('👤 Profile & Link', 'pmenu_my_profile'), @@ -3782,7 +3784,7 @@ function userMainMenuText(user) { `👋 Hey ${name}! Here's your Runewager menu.\n\n` + (statusLine ? `${statusLine}\n\n` : '') + `🌍 *Runewager is 100% FREE to play — worldwide access, no deposits.*\n\n` - + `🎮 *Play Runewager* — Open the Runewager Mini App\n` + + `🎮 *Play Runewager* — Opens Browser or Mini App based on your Play Mode setting\n` + `🎁 *Bonuses & Rewards* — Promo menu + 30 SC wager bonus\n` + `👤 *Profile & Link* — Linked account, XP, badges & referrals\n` + `🏆 *Giveaways & Community* — Active SC giveaways + join links\n` @@ -3799,6 +3801,7 @@ function userMainMenuText(user) { async function sendPersistentUserMenu(ctx, user) { const chatId = ctx.chat ? ctx.chat.id : (ctx.callbackQuery && ctx.callbackQuery.message ? ctx.callbackQuery.message.chat.id : null); if (!chatId) return; + await clearOldMenus(ctx, user); await deleteEphemeralBonusPrompt(ctx, user); // Show admin button only when admin view is ON const isAdminUser = ADMIN_IDS.includes(Number(user.id)) && user.adminModeOn; @@ -3822,7 +3825,7 @@ async function sendPersistentUserMenu(ctx, user) { } const text = userMainMenuText(user); - const keyboard = userMainMenuKeyboard(isAdminUser); + const keyboard = userMainMenuKeyboard(isAdminUser, user); const sent = await ctx.telegram.sendMessage(chatId, text, { parse_mode: 'Markdown', ...keyboard }); user.mainMenuMsgId = sent.message_id; user.mainMenuChatId = sent.chat.id; @@ -6831,6 +6834,7 @@ bot.command('join', async (ctx) => { bot.action('to_main_menu', async (ctx) => { const user = getUser(ctx); await ctx.answerCbQuery(); + await clearOldMenus(ctx, user); await deleteEphemeralBonusPrompt(ctx, user); await sendPersistentUserMenu(ctx, user); }); diff --git a/test/smoke.test.js b/test/smoke.test.js index 4e1f65d..f68b91e 100644 --- a/test/smoke.test.js +++ b/test/smoke.test.js @@ -541,3 +541,22 @@ test('testall covers required diagnostic category labels', () => { assert.ok(source.includes(cat), `Expected TestAll category/check mention: ${cat}`); } }); + + +test('to_main_menu clears old menus before rendering persistent menu', () => { + const source = fs.readFileSync(path.resolve(__dirname, '..', 'index.js'), 'utf8'); + const blockStart = source.indexOf("bot.action('to_main_menu'"); + assert.ok(blockStart >= 0, 'Expected to_main_menu callback'); + const block = source.slice(blockStart, blockStart + 400); + assert.ok(block.includes('await clearOldMenus(ctx, user);'), 'Expected clearOldMenus in to_main_menu'); + assert.ok(block.includes('await sendPersistentUserMenu(ctx, user);'), 'Expected sendPersistentUserMenu in to_main_menu'); +}); + +test('persistent user menu play button uses settings-aware getPlayLink', () => { + const source = fs.readFileSync(path.resolve(__dirname, '..', 'index.js'), 'utf8'); + const start = source.indexOf('function userMainMenuKeyboard('); + assert.ok(start >= 0, 'Expected userMainMenuKeyboard'); + const block = source.slice(start, start + 500); + assert.ok(block.includes("getPlayLink(user, 'play')"), 'Expected settings-aware play link in persistent menu'); + assert.ok(block.includes('Play Runewager (Browser)'), 'Expected browser-mode label for persistent play button'); +});