Skip to content

Claude/plan v3 deployment vp mpw - #112

Merged
gamblecodezcom merged 8 commits into
mainfrom
claude/plan-v3-deployment-VPMpw
Feb 28, 2026
Merged

Claude/plan v3 deployment vp mpw#112
gamblecodezcom merged 8 commits into
mainfrom
claude/plan-v3-deployment-VPMpw

Conversation

@gamblecodezcom

@gamblecodezcom gamblecodezcom commented Feb 28, 2026

Copy link
Copy Markdown
Owner

User description

Summary by Sourcery

Upgrade Telegram bot to v3.x with safer link handling, hardened admin/dev tooling, richer giveaway and onboarding UX, and a rebranded, script-backed Helpful Tooltips system.

New Features:

  • Introduce group command guard middleware that redirects most group commands to DM with a deep-link button while allowing a small set of group-aware commands to run in chats.
  • Add onboarding progress indicators, a one-time completion card, and a dedicated referral panel with shareable deep links for improved user guidance.
  • Provide paginated giveaway views for users and admins, including auto-vanish menus, richer admin health and stats panels, and broadcast preview flows before sending.
  • Create generate_tooltips and add_tooltip scripts plus deployment hooks to auto-generate and maintain tooltip data from source definitions.

Bug Fixes:

  • Harden SSH console and admin shell commands against injection by validating input and switching sensitive operations from exec to execFile.
  • Fix giveaway winner selection to avoid infinite loops by shrinking the weighted pool and centralizing participant weighting logic.
  • Correct pending-action timeout handling to avoid mutating timestamps, handle invalid createdAt values, and track expirations via metrics.
  • Ensure menu/help panels and admin mode toggles no longer stack or double-refresh by routing through shared menu helpers and clearing stale menu IDs on startup.
  • Restore legacy play mode fallback for users with older schema and avoid returning unsafe or misconfigured URLs in Telegram, Discord, and mini-app links.

Enhancements:

  • Rebrand and expand the Content Drops system into a Helpful Tooltips manager with inline button syntax, richer settings, group linking tools, and improved admin copy throughout.
  • Improve Helpful Tooltips delivery with parsed inline keyboards, configurable intervals, silent mode, better target linking UX, and additional diagnostics in the test suite.
  • Enhance giveaways with scheduled announcement refreshes, countdown reminders, HTML-formatted results, detailed admin DM reports, and safer preflight checks for group permissions.
  • Refine ephemeral promo prompts and onboarding flows with progress bars, auto-deleting messages, updated schemas, and centralized eligibility checks.
  • Expand runtime metrics with new counters for menu stale recoveries, pending action timeouts, and uptime, and add more informative admin health panels.
  • Strengthen smoke tests around regex catch-alls and command extraction while making the test file walker resilient to filesystem errors.

Build:

  • Add tooltip generation to deploy and prod-run scripts, including directory/permission setup and admin notifications on failure.

Deployment:

  • Tighten filesystem permissions for data, backup, log, and env files during deployment and prod runs to improve runtime security.

Documentation:

  • Update functionality map, CLAUDE guidance, and todo list to reflect v3.0/v3.1 behavior, including group command guarding, tooltip rebrand, and new admin tooling.
  • Introduce a CHANGELOG documenting releases from v2.0.0 through v3.0.0 with high-level changes and rationale.

Tests:

  • Extend smoke tests to better detect and ignore catch-all regex handlers, support more declaration styles when extracting commands, and tolerate directory read errors during file discovery.

Chores:

  • Bump package version to 3.0.0 and add lightweight pre-deploy checks that assert presence of critical menu helper functions in the main bot file.

Summary by CodeRabbit

  • New Features

    • Group command guard middleware redirects group chat commands to DM with specific passthrough commands (link, giveaway, admin, etc.)
    • Added onboarding progress bar visualization and completion card display
    • Added Group Linking tools to Admin System Tools
  • Tests

    • Enhanced command detection to recognize let/var declarations alongside const declarations
  • Chores

    • Improved shell script robustness for tooltip text handling

CodeAnt-AI Description

Redirect group commands to DM; add onboarding progress + one-time completion; safe tooltip import and script fixes

What Changed

  • Commands sent in group/supergroup chats are intercepted and redirected to a DM deep-link unless the command is explicitly allowed in groups (link, giveaway, start_giveaway, admin); suppressed group commands no longer run their handlers.
  • Onboarding now shows a short visual progress bar before each step (auto-deletes after ~8s) and displays a one-time completion card the first time a user reaches the main menu after finishing onboarding.
  • Admin UI: added "🔗 Group Linking" to System Tools with a handler to open group-linking tools from the admin panel.
  • Helpful Tooltips (tips) supports batch JSON import via a new admin flow that validates a pasted JSON array and reports the created tooltip IDs.
  • Deployment and helper scripts hardened: tooltip generation and add-tooltip scripts no longer expose tooltip text to shell parsing and use environment-passed values to avoid injection or command-substitution pollution.
  • Tests and detection logic improved: catch-all regex detection and command-name extraction tests expanded to cover multiline regex forms and let/var/no-semicolon command declarations.
  • Removed duplicate/obsolete giveaway/handler definitions and fixed related dead code; test suite now passes after audit fixes.

Impact

✅ Fewer accidental group-command executions
✅ Clearer onboarding progress and a single onboarding completion notice
✅ Safer tooltip import and deployment scripts (reduced shell-injection risk)

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…urity, 23 Block 1 fixes

Menu lifecycle overhaul:
- replyMenu() TTL auto-vanish via ttlMs parameter
- clearStaleMenuIds() on startup (24h threshold)
- mainMenuSentAt / adminMenuSentAt timestamps on user schema
- sendHelpMenu() fixed: bare ctx.reply() → replyMenu() (panel stacking bug)
- replaceCallbackPanel() fallback now tracks message ID in user state
- Admin mode toggle double-fire fixed

Feature upgrades:
- User giveaway list: sendUserGiveawaysPage() 5/page + 2-min auto-vanish
- Admin giveaway panel: activeGiveawaysKeyboard(page) 5/page pagination
- pmenu_referral sub-menu with share deep-link button
- Admin stats: active window indicator + Refresh button
- Admin health panel: inline memory/errors/users/persist-age/uptime
- Broadcast builder: Preview button sends to admin DM before mass send

