Claude/plan v3 deployment vp mpw - #112
Conversation
…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 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 · |
Reviewer's GuideRefactors 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 groupssequenceDiagram
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
Sequence diagram for v3 giveaway lifecycle with preflight, refresh, and finalizationsequenceDiagram
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
Sequence diagram for Helpful Tooltips management and postingsequenceDiagram
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
Updated class diagram for user state, tipsStore, giveaways, and metricsclassDiagram
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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis 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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Hey - I've found 6 issues, and left some high level feedback:
- The
generate_tooltips.shscript’s here‑doc/command substitution block that runsnode - <<'EOF' ... EOF node "$APP_DIR/index.js" --versionlooks malformed and will likely execute an extranodeprocess or fail unexpectedly; consider separating the version check from the JSON extraction into distinct, clearly scoped commands. - In
add_tooltip.sh, the Node snippet buildingtext: $(node -e "process.stdout.write(JSON.stringify('$TOOLTIP_TEXT'))")is fragile whenTOOLTIP_TEXTcontains 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Nitpicks 🔍
|
|
@sourcery-ai |
There was a problem hiding this comment.
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
📒 Files selected for processing (15)
.gitignoreCHANGELOG.mdCLAUDE.mdRUNEWAGER_FUNCTIONALITY_MAP.mdadd_tooltip.shdeploy.shgenerate_tooltips.shindex.jsload_tooltips.shpackage.jsonprod-run.shrunewager.servicescripts/pre-deploy-checks.shtest/smoke.test.jstodolist.md
| ### 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. |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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
doneBased 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.
| 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; |
There was a problem hiding this comment.
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.
| 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)); |
There was a problem hiding this comment.
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.| 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.| 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 |
There was a problem hiding this comment.
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.| 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.| # 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 |
There was a problem hiding this comment.
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".| 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 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 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 · |
|
CodeAnt AI Incremental review completed. |
## 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>
… 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
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
## **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>
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:
Bug Fixes:
Enhancements:
Build:
Deployment:
Documentation:
Tests:
Chores:
Summary by CodeRabbit
New Features
Tests
Chores
CodeAnt-AI Description
Redirect group commands to DM; add onboarding progress + one-time completion; safe tooltip import and script fixes
What Changed
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:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
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:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
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.