Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RUNEWAGER_FUNCTIONALITY_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand Down
12 changes: 8 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -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`
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
});
Expand Down
19 changes: 19 additions & 0 deletions test/smoke.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});