Block 1 security & logic fixes:
- getStartAppLink(): encodeURIComponent + regex whitelist
- referralShareHTML(): escapeHtml() on code param
- unwrapTelegramUrl(): safe fallback, scheme whitelist, www.t.me support
- getDiscordLink(): null when not configured (no hardcoded fallback)
- evaluatePendingActionTimeout(): >= boundary, NaN guard, no createdAt mutation
- getPlayLink(): legacy user.playMode fallback restored
- Weighted winner pool: splice-after-pick guarantees termination
- deploy_status + logs: exec() → execFile() (no shell spawn)
- SSHV: rejects null bytes, backticks, $( and ${ before exec
- Startup env warnings: ADMIN_IDS, TELEGRAM_CHANNEL_ID, TELEGRAM_GROUP_ID

Centralized helpers: computeParticipantWeight(), getRealGiveaways(), isNewUserPromoEligible()

Metrics: added runewager_menu_stale_recoveries, pending_actions_timed_out, uptime_seconds

load_tooltips.sh: full rewrite — atomic write, --push/--pull flags, parameterized REPO_DIR,
  HTML-safe tooltips, --dry-run mode, permission checks, guarded git ops

Tests: readdirSync wrapped in try/catch; isCatchAllRegexPattern expanded;
  extractCommandHandlerNames supports let/var/no-semicolon

Infrastructure: pre-deploy-checks gate 3b (menu symbols), CHANGELOG.md created,
  package.json bumped to 3.0.0

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
… during PR merges

Restores 12 categories of codex changes that were incorrectly overwritten by
"current" (main) when resolving merge conflicts across 6 merge commits.

Security / TLS hardening (index.js):
- Restore resolveTlsCertPathIfAllowed(): multi-directory fallback for TLS certs
  (PROJECT_DIR, certs/, /etc/ssl, /etc/letsencrypt) — was collapsed to PROJECT_DIR only
- Restore isHealthTlsEnabled() to use resolveTlsCertPathIfAllowed()
- Restore requestHealthPayload(): centralized HTTP/HTTPS health fetcher with
  dynamic protocol detection — was inlined with hardcoded https in two places
- Update /health command to use requestHealthPayload() (shows protocol label)
- Update pamenu_tools_health to use requestHealthPayload()
- Update startHealthServer() TLS cert read to use resolveTlsCertPathIfAllowed()

Command injection protection (index.js):
- commandNeedsConfirmation(): restore pipe-to-bash/zsh detection,
  destructive-cmd-piped pattern, redirect pattern — main simplified to only catch "sh"
- commandBlocked(): restore explicit pipe-to-shell blocker pattern

Race condition fixes (index.js):
- deleteEphemeralBonusPrompt(): restore runUserMutation guards for safe
  read-delete-write of ephemeralBonusMsgId/ChatId
- sendEphemeralBonusPrompt(): restore guards (claimedPromo check, active promo lookup),
  per-user dynamic promo lookup via getActivePromoCodeForUser() (was hardcoded
  promoStore.code), "I Have Claimed" callback button, mutation-safe writes
- Add promo_confirm_claimed_next callback handler (button was added, handler missing)

Deploy/runtime hardening (deploy.sh, prod-run.sh, runewager.service):
- deploy.sh: restore data/backups/ mkdir, chown -R APP_NAME, chmod 0750/0640/0600
  permission hardening after npm install
- prod-run.sh: restore chmod 0750 for data/data/backups/logs dirs,
  chmod 0640 for log+session files, chown -R to service user after dir creation
- runewager.service: add UMask=0077 (prevents world-readable files from service)

.gitignore:
- Restore legacy root-level data file patterns: users.json, giveaways.json,
  promo.json, env.json, analytics*.json (pre-data/ directory structure)

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
- TDZ fix: move pendingActionsTimedOut/menuStaleRecoveries declarations
  before evaluatePendingActionTimeout to eliminate temporal dead-zone risk
- Timeout boundary: revert < to <= so age exactly equal to 15m is NOT
  expired — aligns with /testall check and unit test expectations
- /logs line count: clamp to [1, 200] (adds Math.max(1,...) lower bound
  to reject negative/zero values from user input)
- Health panel: fix _errorRate.windowErrors → _errorRate.count (field
  did not exist; .count is the correct field on _errorRate object)
- /logs fallback: add execFile('tail') fallback when journalctl errors
  with no output (non-systemd systems); uses BOT_LOG_FILE env or default
- executeSshvCommand: fix comment — said "spawn" but code uses exec();
  updated to accurately describe exec with shell:true (admin-only)
- load_tooltips.sh: remove || true that defeated diff --cached check,
  causing .gitignore changes to never be committed on --push

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
## Helpful Tooltips (was Content Drops)
- Rename all UI strings: Content Drops → Helpful Tooltips throughout
- New settings panel: interval, silent mode, Link Channel/Group
- Target group linking via forwarded message (saves name + ID)
- Dashboard footer shows real group name + ID
- "Show all Helpful Tooltips (N)" button with dynamic count
- Inline button builder: [Label - URL] && [Label2 - URL2] syntax
  - Multiple buttons per row with &&, new line = new row
  - [Open Bot] shorthand for standard Open Bot button
  - Full URL + label validation before save
- postTipToConfiguredTarget uses silentMode flag + parsed buttons
- tipsStore extended with targetGroupTitle and silentMode fields

## Giveaway v3.0+
- Extracted buildGiveawayAnnouncementText + buildGiveawayAnnouncementKeyboard helpers
- scheduleGiveawayRefresh: auto-refresh at 25%, 50%, 75% of duration + re-pin
- scheduleGiveawayReminders overhauled: 10m, 5m, 1min, 30sec, 10→1 countdown
- HTML results format: @handle, SC WON, (2x boost applied), DM tip
- Full admin winner report per winner: TG handle, display name, RW username, prize, boost
- "View Results in Group" deep-link button in admin report
- Winner DMs include SC amount, boost status, RW username
- DM failure tracking with summary count
- giveawayPreflightCheck: validates group linked, warns on missing pin permission
- gwizStart calls preflight before wizard begins

## Scripts
- generate_tooltips.sh: extracts DEFAULT_TIPS_LIST from index.js → data/tooltips.json (atomic, idempotent)
- add_tooltip.sh: appends placeholder tooltip entry, outputs new ID
- deploy.sh: step 3b auto-runs generate_tooltips.sh before service start
- prod-run.sh: step 6b auto-runs generate_tooltips.sh before bot launch

## /testall
- Added Helpful Tooltips System checks (tipsStore shape, count, interval, target, parser)
- Added Giveaway Extended checks (helpers defined, preflight defined)

## Docs
- RUNEWAGER_FUNCTIONALITY_MAP.md: full v3.0+ sync with flowcharts

All 60 tests pass. node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
… linking

- Group command guard middleware: bot.use() intercepts all commands in
  group/supergroup chats and redirects to DM with a deep-link button.
  Passthrough commands with own group logic: link, linkrunewager, giveaway,
  start_giveaway, admin. Suppresses handler execution for all others.

- Onboarding progress bar: onboardingProgressBar(step) renders ●●○○○
  Step N of 5 — Label. showOnboardingPrompt() prepends a Markdown progress
  header (auto-deletes after 8s) before each step-specific prompt.

- Onboarding completion card: shown once (user.onboarding.completionCardShown
  flag) when user reaches the main menu after completing all 5 steps.
  Includes feature summary and Open Menu button.

- Admin System Tools: added 🔗 Group Linking button to
  adminSystemToolsKeyboard() with admin_sys_group_linking action handler
  (renders group linking panel with back-to-system-tools navigation).

- Schema: completionCardShown added to onboarding default + migration guard.
- Map: RUNEWAGER_FUNCTIONALITY_MAP.md fully updated; todolist.md updated.
- All 60 tests pass, node --check clean.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
@codeant-ai

codeant-ai Bot commented Feb 28, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@sourcery-ai

sourcery-ai Bot commented Feb 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors and hardens the Runewager Telegram bot around menu lifecycle, security, giveaways, helpful tooltips, onboarding UX, and deployment scripts, while adding metrics and documentation updates for a v3.x production rollout.

Sequence diagram for group command guard middleware in Telegram groups

sequenceDiagram
  actor Admin
  participant Telegram
  participant TelegrafBot as Telegraf_bot
  participant GroupGuard as Group_command_guard_middleware
  participant CommandHandler as Command_handler

  Admin->>Telegram: Send /menu in group chat
  Telegram->>TelegrafBot: Update (message.text = "/menu", chat.type = group)

  TelegrafBot->>GroupGuard: ctx (group message with /menu)
  GroupGuard->>GroupGuard: Inspect chat.type and message.text
  GroupGuard->>GroupGuard: Extract rawCmd = "menu"
  GroupGuard-->>TelegrafBot: Do not call next() (command not in GROUP_PASSTHROUGH_COMMANDS)

  GroupGuard->>GroupGuard: Build DM deep link using ctx.botInfo.username
  GroupGuard->>Telegram: reply "This command works in DM" + inline Open_DM button
  Telegram-->>Admin: DM redirect message in group

  note over CommandHandler: Command handler for /menu is never invoked

  Admin->>Telegram: Tap Open DM button
  Telegram->>TelegrafBot: Start DM chat with /start or /menu
  TelegrafBot->>GroupGuard: ctx (private chat)
  GroupGuard-->>TelegrafBot: next() (chat.type != group)
  TelegrafBot->>CommandHandler: Handle /menu in DM
  CommandHandler->>Telegram: Send persistent user menu
Loading

Sequence diagram for v3 giveaway lifecycle with preflight, refresh, and finalization

sequenceDiagram
  actor Admin
  participant Telegram
  participant Bot as Telegraf_bot
  participant GwWizard as gwizStart
  participant GwPreflight as giveawayPreflightCheck
  participant Giveaway as Giveaway_object
  participant Scheduler as Timers_refresh_and_reminders

  Admin->>Telegram: Tap "Start Giveaway" (pamenu_start_giveaway)
  Telegram->>Bot: CallbackQuery pamenu_start_giveaway
  Bot->>GwWizard: gwizStart(ctx, user, config)

  GwWizard->>GwPreflight: giveawayPreflightCheck(ctx, targetChatId)
  GwPreflight->>Bot: getChatMember(chatId, botId)
  Bot-->>GwPreflight: ChatMember (permissions)
  GwPreflight-->>GwWizard: { ok: true } or { ok: false, reason }

  alt Preflight failed
    GwWizard->>Telegram: reply "Cannot start giveaway: reason"
    GwWizard-->>Bot: return
  else Preflight ok
    GwWizard->>GwWizard: Collect wizard steps (config)
    GwWizard->>Giveaway: createGiveaway(config)
    GwWizard->>Bot: announceGiveaway(giveaway, botUsername)

    Bot->>Telegram: sendMessage(giveaway.chatId, initial announcement)
    Telegram-->>Bot: Message (announceMsgId)
    Bot->>Giveaway: Store announceMsgId, pinnedMsgId
    Bot->>Telegram: pinChatMessage(chatId, announceMsgId)

    Bot->>Scheduler: scheduleGiveawayRefresh(giveaway)
    Bot->>Scheduler: scheduleGiveawayReminders(giveaway)
    Bot->>Scheduler: resetGiveawayTimer(giveaway) (finalizeGiveaway at endTime)
  end

  loop During giveaway
    participant User as Participant
    User->>Telegram: Tap "Join" button
    Telegram->>Bot: CallbackQuery gw_join_id
    Bot->>Giveaway: evaluateEligibility(user, giveaway)
    Giveaway-->>Bot: Eligible or not
    Bot->>Telegram: AnswerCbQuery + status message
  end

  par Scheduled refreshes at 25%, 50%, 75%
    Scheduler->>Bot: Timer fires (refresh point)
    Bot->>Giveaway: buildGiveawayAnnouncementText(giveaway, remaining)
    Bot->>Telegram: editMessageText or sendMessage(updated announcement)
    Bot->>Telegram: pinChatMessage(updated announceMsgId)
  and Time based reminders (10m, 5m, 1m, 30s, 10..1s)
    Scheduler->>Bot: Reminder timer fires
    Bot->>Telegram: sendMessage("X minutes/seconds remaining")
  end

  Scheduler->>Bot: endTimer fires
  Bot->>Giveaway: finalizeGiveaway(giveawayId)

  Giveaway->>Giveaway: Build weightedPool using computeParticipantWeight
  Giveaway->>Giveaway: Randomly pick winners, splice weightedPool to avoid loops
  Giveaway->>Bot: renderWinnersText(giveaway)
  Bot->>Telegram: sendMessage(group, HTML winners results)

  loop Notify winners
    Giveaway->>Bot: Winner info (userId, prize, boost)
    Bot->>Telegram: sendMessage(winner DM, Markdown notice)
    Telegram-->>Bot: OK or error (user blocked)
  end

  Giveaway->>Bot: Build admin report text and keyboard
  loop For each adminId in ADMIN_IDS
    Bot->>Telegram: sendMessage(admin DM, report + deep link + reroll/paid buttons)
  end
Loading

Sequence diagram for Helpful Tooltips management and posting

sequenceDiagram
  actor Admin
  participant Telegram
  participant Bot as Telegraf_bot
  participant TipsDashboard as sendTipsDashboard
  participant TipsStore as tipsStore

  Admin->>Telegram: Tap "Helpful Tooltips" in admin menu
  Telegram->>Bot: CallbackQuery admin_cmd_tips_dashboard
  Bot->>TipsDashboard: sendTipsDashboard(ctx)
  TipsDashboard->>TipsStore: Read tips, intervalHours, targetGroup, targetGroupTitle, silentMode
  TipsDashboard->>Telegram: replaceCallbackPanel(dashboard text + tipsDashboardKeyboard)

  Admin->>Telegram: Tap "Add Tooltip"
  Telegram->>Bot: CallbackQuery tips_cmd_add
  Bot->>TipsStore: user.pendingAction = await_tip_add_text (with createdAt)
  Bot->>Telegram: reply instructions with button syntax examples

  Admin->>Telegram: Send tooltip text with button lines
  Telegram->>Bot: Message text
  Bot->>TipsStore: pendingAction router sees await_tip_add_text
  Bot->>Bot: parseTooltipButtons(text)
  alt Invalid button syntax
    Bot->>Telegram: reply "Button syntax error" with details
  else Valid
    Bot->>TipsStore: Append new tip { id, text, enabled: true }
    Bot->>TipsStore: Increment nextTipId, clear pendingAction
    Bot->>Telegram: reply "Tooltip #id added (with inline buttons if any)"
  end

  Admin->>Telegram: Tap "Helpful Tooltips Settings"
  Telegram->>Bot: CallbackQuery tips_cmd_settings
  Bot->>TipsStore: Read intervalHours, targetGroup, targetGroupTitle, silentMode
  Bot->>Telegram: reply settings text + tipsSettingsKeyboard

  Admin->>Telegram: Tap "Link Channel/Group"
  Telegram->>Bot: CallbackQuery tips_set_link_target
  Bot->>TipsStore: user.pendingAction = await_tip_link_target
  Bot->>Telegram: reply "Forward a message from the target channel/group"

  Admin->>Telegram: Forward message from group/channel
  Telegram->>Bot: Message with forward info
  Bot->>Bot: extractForwardedChat(message)
  Bot->>TipsStore: approvedGroupsStore.add(chatId)
  Bot->>TipsStore: tipsStore.targetGroup = chatId, tipsStore.targetGroupTitle = title
  Bot->>TipsStore: broadcastConfigStore.targetGroup = chatId
  Bot->>TipsStore: Clear pendingAction, persistRuntimeState
  Bot->>Telegram: reply "Helpful Tooltips target linked: title (id)"

  Note over Admin,Bot: Scheduled posting
  participant Scheduler as startTipsScheduler
  Scheduler->>TipsStore: Periodic timer based on intervalHours
  Scheduler->>Bot: Select next enabled tip, avoid lastSentTipId when possible
  Bot->>TipsStore: postTipToConfiguredTarget(tip, telegram)
  Bot->>Bot: parseTooltipButtons(tip.text)
  Bot->>Telegram: sendMessage(targetGroup, formatTipForHtml(parsed.text), keyboard, silentMode)
  Telegram-->>Bot: Message OK
  Bot->>TipsStore: Update lastSentTipId, persistRuntimeState, saveHelpfulMessages
Loading

Updated class diagram for user state, tipsStore, giveaways, and metrics

classDiagram
  class User {
    +string id
    +number balance
    +number lastSeenAt
    +string playMode
    +object settings
    +Set giveawayJoinedIds
    +Set badges
    +OnboardingState onboarding
    +WalkthroughState walkthrough
    +number boostExpiresAt
    +string runewagerUsername
    +boolean hasClaimedNewUserPromo
    +boolean claimedPromo
    +number lastAnyPromoClaimAt
    +number mainMenuMsgId
    +number mainMenuChatId
    +number mainMenuSentAt
    +number adminMenuMsgId
    +number adminMenuChatId
    +number adminMenuSentAt
    +number lastMenuMsgId
    +number lastMenuChatId
    +number ephemeralBonusMsgId
    +number ephemeralBonusChatId
    +PendingAction pendingAction
  }

  class OnboardingState {
    +number currentStep
    +number startedAt
    +number completedAt
    +number[] stepTimestamps
    +boolean completionCardShown
  }

  class PendingAction {
    +string type
    +object data
    +number createdAt
  }

  class WalkthroughState {
    +number currentStep
    +Set completed
    +boolean started
  }

  class TipsStore {
    +Tooltip[] tips
    +boolean systemEnabled
    +number intervalHours
    +string targetGroup
    +string targetGroupTitle
    +boolean silentMode
    +number nextTipId
    +number lastSentTipId
  }

  class Tooltip {
    +number id
    +string text
    +boolean enabled
  }

  class Giveaway {
    +number id
    +string chatId
    +string title
    +number maxWinners
    +number scPerWinner
    +number durationMinutes
    +number endTime
    +Set participants
    +boolean dryRun
    +boolean testMode
    +number minParticipants
    +boolean requireLinked
    +boolean requireChannel
    +boolean requireGroup
    +boolean requireAge
    +boolean requireVerified
    +boolean requirePromo
    +boolean requireWalkthrough
    +string joinSurface
    +Participant[] winners
    +number announceMsgId
    +number pinnedMsgId
    +Timeout endTimer
    +Timeout[] reminders
  }

  class Participant {
    +string userId
    +string tgUsername
    +string firstName
    +string rwUsername
    +number awardedSc
    +boolean boosted
  }

  class MetricsCounters {
    +number menuStaleRecoveries
    +number pendingActionsTimedOut
  }

  class GroupCommandGuard {
    +Set GROUP_PASSTHROUGH_COMMANDS
    +use(ctx, next)
  }

  class Helpers {
    +number computeParticipantWeight(pUser)
    +Giveaway[] getRealGiveaways()
    +boolean isNewUserPromoEligible(user)
    +string onboardingProgressBar(step)
    +object evaluatePendingActionTimeout(user, now)
  }

  User --> OnboardingState : has
  User --> WalkthroughState : has
  User --> PendingAction : optional
  TipsStore --> Tooltip : manages
  Giveaway --> Participant : winners
  MetricsCounters <.. Helpers : updated_by
  GroupCommandGuard <.. User : reads_user_state
  Helpers <.. Giveaway : used_by
  Helpers <.. TipsStore : used_by
Loading

File-Level Changes

Change Details Files
Harden URL handling, SSH console, and command safety while improving link and play-mode behavior.
  • Added SAFE_URL_SCHEMES/UNSAFE_URL_SCHEMES and tightened unwrapTelegramUrl to validate embedded and raw URLs, never returning unvalidated input on parse failure.
  • Adjusted getDiscordLink to return null on invalid/non-configured links and removed hardcoded fallback; getPlayLink now supports legacy user.playMode and sanitizes playMode to browser/miniapp only.
  • Sanitized getStartAppLink route via regex and encodeURIComponent, and escaped referral codes in referralShareHTML to avoid HTML injection.
  • Strengthened SSHV command validation by blocking null bytes, backticks, and shell substitution patterns before exec, and blocked pipe-to-shell exploits via commandBlocked.
index.js
Introduce group command guard middleware and expand onboarding UX with progress indicators and completion card.
  • Added GROUP_PASSTHROUGH_COMMANDS set and middleware to intercept group/supergroup slash commands, redirecting non-whitelisted commands to DM with deep-link button.
  • Implemented onboardingProgressBar and updated showOnboardingPrompt to send a temporary progress header before each onboarding step.
  • Extended onboarding state with completionCardShown flag and added a one-time onboarding completion card when users reach the main menu after finishing onboarding.
index.js
RUNEWAGER_FUNCTIONALITY_MAP.md
todolist.md
Stabilize menu lifecycle with stale-menu cleanup, auto-vanish TTL, and better tracking of transient/persistent menus.
  • Introduced pendingActionsTimedOut and menuStaleRecoveries metrics, plus clearStaleMenuIds to clean transient and stale persistent menu IDs on startup using mainMenuSentAt/adminMenuSentAt timestamps.
  • Updated replyMenu and replaceCallbackPanel to track newly sent messages, support optional ttlMs auto-delete, and ensure clearOldMenus runs before help and other menus.
  • Fixed duplicate admin menu refresh calls around admin mode toggles and ensured settings_toggle_playmode refreshes the persistent user menu so labels stay in sync.
index.js
scripts/pre-deploy-checks.sh
Rework giveaways with pagination, weighted selection helper, auto-refresh announcements, richer reminders, and admin/user-facing panels.
  • Added computeParticipantWeight helper and used it in finalizeGiveaway with safer pool splicing to avoid infinite loops while preserving weighting.
  • Implemented getRealGiveaways helper and used it across user/admin panels and health metrics to consistently filter out testMode giveaways.
  • Paginated user giveaway list via sendUserGiveawaysPage (5 per page, 2‑minute TTL) and admin active giveaways via activeGiveawaysKeyboard with page callbacks.
  • Expanded giveaway lifecycle with giveawayPreflightCheck, scheduleGiveawayRefresh (25/50/75% edits with re-pins), richer scheduleGiveawayReminders (down to 10s countdown), HTML-formatted results, and detailed admin DM reports including deep-links and inline controls.
index.js
RUNEWAGER_FUNCTIONALITY_MAP.md
Rename and enhance the Content Drops system into a "Helpful Tooltips" system with inline buttons, settings, and group linking.
  • Renamed content drops terminology to Helpful Tooltips across admin menus, help text, commands, and dashboards; tipsStore gained targetGroupTitle and silentMode fields with snapshot load/save wiring.
  • Added parseTooltipButtons to parse inline button syntax from tooltip text (including special [Open Bot] button), wiring it into postTipToConfiguredTarget, add/edit flows, and validation paths.
  • Built tipsDashboardKeyboard, tipsSettingsKeyboard, and new tip link/interval flows (tips_cmd_settings, tips_set_interval, tips_set_link_target), plus await_tip_link_target handler to link group/channel targets with titles.
  • Improved command and inline actions for listing, testing, adding, editing, toggling, and importing tooltips, with clearer messages, error handling, and target display including linked group title.
index.js
RUNEWAGER_FUNCTIONALITY_MAP.md
todolist.md
Improve promo and referral flows including ephemeral bonus prompts and referral menu.
  • Refactored sendEphemeralBonusPrompt and deleteEphemeralBonusPrompt to be concurrency-safe via runUserMutation and to respect active promo availability, plus added a confirmation callback promo_confirm_claimed_next that marks claim state and guides the user.
  • Added isNewUserPromoEligible helper and integrated it into promo UX, and wired menu_claim_bonus action to rely on the new logic.
  • Introduced a referral menu (pmenu_referral) with code display, boost status, share link, and callback buttons to tie into the existing referral system.
index.js
Centralize health/metrics functionality with TLS-safe path resolution, shared health requester, and richer admin panels.
  • Introduced resolveTlsCertPathIfAllowed to validate HTTPS cert/key paths against multiple allowed directories and reused it in isHealthTlsEnabled and startHealthServer.
  • Created requestHealthPayload as a shared HTTP/HTTPS /health fetcher and reused it in /health and admin health tools actions.
  • Extended metrics output with menuStaleRecoveries, pendingActionsTimedOut, and uptime seconds, and upgraded admin_cmd_health to show uptime, memory, error window, real giveaway count, and allow refresh.
index.js
Refine tests and smoke checks to better detect catch-all regexes and command handlers, and ensure menu symbols exist pre-deploy.
  • Hardened smoke test file traversal by catching readdirSync errors when walking the repo for JS files.
  • Expanded isCatchAllRegexPattern to recognize more catch-all regex idioms and updated tests to ensure non-catch-all action patterns (e.g., gw_join_ and user_giveaways_page_) survive filtering.
  • Extended extractCommandHandlerNames to handle const/let/var declarations with or without semicolons, and added a new pre-deploy check to assert core menu functions exist in index.js.
test/smoke.test.js
scripts/pre-deploy-checks.sh
Rebuild tooltip-related scripts into safer, configurable generators and refreshers, and tighten runtime directory permissions.
  • Rewrote load_tooltips.sh into a generic, flag-driven script that writes a sanitized JSON tooltip list to data/tooltips.json, supports --push/--pull/--dry-run, and decouples from hardcoded paths.
  • Added generate_tooltips.sh to extract DEFAULT_TIPS_LIST from index.js and write data/tooltips.json atomically, plus add_tooltip.sh to append a new tooltip entry safely.
  • Hooked generate_tooltips.sh into deploy.sh and prod-run.sh (steps 3b/6b) so tooltips are refreshed before restart, and added chown/chmod hardening for data and logs directories and key files.
load_tooltips.sh
generate_tooltips.sh
add_tooltip.sh
deploy.sh
prod-run.sh
Update documentation and task tracking for v3.x behavior and new systems.
  • Updated RUNEWAGER_FUNCTIONALITY_MAP to describe group command guard middleware, Helpful Tooltips rebrand, giveaway auto-refresh behavior, and new onboarding progress/completion flows.
  • Extended todolist.md with a detailed v3.0 task board, marking implemented items and listing deferred v3.1 work.
  • Augmented CLAUDE.md with a v3.0 upgrade session narrative and finalized status, and added a full CHANGELOG.md capturing versions 2.0.0, 2.1.0, and 3.0.0.
RUNEWAGER_FUNCTIONALITY_MAP.md
todolist.md
CLAUDE.md
CHANGELOG.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Feb 28, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c279f5f and 86a6e1d.

📒 Files selected for processing (6)
  • RUNEWAGER_FUNCTIONALITY_MAP.md
  • add_tooltip.sh
  • generate_tooltips.sh
  • index.js
  • test/smoke.test.js
  • todolist.md

📝 Walkthrough

Walkthrough

This PR introduces group command guard middleware that intercepts non-passthrough commands in group chats and redirects users to direct messages, adds onboarding progress UI elements with visual progress indicators and completion cards, expands admin system tools with group linking capabilities, and updates shell scripts and test fixtures to support new patterns and environment-based configuration.

Changes

Cohort / File(s) Summary
Documentation & Configuration
RUNEWAGER_FUNCTIONALITY_MAP.md, todolist.md
Updated functionality map with group command guard behavior, onboarding progress UI details, and admin group-linking tools. Updated todolist with PR #112 review fixes and audit pass status.
Core Middleware & Admin Logic
index.js
Added group command guard middleware intercepting commands in group/supergroup chats with passthrough exceptions; introduced onboarding progress bar and completion card UI functions; added GROUP_PASSTHROUGH_COMMANDS constant; expanded admin system tools with group linking button and handler; updated user schema to include onboarding.completionCardShown flag.
Shell Script Utilities
add_tooltip.sh, generate_tooltips.sh
Refactored tooltip scripts to use environment variables (TOOLTIP_TEXT_ENV, TOOLTIP_TMP_FILE, RUNEWAGER_APP) instead of shell parameter expansion, reducing special-character exposure and improving safety.
Test Infrastructure
test/smoke.test.js
Extended regex catch-all recognition to include newline variants and post-whitespace-strip forms; expanded command-name extraction tests to cover let/var declarations without semicolons; added derived constant recognition in expected command sets.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • #8: Modifies onboarding behavior and user data shape in index.js, directly related to the new onboarding progress and completionCardShown field additions.
  • #98: Adds group-linking tools and admin handlers in index.js, directly related to the new admin group-linking callback and keyboard integration.
  • #102: Implements group linking feature and admin tooling in index.js, directly related to the expanded admin system tools with group-linking capabilities.

Suggested labels

codex

Poem

🐰 Through groups the commands now take flight,
With guards to steer them left and right!
Progress bars bloom in steps so bright,
Linking tools shine in admin's light—
A rabbit hops through v3.1's delight! 🎉

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Claude/plan v3 deployment vp mpw' is vague and uses non-descriptive abbreviations (vp, mpw) that obscure the actual change, making it unclear what the main purpose of the PR is despite containing extensive v3.0 production deployment updates across multiple files. Use a clear, descriptive title such as 'Deploy v3.0 production upgrade with menu lifecycle overhaul and security fixes' to convey the main change and scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/plan-v3-deployment-VPMpw

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Feb 28, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 6 issues, and left some high level feedback:

  • The generate_tooltips.sh script’s here‑doc/command substitution block that runs node - <<'EOF' ... EOF node "$APP_DIR/index.js" --version looks malformed and will likely execute an extra node process or fail unexpectedly; consider separating the version check from the JSON extraction into distinct, clearly scoped commands.
  • In add_tooltip.sh, the Node snippet building text: $(node -e "process.stdout.write(JSON.stringify('$TOOLTIP_TEXT'))") is fragile when TOOLTIP_TEXT contains quotes or shell metacharacters; it would be safer to pass the text via an environment variable or stdin and stringify it entirely inside Node to avoid shell interpolation issues.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `generate_tooltips.sh` script’s here‑doc/command substitution block that runs `node - <<'EOF' ... EOF node "$APP_DIR/index.js" --version` looks malformed and will likely execute an extra `node` process or fail unexpectedly; consider separating the version check from the JSON extraction into distinct, clearly scoped commands.
- In `add_tooltip.sh`, the Node snippet building `text: $(node -e "process.stdout.write(JSON.stringify('$TOOLTIP_TEXT'))")` is fragile when `TOOLTIP_TEXT` contains quotes or shell metacharacters; it would be safer to pass the text via an environment variable or stdin and stringify it entirely inside Node to avoid shell interpolation issues.

## Individual Comments

### Comment 1
<location path="index.js" line_range="11743" />
<code_context>
+bot.action('tips_cmd_import_batch', async (ctx) => {
</code_context>
<issue_to_address>
**issue (bug_risk):** Import Tooltips action reuses the single-tip pendingAction type, causing JSON input to be handled as plain text.

This action currently sets `user.pendingAction = { type: 'await_tip_add_text', createdAt: Date.now() }`, but the UI tells admins to paste a JSON array. The `await_tip_add_text` handler treats the incoming text as a single tooltip (including button syntax parsing), so the JSON array is stored as one tooltip string instead of being parsed into multiple tooltips.

Consider adding a separate pendingAction type (e.g. `await_tip_import_batch`) with a dedicated handler that parses the JSON array, validates entries, and appends multiple tooltips, so batch import doesn’t reuse the single-tip flow.
</issue_to_address>

### Comment 2
<location path="generate_tooltips.sh" line_range="37-46" />
<code_context>
+fi
+
+info "Extracting DEFAULT_TIPS_LIST from index.js..."
+TOOLTIP_JSON=$(node - <<'EOF'
+const fs = require('fs');
+const src = fs.readFileSync(process.argv[1] || 'index.js', 'utf8');
+// Execute just the DEFAULT_TIPS_LIST block and print it as JSON
+const m = src.match(/const DEFAULT_TIPS_LIST\s*=\s*(\[[\s\S]+?\]);/);
+if (!m) { process.stderr.write('DEFAULT_TIPS_LIST not found\n'); process.exit(1); }
+try {
+  // Use Function constructor for safe eval of the array literal
+  const list = (new Function('return ' + m[1]))();
+  console.log(JSON.stringify(list, null, 2));
+} catch (e) { process.stderr.write('Parse error: ' + e.message + '\n'); process.exit(1); }
+EOF
+node "$APP_DIR/index.js" --version 2>/dev/null || true
+) || {
+  # Fallback: emit a minimal valid tooltips.json with a placeholder
</code_context>
<issue_to_address>
**issue (bug_risk):** Command substitution mixes two node commands, which corrupts TOOLTIP_JSON and uses the wrong index.js path.

Because both `node - <<'EOF'` and `node "$APP_DIR/index.js" --version` run in the same command substitution, their stdout is combined and assigned to `TOOLTIP_JSON`, so any version output will invalidate the JSON. In addition, the extractor reads `process.argv[1] || 'index.js'`, but `node -` doesn’t set `argv[1]`, so it falls back to `index.js` in the current directory rather than `$APP_DIR/index.js`.

Please:
- Move or remove `node "$APP_DIR/index.js" --version` so it doesn’t run inside the `TOOLTIP_JSON=$(...)` substitution.
- Ensure the extractor reads the correct file, e.g. by invoking `node "$APP_DIR/index.js" <<'EOF'` and using `process.argv[1]`, or by passing the path via an environment variable.

This keeps `TOOLTIP_JSON` valid and avoids dependence on the working directory.
</issue_to_address>

### Comment 3
<location path="add_tooltip.sh" line_range="42-55" />
<code_context>
+TOOLTIP_TEXT="${CUSTOM_TEXT:-New tooltip — edit in admin panel via /tips.}"
+
+# Append new entry and get new ID using Node.js
+NEW_ID=$(node - "$TOOLTIPS_FILE" <<EOF
+const fs = require('fs');
+const file = process.argv[1];
+const list = JSON.parse(fs.readFileSync(file, 'utf8'));
+const maxId = list.reduce((m, t) => Math.max(m, Number(t.id) || 0), 0);
+const newId = maxId + 1;
+list.push({ id: newId, text: $(node -e "process.stdout.write(JSON.stringify('$TOOLTIP_TEXT'))"), enabled: true });
+fs.writeFileSync('${TMP_FILE}', JSON.stringify(list, null, 2));
+console.log(newId);
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Shell interpolation into the embedded Node script can break when TOOLTIP_TEXT contains quotes or special characters.

Because the here-doc is unquoted (`<<EOF`), the shell expands `$(...)` first and injects `$TOOLTIP_TEXT` into a single-quoted JS string. Any single quote or `$` in the tooltip text can break the JS/JSON or change what is passed to Node.

Instead, avoid interpolating `$TOOLTIP_TEXT` into the JS source:
- Pass it via an environment variable and read it with `process.env.TOOLTIP_TEXT`, or
- Quote the here-doc (`<<'EOF'`) and pass the text via `process.argv`/`process.env` rather than inline in the object literal.

This keeps the tooltip text safe for arbitrary content.

```suggestion
TOOLTIP_TEXT="${CUSTOM_TEXT:-New tooltip — edit in admin panel via /tips.}"

# Append new entry and get new ID using Node.js
NEW_ID=$(TOOLTIP_TEXT="$TOOLTIP_TEXT" node - "$TOOLTIPS_FILE" "$TMP_FILE" <<'EOF'
const fs = require('fs');

const [, , file, tmpFile] = process.argv;
const list = JSON.parse(fs.readFileSync(file, 'utf8'));
const maxId = list.reduce((m, t) => Math.max(m, Number(t.id) || 0), 0);
const newId = maxId + 1;

const text = process.env.TOOLTIP_TEXT ?? '';
list.push({ id: newId, text, enabled: true });

fs.writeFileSync(tmpFile, JSON.stringify(list, null, 2));
console.log(newId);
EOF
)
```
</issue_to_address>

### Comment 4
<location path="test/smoke.test.js" line_range="447-449" />
<code_context>
-  for (const c of cases) assert.equal(isCatchAllRegexPattern(c), true, `expected ${c} to be catch-all`);
-  for (const c of ['gw_join_(\d+)', 'help_page_(\d+)', 'promo_claim_(.+)']) {
-    assert.equal(isCatchAllRegexPattern(c), false, `expected ${c} not to be catch-all`);
+  const catchAllCases = ['.*', '^.*$', '.+', '^.+$', '(.*)', '(.+)', '(?:.*)', '(?:.+)', '^(?:.*)$', '(.+)?'];
+  for (const c of catchAllCases) assert.equal(isCatchAllRegexPattern(c), true, `expected "${c}" to be catch-all`);
+  // Specific patterns with capture groups are NOT catch-all
</code_context>
<issue_to_address>
**suggestion (testing):** Extend catch-all regex test cases to cover all newly supported patterns.

`isCatchAllRegexPattern` now treats patterns like `(.|\n)*`, `(.|\n)+`, and `(\.|[\s\S])*` as catch-alls, but the test only covers a subset. Please add these (and possibly a whitespace-padded variant) to `catchAllCases` so the new behavior is fully exercised and protected against regressions.

```suggestion
test('catch-all detection recognizes supported regex forms', () => {
  const catchAllCases = [
    '.*',
    '^.*$',
    '.+',
    '^.+$',
    '(.*)',
    '(.+)',
    '(?:.*)',
    '(?:.+)',
    '^(?:.*)$',
    '(.+)?',
    '(.|\\n)*',
    '(.|\\n)+',
    '(\\.|[\\s\\S])*',
    ' (?:.*) ',
  ];
  for (const c of catchAllCases) assert.equal(isCatchAllRegexPattern(c), true, `expected "${c}" to be catch-all`);
```
</issue_to_address>

### Comment 5
<location path="test/smoke.test.js" line_range="241" />
<code_context>
   const names = new Set();

-  // Resolve simple constants: const HELP = 'help'; const GW = `giveaway`;
+  // Resolve simple constants/variables: const/let/var HELP = 'help'; — semicolon optional
   const constMap = new Map();
-  for (const m of source.matchAll(/\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*(["'`])((?:\\.|(?!\2).)*)\2\s*;/g)) {
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests for `extractCommandHandlerNames` covering `let`/`var` and missing semicolons.

The updated regex now supports `const|let|var` and optional semicolons, but the tests don’t cover these new patterns. Please add a fixture with cases like `let HELP = 'help'` without a semicolon and assert that `extractCommandHandlerNames` still finds the `help` command to protect against regressions in the constant-resolution logic.

Suggested implementation:

```javascript
  // Resolve simple constants/variables: const/let/var HELP = 'help'; — semicolon optional
  const constMap = new Map();
  for (const m of source.matchAll(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(["'`])((?:\\.|(?!\2).)*)\2\s*[;\n]/g)) {
    const value = m[3];
    if (!value.includes('${')) constMap.set(m[1], value);
  }

  // NOTE: Tests for this constant-resolution logic (including let/var and missing semicolons)
  // are defined below in the "extractCommandHandlerNames" test suite.

```

I can only see the helper implementation, not the existing tests for `extractCommandHandlerNames`. To fully implement your suggestion, please add a new test case (or extend the existing suite) along these lines somewhere in `test/smoke.test.js` where other `extractCommandHandlerNames` tests live:

```js
describe('extractCommandHandlerNames', () => {
  // … existing tests …

  it('resolves command names from let/var declarations and missing semicolons', () => {
    const source = `
      // no semicolons
      let HELP = 'help'
      var GW = 'giveaway'

      // some other noise
      const PREFIX = '/';

      bot.command(HELP, fn);
      bot.command(GW, fn);
    `;

    const names = extractCommandHandlerNames(source);
    expect(names).toContain('help');
    expect(names).toContain('giveaway');
  });
});
```

If the tests use a different assertion style (e.g. `assert.deepStrictEqual` or `chai.expect`), adapt the expectations accordingly.  
If `extractCommandHandlerNames` is not in scope in `test/smoke.test.js`, ensure it is either exported/imported from the module under test or attached to the appropriate object so the test can call it.
</issue_to_address>

### Comment 6
<location path="RUNEWAGER_FUNCTIONALITY_MAP.md" line_range="404" />
<code_context>
+- 2026-02-28: v3.1 — added group command guard middleware (`GROUP_PASSTHROUGH_COMMANDS` + `bot.use` interceptor); added `onboardingProgressBar()` and progress header on each onboarding step prompt (auto-deletes 8s); added one-time onboarding completion card (tracked via `user.onboarding.completionCardShown`); added `🔗 Group Linking` to Admin System Tools keyboard (`admin_sys_group_linking` callback with back-to-system-tools navigation).
</code_context>
<issue_to_address>
**issue (typo):** Clarify phrasing to "auto-deletes after 8s" for grammatical correctness and consistency.

Here this reads as “auto-deletes 8s”, which looks like a missing word. Please update to “auto-deletes after 8s” to match the earlier phrasing and improve clarity.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread index.js Outdated
Comment thread generate_tooltips.sh Outdated
Comment thread add_tooltip.sh
Comment thread test/smoke.test.js
Comment thread test/smoke.test.js
Comment thread RUNEWAGER_FUNCTIONALITY_MAP.md Outdated
@codeant-ai

codeant-ai Bot commented Feb 28, 2026

Copy link
Copy Markdown

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Code Execution
    The script extracts a JS array from index.js and evaluates it with the Function constructor, which executes code derived from the repository file. If index.js is manipulated or contains unexpected constructs this can execute arbitrary JS during deploy. Consider a safer extraction/export path or strong validation of the extracted text.

  • Log & secrets exposure
    The prod-run flow writes generate_tooltips.sh output to /tmp/runewager-deploy.log and uses curl with BOT_TOKEN to send admin messages; logs or temporary files could expose tokens or other sensitive info if permissions are loose. Ensure logs and temp files are protected and avoid writing tokens to world-readable locations.

  • Race condition / atomicity
    The read-modify-write sequence is handled by a separate Node process writing to a temporary file then moved into place. Without explicit locking, concurrent runs can read the same input and produce duplicate IDs or corrupt the file during the mv. Consider using file locking or an atomic temp creation approach to avoid concurrent writer conflicts.

  • Temp file race
    The script uses a predictable temp filename ("$TOOLTIPS_FILE.tmp.$$") and writes to it before validation. This can be prone to race conditions or incorrect permissions on shared filesystems. Use secure atomic temp creation and ensure proper permissions/umask.

  • Command substitution
    The script performs command substitution to embed JSON-encoded user-supplied tooltip text into a Node.js heredoc. This mixing of shell and node expansions (and an unquoted heredoc) can be brittle and may allow unexpected content or injection-like breakage for specially crafted input. Consider passing the text to Node via environment/stdin or making the heredoc robustly quoted.

@gamblecodezcom

Copy link
Copy Markdown
Owner Author

@sourcery-ai
Fix all pr review issues

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@add_tooltip.sh`:
- Around line 20-23: The script uses the wrong Node argv index and injects
CUSTOM_TEXT directly into inline JS; change the node invocation so the tooltip
text is passed as a separate CLI argument (not interpolated into code) and read
it in the Node script from process.argv[2] (node - receives ["node", "-",
"<text>"] so the file/arg is at index 2), and remove any direct string
interpolation into the -e or heredoc JS; instead pass CUSTOM_TEXT as an argument
(or as JSON via stdin) and parse it safely in the Node code to avoid breaking on
quotes or enabling JS injection. Refer to CUSTOM_TEXT, the node - invocation,
and process.argv usage when making the fix.

In `@CLAUDE.md`:
- Around line 180-229: In the CLAUDE.md "2026-02-27 — v3.0 Upgrade Session"
section replace or augment the plain bullet lists with markdown task checkboxes
(- [ ] for incomplete, - [x] for done) so the release notes are trackable;
specifically convert key items such as Menu Lifecycle Overhaul entries
(clearStaleMenuIds(), replyMenu(), sendHelpMenu(), replaceCallbackPanel(),
settings_toggle_playmode), Feature Upgrades (sendUserGiveawaysPage(),
activeGiveawaysKeyboard(), pmenu_referral, adminStatsKeyboard()), Block 1 fixes
(getStartAppLink(), unwrapTelegramUrl(), getDiscordLink(),
evaluatePendingActionTimeout(), getPlayLink(), computeParticipantWeight()),
scripts/tests (load_tooltips.sh, test/smoke.test.js, pre-deploy-checks.sh) and
Infrastructure/Final Status lines into checkbox tasks, marking completed items
with - [x] and any outstanding follow-ups as - [ ]; keep the exact section
header and surrounding text unchanged.

In `@deploy.sh`:
- Around line 219-230: The current check uses the execute bit (-x) on
TOOLTIP_SCRIPT which wrongly skips readable-but-not-executable scripts that can
still be run with bash; update the condition to test readability/existence
(e.g., [[ -r "$TOOLTIP_SCRIPT" ]] or -f && -r) so the branch that runs
RUNEWAGER_DIR="$PROJECT_DIR" bash "$TOOLTIP_SCRIPT" still executes for readable
files, keeping the same logging calls (say, warn, send_admin) and messages for
success/failure and the fallback when the file is actually missing or
unreadable.

In `@generate_tooltips.sh`:
- Around line 37-50: The TOOLTIP_JSON extraction currently reads process.argv[1]
from a node - invocation and then appends a separate node "$APP_DIR/index.js"
--version in the same command substitution which makes fs.readFileSync fall back
to a relative 'index.js' and can contaminate stdout with a version string; fix
this by invoking the extractor with the app path as an explicit argument (e.g.
call node - "$APP_DIR/index.js" <<'EOF' so fs.readFileSync(process.argv[1] ||
'index.js') resolves to APP_DIR/index.js), remove the appended version check
from the same substitution (run the version check separately if needed) and
ensure any separate version check redirects both stdout and stderr (or
suppresses output) so only the JSON derived from DEFAULT_TIPS_LIST is emitted
into TOOLTIP_JSON.

In `@prod-run.sh`:
- Around line 272-277: The check uses BOT_TOKEN but the environment actually
provides TELEGRAM_BOT_TOKEN, causing the alert to be skipped; update the
condition and usage to reference TELEGRAM_BOT_TOKEN (or canonicalize by setting
BOT_TOKEN="${BOT_TOKEN:-${TELEGRAM_BOT_TOKEN:-}}") so the if [[ -n ... ]] test
and the curl invocation use the correct token variable (also keep
ADMIN_IDS/ADMIN_ID and the curl sendMessage call intact) to ensure failure DMs
are sent.

In `@scripts/pre-deploy-checks.sh`:
- Around line 57-63: The script currently validates helper function names but
should instead verify the five menu-system lifecycle symbols are present;
replace the for-loop's symbol list (currently getUser replyMenu clearOldMenus
sendPersistentUserMenu sendPersistentAdminMenu) with the required menu markers:
mainMenuSentAt, lastMenuSentAt, lastMenuViewedAt, persistentUserMenuSentAt,
persistentAdminMenuSentAt, and update the grep checks to look for those symbol
names in index.js (use word-boundary/assignment-aware patterns like grep -q -E
"\\bMAIN_SYMBOL\\b|\\bMAIN_SYMBOL\\s*=" to catch declarations/usages) so the
script fails if any of those five menu lifecycle symbols are missing.

In `@test/smoke.test.js`:
- Line 243: The regex used in the source.matchAll loop (used by
extractCommandHandlerNames) requires a trailing semicolon or newline (\s*[;\n])
and thus misses declarations at EOF; update that pattern to accept end-of-string
as a valid terminator (e.g., replace the terminator portion with a construct
that allows ;, newline, or end-of-string such as \s*(?:;|\n|$)) so no-semicolon
final-line declarations are captured.
- Around line 223-231: The catch-all detection in isCatchAllRegexPattern is
missing forms like ^(?:.*?)$ and is using a real newline in patterns like
'(.|\n)*' instead of the escaped '\\n' that comes from parsed regex literals;
update the CATCH_ALL_CORES set used by isCatchAllRegexPattern to include the
missing variants (e.g., '^(?:.*?)$', '(?:.*?)', '(.+)', '(.*)', '(?:.*)', and
their anchored forms) and replace any entries that contain an actual newline
with escaped-backslash-n versions (e.g., change '(.|\n)*' and '(.|\n)+' to
'(.|\\n)*' and '(.|\\n)+'), and ensure the function continues to check both
compact and stripped values (compact, stripped) against the expanded set.

In `@todolist.md`:
- Line 3: The "Last updated: 2026-02-28 — v3.1 items implemented and verified"
banner overstates status; update the header string "_Last updated: 2026-02-28 —
v3.1 items implemented and verified_" to wording that reflects in-progress work
(e.g., "in progress" or "some items implemented") and ensure it aligns with the
remaining open v3.1 checkboxes elsewhere in todolist.md so the banner no longer
claims full verification.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28c56b2 and c279f5f.

📒 Files selected for processing (15)
  • .gitignore
  • CHANGELOG.md
  • CLAUDE.md
  • RUNEWAGER_FUNCTIONALITY_MAP.md
  • add_tooltip.sh
  • deploy.sh
  • generate_tooltips.sh
  • index.js
  • load_tooltips.sh
  • package.json
  • prod-run.sh
  • runewager.service
  • scripts/pre-deploy-checks.sh
  • test/smoke.test.js
  • todolist.md

Comment thread add_tooltip.sh
Comment thread CLAUDE.md
Comment on lines +180 to +229
### 2026-02-27 — v3.0 Upgrade Session

**Scope:** Full production v3.0 deployment upgrade. `index.js` (14,368 lines after changes), `load_tooltips.sh`, `test/smoke.test.js`, `scripts/pre-deploy-checks.sh`, `package.json`, `CHANGELOG.md`, `todolist.md`.

**Menu Lifecycle Overhaul:**
- `mainMenuSentAt` / `adminMenuSentAt` timestamps added to user schema for stale detection
- `clearStaleMenuIds()` runs at bot startup, nulls out transient and 24h-stale menu IDs
- `replyMenu()` extended with `ttlMs` parameter — auto-deletes messages after timeout via `setTimeout`
- `sendHelpMenu()` now uses `replyMenu()` (was bare `ctx.reply()` — caused panel stacking)
- `replaceCallbackPanel()` fallback tracks sent message ID in user state
- Admin mode toggle double-fire fixed (removed duplicate `refreshAdminMenuHeader()`)
- `settings_toggle_playmode` now refreshes persistent user menu after toggle

**Feature Upgrades:**
- User giveaway list: `sendUserGiveawaysPage()` with 5/page pagination + 2-min auto-vanish TTL
- Admin giveaway panel: `activeGiveawaysKeyboard(page)` with 5/page pagination + `admin_gw_page_N` callbacks
- Referral sub-menu: `pmenu_referral` callback with share deep-link button
- Admin stats: active window indicator + Refresh button in `adminStatsKeyboard(activeWindow)`
- Admin health panel: fully inline with memory, error rate, active users (24h), persist age, giveaway count
- Broadcast builder: `👁 Preview` button sends preview to admin DM before mass send

**Block 1 Security & Logic Fixes (23 items):**
- `getStartAppLink()`: `encodeURIComponent()` + regex whitelist for route
- `referralShareHTML()`: `escapeHtml()` wraps code parameter
- `unwrapTelegramUrl()`: returns `''` on failure; scheme whitelist; handles `www.t.me`/`telegram.me`
- `getDiscordLink()`: returns `null` when DISCORD env vars not configured (no hardcoded fallback)
- `evaluatePendingActionTimeout()`: strict `>=` boundary; NaN guard; never mutates `createdAt`
- `getPlayLink()`: restored legacy `user.playMode` fallback
- Weighted winner pool: splice after pick, guaranteed termination
- `deploy_status` / `logs` commands: `execFile()` instead of `exec()` (no shell spawn)
- SSHV command validation: rejects null bytes, backticks, `$(`, `${`
- Startup warnings: non-fatal alerts for missing `ADMIN_IDS`, `TELEGRAM_CHANNEL_ID`, `TELEGRAM_GROUP_ID`
- Centralized helpers: `computeParticipantWeight()`, `getRealGiveaways()`, `isNewUserPromoEligible()`

**Shell Script (`load_tooltips.sh` — full rewrite):**
- Atomic write (temp→validate→mv); `--push`/`--pull` flags required for git ops
- Parameterized `REPO_DIR`; HTML-safe tooltips; `--dry-run` mode; permission checks

**Tests (`test/smoke.test.js`):**
- `readdirSync` call wrapped in try/catch
- `isCatchAllRegexPattern()` expanded to detect `(.*)`, `(.+)`, `(?:.*)`, `^(?:.*)$`, `(.+)?`
- `extractCommandHandlerNames()` supports `let`/`var` and no-semicolon forms

**Infrastructure:**
- `/metrics`: added `runewager_menu_stale_recoveries`, `runewager_pending_actions_timed_out`, `runewager_uptime_seconds`
- `pre-deploy-checks.sh`: Gate 3b — verifies 5 menu system symbols present in index.js
- `CHANGELOG.md`: created (v3.0.0 + v2.1.0 + v2.0.0 history)
- `package.json`: version bumped `2.1.0` → `3.0.0`

**Final Status:** `node --check index.js` clean, all 60 tests pass.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add checkbox task tracking to the new v3.0 session section.

The session log is well timestamped, but this block still needs - [ ] / - [x] task items so CLAUDE.md remains trackable per repo standard.

As per coding guidelines "**/{CLAUDE,todolist}.md: Maintain CLAUDE.md and todolist.md with markdown checkboxes (- [ ] and - [x]) for task tracking."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 180 - 229, In the CLAUDE.md "2026-02-27 — v3.0
Upgrade Session" section replace or augment the plain bullet lists with markdown
task checkboxes (- [ ] for incomplete, - [x] for done) so the release notes are
trackable; specifically convert key items such as Menu Lifecycle Overhaul
entries (clearStaleMenuIds(), replyMenu(), sendHelpMenu(),
replaceCallbackPanel(), settings_toggle_playmode), Feature Upgrades
(sendUserGiveawaysPage(), activeGiveawaysKeyboard(), pmenu_referral,
adminStatsKeyboard()), Block 1 fixes (getStartAppLink(), unwrapTelegramUrl(),
getDiscordLink(), evaluatePendingActionTimeout(), getPlayLink(),
computeParticipantWeight()), scripts/tests (load_tooltips.sh,
test/smoke.test.js, pre-deploy-checks.sh) and Infrastructure/Final Status lines
into checkbox tasks, marking completed items with - [x] and any outstanding
follow-ups as - [ ]; keep the exact section header and surrounding text
unchanged.

Comment thread deploy.sh
Comment thread generate_tooltips.sh Outdated
Comment thread prod-run.sh
Comment on lines +272 to +277
if [[ -n "${BOT_TOKEN:-}" && -n "${ADMIN_IDS:-}" ]]; then
ADMIN_ID="${ADMIN_IDS%%,*}"
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="$ADMIN_ID" \
-d text="⚠️ generate_tooltips.sh failed during prod-run — tooltips may be stale." \
>/dev/null 2>&1 || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tooltip-failure alert can be silently skipped due to token variable mismatch.

This block checks BOT_TOKEN, but the script’s .env token key is TELEGRAM_BOT_TOKEN, so failure DMs may never send unless BOT_TOKEN is separately exported.

🔧 Suggested fix
-        if [[ -n "${BOT_TOKEN:-}" && -n "${ADMIN_IDS:-}" ]]; then
-            ADMIN_ID="${ADMIN_IDS%%,*}"
-            curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
+        ALERT_TOKEN="${BOT_TOKEN:-$(read_env_value TELEGRAM_BOT_TOKEN || true)}"
+        ALERT_ADMINS="${ADMIN_IDS:-$(read_env_value ADMIN_IDS || true)}"
+        if [[ -n "$ALERT_TOKEN" && -n "$ALERT_ADMINS" ]]; then
+            ADMIN_ID="${ALERT_ADMINS%%,*}"
+            curl -s "https://api.telegram.org/bot${ALERT_TOKEN}/sendMessage" \
                 -d chat_id="$ADMIN_ID" \
                 -d text="⚠️ generate_tooltips.sh failed during prod-run — tooltips may be stale." \
                 >/dev/null 2>&1 || true
         fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prod-run.sh` around lines 272 - 277, The check uses BOT_TOKEN but the
environment actually provides TELEGRAM_BOT_TOKEN, causing the alert to be
skipped; update the condition and usage to reference TELEGRAM_BOT_TOKEN (or
canonicalize by setting BOT_TOKEN="${BOT_TOKEN:-${TELEGRAM_BOT_TOKEN:-}}") so
the if [[ -n ... ]] test and the curl invocation use the correct token variable
(also keep ADMIN_IDS/ADMIN_ID and the curl sendMessage call intact) to ensure
failure DMs are sent.

Comment on lines +57 to +63
for sym in getUser replyMenu clearOldMenus sendPersistentUserMenu sendPersistentAdminMenu; do
if grep -q "function ${sym}(" index.js 2>/dev/null || grep -q "async function ${sym}(" index.js 2>/dev/null; then
ok "$sym() defined"
else
fail "$sym() NOT found in index.js"
fi
done

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Gate 3b validates the wrong symbol set for the required menu lifecycle checks.

This currently checks helper function names, not the required menu-system symbols (e.g., mainMenuSentAt), so deployments can pass while required lifecycle markers are absent.

🔧 Suggested fix
-echo "3b) Menu System Symbols"
-for sym in getUser replyMenu clearOldMenus sendPersistentUserMenu sendPersistentAdminMenu; do
-  if grep -q "function ${sym}(" index.js 2>/dev/null || grep -q "async function ${sym}(" index.js 2>/dev/null; then
-    ok "$sym() defined"
+echo "3b) Menu System Symbols"
+for sym in mainMenuSentAt adminMenuSentAt clearStaleMenuIds sendPersistentUserMenu sendPersistentAdminMenu; do
+  if grep -Eq "\\b${sym}\\b" index.js 2>/dev/null; then
+    ok "$sym found"
   else
-    fail "$sym() NOT found in index.js"
+    fail "$sym NOT found in index.js"
   fi
 done

Based on learnings "Applies to **/pre-deploy-checks.sh : Gate pre-deploy-checks: verify 5 menu system symbols (mainMenuSentAt, etc.) present in index.js".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/pre-deploy-checks.sh` around lines 57 - 63, The script currently
validates helper function names but should instead verify the five menu-system
lifecycle symbols are present; replace the for-loop's symbol list (currently
getUser replyMenu clearOldMenus sendPersistentUserMenu sendPersistentAdminMenu)
with the required menu markers: mainMenuSentAt, lastMenuSentAt,
lastMenuViewedAt, persistentUserMenuSentAt, persistentAdminMenuSentAt, and
update the grep checks to look for those symbol names in index.js (use
word-boundary/assignment-aware patterns like grep -q -E
"\\bMAIN_SYMBOL\\b|\\bMAIN_SYMBOL\\s*=" to catch declarations/usages) so the
script fails if any of those five menu lifecycle symbols are missing.

Comment thread test/smoke.test.js
Comment on lines +223 to +231
const CATCH_ALL_CORES = new Set([
'.*', '.+', '(?:.*)', '(?:.+)',
'(.*)', '(.+)', '(.+)?',
'(.|\n)*', '(.|\n)+',
'(\\.|[\\s\\S])*',
]);
if (CATCH_ALL_CORES.has(compact) || CATCH_ALL_CORES.has(stripped)) return true;
// Compact form already stripped of anchors
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Catch-all detection still misses required forms and has escaped-\n mismatches.

^(?:.*?)$ is still not recognized, and entries like (.|\n)* in CATCH_ALL_CORES currently encode a real newline instead of matching \\n text from parsed regex literals.

🐛 Proposed fix
   const CATCH_ALL_CORES = new Set([
     '.*', '.+', '(?:.*)', '(?:.+)',
+    '(?:.*?)',
     '(.*)', '(.+)', '(.+)?',
-    '(.|\n)*', '(.|\n)+',
+    '(.|\\n)*', '(.|\\n)+',
     '(\\.|[\\s\\S])*',
   ]);
-  const catchAllCases = ['.*', '^.*$', '.+', '^.+$', '(.*)', '(.+)', '(?:.*)', '(?:.+)', '^(?:.*)$', '(.+)?'];
+  const catchAllCases = ['.*', '^.*$', '.+', '^.+$', '(.*)', '(.+)', '(?:.*)', '(?:.+)', '^(?:.*)$', '^(?:.*?)$', '(.+)?'];

As per coding guidelines "**/*.test.js: Expand isCatchAllRegexPattern() to detect (.+), (.), (?:.), and ^(?:.*?)$ patterns."

Also applies to: 448-453

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/smoke.test.js` around lines 223 - 231, The catch-all detection in
isCatchAllRegexPattern is missing forms like ^(?:.*?)$ and is using a real
newline in patterns like '(.|\n)*' instead of the escaped '\\n' that comes from
parsed regex literals; update the CATCH_ALL_CORES set used by
isCatchAllRegexPattern to include the missing variants (e.g., '^(?:.*?)$',
'(?:.*?)', '(.+)', '(.*)', '(?:.*)', and their anchored forms) and replace any
entries that contain an actual newline with escaped-backslash-n versions (e.g.,
change '(.|\n)*' and '(.|\n)+' to '(.|\\n)*' and '(.|\\n)+'), and ensure the
function continues to check both compact and stripped values (compact, stripped)
against the expanded set.

Comment thread test/smoke.test.js
Comment thread todolist.md Outdated
Comment thread add_tooltip.sh Outdated
Comment on lines +45 to +52
NEW_ID=$(node - "$TOOLTIPS_FILE" <<EOF
const fs = require('fs');
const file = process.argv[1];
const list = JSON.parse(fs.readFileSync(file, 'utf8'));
const maxId = list.reduce((m, t) => Math.max(m, Number(t.id) || 0), 0);
const newId = maxId + 1;
list.push({ id: newId, text: $(node -e "process.stdout.write(JSON.stringify('$TOOLTIP_TEXT'))"), enabled: true });
fs.writeFileSync('${TMP_FILE}', JSON.stringify(list, null, 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The Node.js helper script reads the wrong argument for the JSON file (process.argv[1] is "-" when using node -) and also interpolates the tooltip text directly into JavaScript source, which will crash when the text contains single quotes or newlines; restructuring the call to read the file from process.argv[2] and the tooltip text and temp file path from environment variables makes the JSON update robust. [logic error]

Severity Level: Critical 🚨
- ❌ add_tooltip.sh exits before updating data/tooltips.json.
- ⚠️ Admins cannot append new Helpful Tooltips entries.
- ⚠️ System tooltips file in index.js never reflects additions.
Suggested change
NEW_ID=$(node - "$TOOLTIPS_FILE" <<EOF
const fs = require('fs');
const file = process.argv[1];
const list = JSON.parse(fs.readFileSync(file, 'utf8'));
const maxId = list.reduce((m, t) => Math.max(m, Number(t.id) || 0), 0);
const newId = maxId + 1;
list.push({ id: newId, text: $(node -e "process.stdout.write(JSON.stringify('$TOOLTIP_TEXT'))"), enabled: true });
fs.writeFileSync('${TMP_FILE}', JSON.stringify(list, null, 2));
NEW_ID=$(TOOLTIP_TEXT="$TOOLTIP_TEXT" TMP_FILE="$TMP_FILE" node - "$TOOLTIPS_FILE" <<EOF
const fs = require('fs');
const file = process.argv[2];
const list = JSON.parse(fs.readFileSync(file, 'utf8'));
const maxId = list.reduce((m, t) => Math.max(m, Number(t.id) || 0), 0);
const newId = maxId + 1;
const text = process.env.TOOLTIP_TEXT || '';
list.push({ id: newId, text, enabled: true });
fs.writeFileSync(process.env.TMP_FILE, JSON.stringify(list, null, 2));
Steps of Reproduction ✅
1. From the repo root `/workspace/Runewager`, ensure Node.js is available and
`data/tooltips.json` exists (it can be created by `generate_tooltips.sh` at
`generate_tooltips.sh:13-17` or `load_tooltips.sh` at `load_tooltips.sh:18-21`).

2. Run `./add_tooltip.sh` (script defined in `/workspace/Runewager/add_tooltip.sh`, lines
1-63).

3. The script reaches the Node call at `add_tooltip.sh:45-55`, invoked as `node -
"$TOOLTIPS_FILE" <<EOF`, with `set -euo pipefail` active at `add_tooltip.sh:11`.

4. Inside the inline Node script, `const file = process.argv[1];` at `add_tooltip.sh:47`
resolves to `'-'` because `node - "$TOOLTIPS_FILE"` sets `process.argv[1]` to `'-'` and
`process.argv[2]` to the JSON path; Node then executes `fs.readFileSync(file, 'utf8')`
with `file === '-'`, which fails with ENOENT, causing the Node process to exit non‑zero
and, due to `set -e`, the shell script terminates without writing `${TMP_FILE}` or
updating `tooltips.json`.

5. To reproduce the text‑escaping failure, run `./add_tooltip.sh --text "Bob's new tip"`
so that `TOOLTIP_TEXT` contains a single quote (argument parsing at
`add_tooltip.sh:19-25`).

6. The nested Node command at `add_tooltip.sh:51` builds the JavaScript source
`process.stdout.write(JSON.stringify('Bob's new tip'))` because `$TOOLTIP_TEXT` is
expanded inside double quotes and injected directly into a single‑quoted JS string
literal.

7. Node evaluates this malformed script and throws a `SyntaxError` due to the unescaped
`'` in the literal, again exiting non‑zero.

8. With `set -euo pipefail` enabled (`add_tooltip.sh:11`), either the bad
`process.argv[1]` or the unescaped tooltip text causes the entire `add_tooltip.sh` run to
fail, so no new tooltip is appended to `data/tooltips.json`.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** add_tooltip.sh
**Line:** 45:52
**Comment:**
	*Logic Error: The Node.js helper script reads the wrong argument for the JSON file (`process.argv[1]` is `"-"` when using `node -`) and also interpolates the tooltip text directly into JavaScript source, which will crash when the text contains single quotes or newlines; restructuring the call to read the file from `process.argv[2]` and the tooltip text and temp file path from environment variables makes the JSON update robust.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread generate_tooltips.sh Outdated
Comment on lines +37 to +48
TOOLTIP_JSON=$(node - <<'EOF'
const fs = require('fs');
const src = fs.readFileSync(process.argv[1] || 'index.js', 'utf8');
// Execute just the DEFAULT_TIPS_LIST block and print it as JSON
const m = src.match(/const DEFAULT_TIPS_LIST\s*=\s*(\[[\s\S]+?\]);/);
if (!m) { process.stderr.write('DEFAULT_TIPS_LIST not found\n'); process.exit(1); }
try {
// Use Function constructor for safe eval of the array literal
const list = (new Function('return ' + m[1]))();
console.log(JSON.stringify(list, null, 2));
} catch (e) { process.stderr.write('Parse error: ' + e.message + '\n'); process.exit(1); }
EOF

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The tooltip generation script both (a) reads index.js from the current working directory instead of from the resolved app directory, so running it from outside the project will silently fall back to a placeholder instead of the real DEFAULT_TIPS_LIST, and (b) needlessly starts index.js via node "$APP_DIR/index.js" --version, which will run the full long-lived bot process and can hang the script or spawn an extra bot during deploy; reading $APP_DIR/index.js directly inside the Node snippet and removing the extra node ... --version call fixes this. [logic error]

Severity Level: Critical 🚨
- ❌ Deploy script hangs at tooltip generation step 3b.
- ❌ Prod-run script can hang during tooltip refresh.
- ⚠️ Helpful Tooltips JSON always overwritten with placeholder content.
- ⚠️ Users never see curated DEFAULT_TIPS_LIST tooltips.
Suggested change
TOOLTIP_JSON=$(node - <<'EOF'
const fs = require('fs');
const src = fs.readFileSync(process.argv[1] || 'index.js', 'utf8');
// Execute just the DEFAULT_TIPS_LIST block and print it as JSON
const m = src.match(/const DEFAULT_TIPS_LIST\s*=\s*(\[[\s\S]+?\]);/);
if (!m) { process.stderr.write('DEFAULT_TIPS_LIST not found\n'); process.exit(1); }
try {
// Use Function constructor for safe eval of the array literal
const list = (new Function('return ' + m[1]))();
console.log(JSON.stringify(list, null, 2));
} catch (e) { process.stderr.write('Parse error: ' + e.message + '\n'); process.exit(1); }
EOF
TOOLTIP_JSON=$(node - <<EOF
const fs = require('fs');
const src = fs.readFileSync("${APP_DIR}/index.js", 'utf8');
// Execute just the DEFAULT_TIPS_LIST block and print it as JSON
const m = src.match(/const DEFAULT_TIPS_LIST\s*=\s*(\[[\s\S]+?\]);/);
if (!m) { process.stderr.write('DEFAULT_TIPS_LIST not found\n'); process.exit(1); }
try {
// Use Function constructor for safe eval of the array literal
const list = (new Function('return ' + m[1]))();
console.log(JSON.stringify(list, null, 2));
} catch (e) { process.stderr.write('Parse error: ' + e.message + '\n'); process.exit(1); }
EOF
Steps of Reproduction ✅
1. Trigger a normal VPS deploy by running `/var/www/html/Runewager/deploy.sh` (file
`/workspace/Runewager/deploy.sh`, lines 1–37), which changes directory into the project
(`PROJECT_DIR`) at line 26 and 38 and then proceeds through the deployment steps.

2. After npm dependencies are installed, observe step "3b – Refreshing Helpful Tooltips"
in `deploy.sh` at lines 215–221, where
`TOOLTIP_SCRIPT="$PROJECT_DIR/generate_tooltips.sh"` is set (line 218) and the script is
invoked as `RUNEWAGER_DIR="$PROJECT_DIR" bash "$TOOLTIP_SCRIPT"` (line 220), executing
`/workspace/Runewager/generate_tooltips.sh` with `APP_DIR` resolved to the project
directory.

3. Inside `generate_tooltips.sh` (file `/workspace/Runewager/generate_tooltips.sh`), lines
31–36 confirm that `index.js` exists at `$APP_DIR/index.js`; then at lines 36–47 the Node
snippet is executed as `node - <<'EOF'` and reads `const src =
fs.readFileSync(process.argv[1] || 'index.js', 'utf8');` (line 39). Because Node is
invoked as `node -`, `process.argv[1]` is `'-'`, so it attempts to read a file literally
named `-`, which does not exist, causing an ENOENT error and a non‑zero exit from the Node
process.

4. Due to the Node process exiting with an error, the command substitution wrapping the
Node snippet (lines 36–53) falls into the `|| { ... }` fallback block at lines 50–53,
where `TOOLTIP_JSON` is always set to the hard-coded placeholder JSON string instead of
the real `DEFAULT_TIPS_LIST`, and later lines 62–73 write this placeholder data to
`$APP_DIR/data/tooltips.json`, so the bot's Helpful Tooltips system (which reads
`systemTooltipsFile` `/var/www/html/Runewager/data/tooltips.json` defined in
`/workspace/Runewager/index.js:380`) never sees the curated tips.

5. In the same `TOOLTIP_JSON=$(...)` block, after the here-doc ends at line 48, a second
command `node "$APP_DIR/index.js" --version 2>/dev/null || true` is executed at line 49;
this starts the long‑running bot process synchronously during tooltip generation, so
`generate_tooltips.sh` (and thus `deploy.sh` or `prod-run.sh`, which calls it similarly at
`/workspace/Runewager/prod-run.sh:263–268`) can hang waiting for that Node process to exit
or end up spawning an extra bot instance before the systemd-managed restart.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** generate_tooltips.sh
**Line:** 37:48
**Comment:**
	*Logic Error: The tooltip generation script both (a) reads `index.js` from the current working directory instead of from the resolved app directory, so running it from outside the project will silently fall back to a placeholder instead of the real DEFAULT_TIPS_LIST, and (b) needlessly starts `index.js` via `node "$APP_DIR/index.js" --version`, which will run the full long-lived bot process and can hang the script or spawn an extra bot during deploy; reading `$APP_DIR/index.js` directly inside the Node snippet and removing the extra `node ... --version` call fixes this.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment thread prod-run.sh
# 6b) Auto-run tooltip generation (must run before bot restart)
say "Refreshing Helpful Tooltips..."
TOOLTIP_SCRIPT="$PROJECT_DIR/generate_tooltips.sh"
if [[ -x "$TOOLTIP_SCRIPT" ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The tooltip refresh step wrongly requires generate_tooltips.sh to be executable (-x) even though it is invoked via bash, so if the script file exists but lacks the execute bit (a common situation after checkout), the refresh will be silently skipped and tooltips will remain stale on deploy. [logic error]

Severity Level: Major ⚠️
- ⚠️ Helpful Tooltips not refreshed by prod-run maintenance script.
- ⚠️ data/tooltips.json may lag behind DEFAULT_TIPS_LIST changes.
- ⚠️ Warning mislabels non-executable script as "not found".
Suggested change
if [[ -x "$TOOLTIP_SCRIPT" ]]; then
if [[ -f "$TOOLTIP_SCRIPT" ]]; then
Steps of Reproduction ✅
1. Confirm the production runner is the main maintenance entrypoint:
`/var/www/html/Runewager/prod-run.sh` (script content defined at
`/workspace/Runewager/prod-run.sh:1-9` via Read), and that it is used to restart the bot
on the VPS.

2. On the VPS, ensure the tooltip generator script file exists but is not executable,
while remaining readable: `ls /var/www/html/Runewager/generate_tooltips.sh` (content at
`/workspace/Runewager/generate_tooltips.sh:1-75`) then run `chmod 0644
/var/www/html/Runewager/generate_tooltips.sh`.

3. Run the production runner: `bash /var/www/html/Runewager/prod-run.sh`; during step "6b)
Auto-run tooltip generation" at `/workspace/Runewager/prod-run.sh:262-282`, the guard `if
[[ -x "$TOOLTIP_SCRIPT" ]]; then` fails because the script lacks the execute bit, so the
`else` branch executes and logs `generate_tooltips.sh not found at $TOOLTIP_SCRIPT —
skipping tooltip refresh` instead of invoking `bash "$TOOLTIP_SCRIPT"`.

4. After prod-run completes and the bot restarts, inspect `data/tooltips.json` under the
project directory (written by `generate_tooltips.sh` at
`/workspace/Runewager/generate_tooltips.sh:16,73-75` and consumed by the bot via `const
systemTooltipsFile = '/var/www/html/Runewager/data/tooltips.json';` at
`/workspace/Runewager/index.js:380`); observe that its contents remain unchanged from
before the deploy, so any updates to `DEFAULT_TIPS_LIST` are not reflected because tooltip
generation was skipped solely due to missing execute permissions.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** prod-run.sh
**Line:** 266:266
**Comment:**
	*Logic Error: The tooltip refresh step wrongly requires `generate_tooltips.sh` to be executable (`-x`) even though it is invoked via `bash`, so if the script file exists but lacks the execute bit (a common situation after checkout), the refresh will be silently skipped and tooltips will remain stale on deploy.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai

codeant-ai Bot commented Feb 28, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

…ates

PR #112 review fixes (sourcery-ai):
- R1: tips_cmd_import_batch now uses await_tip_import_batch pending type
  with dedicated JSON-array router handler; proper MarkdownV2 prompt
- R2: generate_tooltips.sh fixes command substitution pollution; use
  RUNEWAGER_APP env var instead of process.argv[1] (undefined in node -)
- R3: add_tooltip.sh fixes shell injection; TOOLTIP_TEXT passed via
  TOOLTIP_TEXT_ENV env var, heredoc uses <<'EOF', process.argv[2] for file
- R4: extend catchAllCases array with (.|\n)*, (.|\n)+, (\.|[\s\S])*;
  add post-whitespace-strip forms to CATCH_ALL_CORES set
- R5: extractCommandHandlerNames test now covers let/var declarations and
  no-semicolon forms (CMD_FOUR/eta, CMD_FIVE/theta)
- R6: RUNEWAGER_FUNCTIONALITY_MAP.md typo "auto-deletes 8s" → "after 8s"

Codebase audit duplicate removal:
- A1: remove dead buildGiveawayAnnouncementText(giveaway, remainingStr)
  at ~12533; keep dynamic version at ~13795
- A2: remove simplified buildGiveawayAnnouncementKeyboard at ~13817
  (wrong tgw_participants_ callback); restore full 5-row version
- A3+A4: remove first duplicate bot.action registrations for
  admin_cat_system and admin_cat_support (identical bodies)

All 60 tests pass, node --check clean, bash -n clean on both scripts.

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
Resolved conflicts in favour of our branch (HEAD) for:
- index.js: keep await_tip_import_batch router + handler, keep A1/A2
  duplicate-function removal, keep Content Drops branding from main
- add_tooltip.sh / generate_tooltips.sh: keep env-var injection fixes
- test/smoke.test.js: keep extended catchAllCases + extractCommand fixtures
- RUNEWAGER_FUNCTIONALITY_MAP.md / todolist.md: keep our updated content

Incoming from origin/main: claude-pr-results-20260228_193756Z.md

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
@codeant-ai

codeant-ai Bot commented Feb 28, 2026

Copy link
Copy Markdown

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@gamblecodezcom
gamblecodezcom merged commit aca9b00 into main Feb 28, 2026
5 of 6 checks passed
@gamblecodezcom
gamblecodezcom deleted the claude/plan-v3-deployment-VPMpw branch February 28, 2026 19:46
@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:XL This PR changes 500-999 lines, ignoring generated files labels Feb 28, 2026
@codeant-ai

codeant-ai Bot commented Feb 28, 2026

Copy link
Copy Markdown

CodeAnt AI Incremental review completed.

gamblecodezcom added a commit that referenced this pull request Feb 28, 2026
## Summary by Sourcery

Add group command guard middleware, enhance onboarding UX, and improve
deployment/startup robustness while addressing PR review and audit
issues.

New Features:
- Introduce middleware to intercept group chat commands and redirect
non-group-specific commands to direct messages with a deep-link button.
- Add onboarding progress indicators with a transient progress header
and a one-time completion card when onboarding is finished.
- Enable batch JSON import of tooltips via a dedicated pending action
and admin command.

Enhancements:
- Expose group linking tools from the Admin System Tools panel via a new
callback action.
- Extend catch-all regex and command handler detection tests to cover
additional patterns and declaration forms.
- Remove duplicate and obsolete giveaway helper functions and admin
action handlers.
- Harden tooltip generation and addition scripts by avoiding command
substitution pollution and potential shell injection.

Build:
- Update start, dev, deploy, rollback, and prod run scripts to pull
latest code, refresh tooltips from index.js, and free blocked bot ports
before (re)starting services.

Deployment:
- Ensure deployment and rollback workflows regenerate tooltips from the
current codebase and kill processes blocking the configured bot port
before restart.

Documentation:
- Refresh functionality map and todo board to document the new group
command guard, onboarding UX, admin group linking entry point, and
PR/audit changes.

Tests:
- Expand smoke tests for regex catch-all detection and command
extraction to ensure broader pattern coverage and correctness.

Chores:
- Record PR #112 review fixes and v3.1 audit notes in project
documentation.

---------

Co-authored-by: Claude <noreply@anthropic.com>
gamblecodezcom pushed a commit that referenced this pull request Mar 1, 2026
… path validation

- index.js: keep MarkdownV2 escaping for runewagerUsername (security fix)
- generate_tooltips.sh: keep path validation (absolute .js, no traversal)
- start.sh / dev-run.sh / scripts/rollback.sh: keep SIGTERM→SIGKILL two-step
- start.sh: keep inline comment stripping in PORT parse
- test/smoke.test.js: keep (.|)* and (.|)+ catch-all cases

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
gamblecodezcom pushed a commit that referenced this pull request Mar 1, 2026
PR review comment fixes:
- add_tooltip.sh: validate list is Array, filter non-finite IDs before
  computing maxId; throw fast on malformed tooltips.json
- docs/12-group-linking.md: fix entry points table — admin System Tools
  uses admin_sys_group_linking, not admin_cat_system
- docs/15-misc-commands.md: remove ⚠️ walkthrough dead-end note (resolved)

T-01 — Walkthrough system hardened:
- sendWalkthroughStep(): clearOldMenus() before every step send
- Back button disabled on step 1 (first step)
- Next replaced with Finish button on last step (step 35)
- walk_done on last step: sets started=false, fires walkthrough_completed
  analytics, answerCbQuery with success toast, returns to main menu
- New doc: docs/features/16-walkthrough.md

T-02 — clearOldMenus() added to 5 missing locations:
- sendOnboardingReferralPrompt()
- renderSshvConsole()
- renderGroupLinkingTools()
- tips_cmd_edit handler
- tips_cmd_remove handler
(sendGiveawayListPage already used replyMenu() — no change needed)

T-03 — Tooltip view flow implemented:
- tipsDashboardKeyboard(): "👁 View Tooltip" button added (row 4)
- tips_cmd_view handler: clearOldMenus + tipSelectKeyboard('tip_view')
- tip_view_{id} handler: preview card with Prev/Next tip navigation,
  Edit / Toggle / Delete buttons, Back to List, Admin Menu

T-15 — Broadcast failure logging made reliable:
- Removed 500-item cap from broadcastFailedUsers push and load
- Every failure logged via adminLog('broadcast_failure', ...) to
  data/admin-events.log — no silent drops
- /broadcast_failed: chunked output (30/chunk, up to 90 inline);
  overflow note points to log file
- High-failure-rate warning: >20% failure rate sends ⚠️ DM to ADMIN_IDS

Merge conflicts resolved (PRs #112-114):
- index.js: keep MarkdownV2 escaping for runewagerUsername
- generate_tooltips.sh: keep path validation (absolute .js, no traversal)
- start.sh/dev-run.sh/rollback.sh: keep SIGTERM→SIGKILL two-step
- test/smoke.test.js: keep (.|)* and (.|)+ catch-all test cases

Tests: 60/60 pass | node --check: clean | bash -n: all scripts OK

https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
gamblecodezcom added a commit that referenced this pull request Mar 1, 2026
## **User description**
>

## Summary by Sourcery

Introduce v3.1 operational and documentation updates, including stricter
group command handling, enhanced onboarding UX, safer tooltip tooling,
and more robust deploy/run scripts with port management and tooltip
regeneration.

New Features:
- Add middleware guard that redirects most group chat commands to DM
with a deep-link button while allowing a small set of group-aware
commands to execute normally.
- Show a visual onboarding progress indicator for each step and a
one-time onboarding completion card when users reach the main menu.
- Expose Group Linking tools directly from the Admin System Tools panel,
with appropriate navigation callbacks.
- Introduce a dedicated batch tooltip JSON import flow for admins with
clearer prompts and validation.

Bug Fixes:
- Prevent Markdown injection in the onboarding completion card by
escaping the linked username.
- Fix tooltip generation and addition scripts to avoid command
substitution pollution and shell injection, and ensure they read the
correct index.js path via environment variables.
- Correct catch-all regex detection and tests to cover additional
multiline and whitespace-stripped patterns.
- Ensure command handler extraction tests cover let/var declarations and
no-semicolon cases.
- Remove duplicate giveaway announcement helpers and duplicate admin
category action registrations that could cause confusion or dead code
paths.

Enhancements:
- Extend onboarding state schema to track whether the completion card
has been shown and backfill existing users safely.
- Improve tips import UX with a richer MarkdownV2 help message and
clearer error handling for invalid or empty JSON payloads.
- Refine admin system tools keyboard layout by adding a Group Linking
entry point and documenting its callback wiring.
- Expand and reorganize in-repo feature documentation, adding a docs
index, per-feature docs (onboarding, menus, promos, giveaways, tooltips,
SSHV, etc.), and a functionality upgrade TODO log.
- Tighten generate_tooltips.sh by validating the app path and guarding
against oversized or malformed DEFAULT_TIPS_LIST literals before
evaluation.

Build:
- Update start, dev-run, deploy, rollback, and prod-run scripts to
always pull latest main, regenerate tooltips from index.js, and
proactively free the configured bot port (via lsof/fuser helpers) before
starting or restarting the service, while logging failures as non-fatal
where appropriate.

Documentation:
- Refresh RUNEWAGER_FUNCTIONALITY_MAP with v3.1 notes on group command
guarding, onboarding progress/completion UI, Group Linking entry in
admin tools, and detailed PR #112 audit results.
- Add a structured docs/ feature documentation system (INDEX,
per-feature markdown files for major subsystems, and a
TODO_FUNCTIONALITY_UPGRADE log) and mandate keeping docs in sync with
code.
- Document tooltip, giveaway, deploy-ops, SSHV, onboarding, user/admin
menus, group linking, referrals, bug reports, announcements, bonus
system, user lookup, misc commands, and background tasks in dedicated
feature files.

Tests:
- Strengthen smoke tests around catch-all regex detection, command
handler name extraction, and related pattern sets to reduce false
negatives and improve coverage.

Chores:
- Update todolist.md to reflect v3.1 work completion and add a detailed
PR #112 + audit-pass section enumerating fixes and their status.


___

## **CodeAnt-AI Description**
Redirect group commands to DM, show onboarding progress and completion,
add admin Group Linking and safer tooltip import/refresh

### What Changed
- Commands sent in group/supergroup chats are intercepted and, except
for a small allowlist (link, giveaway, start_giveaway, admin), receive a
DM deep-link button and the group command is suppressed
- Onboarding shows a visual progress bar for each step (auto-deletes)
and a one-time friendly completion card the first time users reach the
main menu
- Admin System Tools gains a "🔗 Group Linking" entry that opens the
group-linking tools and returns to System Tools
- Admin tooltip workflow: added a dedicated batch JSON import (paste a
JSON array to add multiple tooltips) with validation and clear
success/error replies
- Deployment/dev/start scripts now pull latest main, regenerate tooltips
from index.js, and proactively free the configured bot port before
starting to avoid port conflicts
- Tooltip extraction and add-tooltip shell helpers hardened to avoid
command-substitution/shell-injection and to validate the index.js path
before evaluating literals
- Tests extended to recognize extra catch-all regex forms and to detect
command handlers declared with let/var and without semicolons

### Impact
`✅ Clearer DM guidance for group users`
`✅ Shorter onboarding with visible progress and a welcoming completion
card`
`✅ Fewer deploy-start failures due to blocked ports and stale tooltips`
<details>
<summary><strong>💡 Usage Guide</strong></summary>

### Checking Your Pull Request
Every time you make a pull request, our system automatically looks
through it. We check for security issues, mistakes in how you're setting
up your infrastructure, and common code problems. We do this to make
sure your changes are solid and won't cause any trouble later.

### Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You
can easily get in touch with CodeAnt AI right here. Just type the
following in a comment on your pull request, and replace "Your question
here" with whatever you want to ask:
<pre>
<code>@CodeAnt-AI ask: Your question here</code>
</pre>
This lets you have a chat with CodeAnt AI about your pull request,
making it easier to understand and improve your code.

#### Example
<pre>
<code>@CodeAnt-AI ask: Can you suggest a safer alternative to storing
this secret?</code>
</pre>

### Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future
reviews. Reply directly to the specific CodeAnt AI suggestion (in the
same thread) and replace "Your feedback here" with your input:
<pre>
<code>@CodeAnt-AI: Your feedback here</code>
</pre>
This helps CodeAnt AI learn and adapt to your team's coding style and
standards.

#### Example
<pre>
<code>@CodeAnt-AI: Do not flag unused imports.</code>
</pre>

### Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
<pre>
<code>@CodeAnt-AI: review</code>
</pre>

### Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at
[https://app.codeant.ai](https://app.codeant.ai). This tool helps you
identify potential issues and areas for improvement in your codebase,
ensuring your repository maintains high standards of code health.

</details>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Centralized feature docs index with 16 per-feature guides
* Interactive 35-step walkthrough with progress tracking and completion
indicators
* Enhanced tooltip tooling (validation, runtime-preservation, view/edit
flows, seeding/dry-run/force)

* **Bug Fixes**
* Improved broadcast failure reporting with chunked logs and admin
alerts
  * Safer, more reliable port release across start/deploy/dev scripts

* **Refactors**
  * Cleaner menu lifecycle: stale-menu cleanup and last-message tracking
  * DM-redirects and smoother onboarding/navigation UX

* **Documentation**
  * Added functionality-upgrade backlog and governance cross-references
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants