Claude/plan v3 deployment vp mpw - #114
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
…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
…ry bot start Every script that starts/restarts the bot now follows the same safe sequence: 1. git pull origin main (fetch + reset --hard) 2. generate_tooltips.sh (extracts DEFAULT_TIPS_LIST → data/tooltips.json) 3. kill any process blocking PORT (default 3000) 4. start/restart bot Changes per file: - prod-run.sh: add step 9c — free_port_if_conflicted() BEFORE step 10 restart (port-kill was already present in God-Mode Heal but fired after, not before) - deploy.sh: add step 3c — inline lsof/fuser port-kill before systemctl start - start.sh: add git fetch+reset, generate_tooltips, port-kill, stale-PID kill before bot launch; replace refuse-on-duplicate with kill-and-continue - dev-run.sh: add git fetch+reset (best-effort), generate_tooltips, port-kill before exec node - scripts/rollback.sh: add generate_tooltips after npm ci (refreshes from rolled-back index.js), add lsof/fuser port-kill before service start (no git pull — rollback intentionally targets an older commit) https://claude.ai/code/session_01VijmtzjN63WZJy5gYgJAKs
- Escape Markdown special chars in runewagerUsername to prevent parse failures - Add explicit smoke-test assertions for (.|)* and (.|)+ catch-all forms - Strip inline comments from .env PORT values in deploy.sh, dev-run.sh, start.sh (e.g. PORT=3000 # dev now correctly yields 3000) - Fix showOnboardingPrompt JSDoc: steps documented as 1–4 → 1–5 (matches impl) - Upgrade all port-block kill loops to SIGTERM-first then SIGKILL after 2s grace - Add path + shape validation to generate_tooltips.sh Node extraction block (validates RUNEWAGER_APP is absolute .js path; checks array literal size/shape) 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 guide (collapsed on small PRs)Reviewer's GuideAdds group-chat command guard middleware, improves onboarding with progress/completion UX, introduces batch tooltip import via admin tools, and hardens dev/deploy scripts and tooltip generation while cleaning up duplicate giveaway/admin code and extending regex/command extraction tests. Sequence diagram for group-chat command guard middlewaresequenceDiagram
actor User
participant TelegramChat as TelegramGroupOrSupergroup
participant Bot as TelegrafBot
participant Guard as GroupCommandGuardMiddleware
participant Router as CommandRouter
participant DM as TelegramDirectMessage
User->>TelegramChat: Send /some_command
TelegramChat->>Bot: Update with message
Bot->>Guard: handleUpdate(ctx)
alt Command in group or supergroup
Guard->>Guard: isWhitelistedGroupCommand(command)
alt Command is whitelisted (link, giveaway, start_giveaway, admin)
Guard-->>Router: next(ctx)
Router->>Bot: Execute group-aware handler
Bot-->>TelegramChat: Reply with group-specific response
else Command is not whitelisted
Guard-->>TelegramChat: Send message with deep-link button to bot DM
Guard-->>Router: Skip further group handlers
end
else Command in private chat
Guard-->>Router: next(ctx)
Router->>Bot: Execute normal DM handler
Bot-->>DM: Reply in DM
end
Class diagram for bot modules: group guard, onboarding, and admin tooltip importclassDiagram
class BotApp {
+start()
+useMiddleware(middleware)
+registerCommand(name, handler)
}
class GroupCommandGuardMiddleware {
+handleUpdate(ctx)
-isGroupChat(ctx) bool
-getCommand(ctx) string
-isWhitelistedGroupCommand(command) bool
-buildDeepLink(userId, payload) string
}
class OnboardingService {
+showOnboardingPrompt(ctx, user, step)
+onStepCompleted(ctx, user, step)
+maybeShowCompletionCard(ctx, user)
-onboardingProgressBar(step) string
}
class UserState {
+id string
+onboarding_step number
+onboarding_finished boolean
+completion_card_shown boolean
}
class AdminSystemTools {
+showSystemToolsMenu(ctx)
+handleSystemToolsCallback(ctx, action)
}
class GroupLinkingTools {
+showGroupLinkingMenu(ctx)
+handleGroupLinkingCallback(ctx, action)
}
class TooltipImportService {
+importFromJsonArray(ctx, jsonText)
-parseJsonArray(jsonText) TooltipEntry[]
-validateEntry(entry) boolean
-saveEntries(entries) string[]
}
class TooltipEntry {
+id string
+trigger string
+text string
+metadata object
}
BotApp --> GroupCommandGuardMiddleware : uses
BotApp --> OnboardingService : uses
BotApp --> AdminSystemTools : uses
AdminSystemTools --> GroupLinkingTools : opens
AdminSystemTools --> TooltipImportService : uses
TooltipImportService --> TooltipEntry : creates
OnboardingService --> UserState : reads_writes
GroupCommandGuardMiddleware --> UserState : reads
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
✨ 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 1 issue, and left some high level feedback:
- The tooltip generator still relies on
new Functionto evaluateDEFAULT_TIPS_LIST; if possible, consider requiring this literal to be valid JSON (or parsing via a JS parser/AST and extracting the array) to avoid executing arbitrary code from a compromisedindex.js. - For the onboarding flow, now that you support 5 steps, it may be worth centralizing the total step count in a single constant used by both the progress bar rendering and any step-transition logic to avoid future off-by-one or mismatch issues.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The tooltip generator still relies on `new Function` to evaluate `DEFAULT_TIPS_LIST`; if possible, consider requiring this literal to be valid JSON (or parsing via a JS parser/AST and extracting the array) to avoid executing arbitrary code from a compromised `index.js`.
- For the onboarding flow, now that you support 5 steps, it may be worth centralizing the total step count in a single constant used by both the progress bar rendering and any step-transition logic to avoid future off-by-one or mismatch issues.
## Individual Comments
### Comment 1
<location path="generate_tooltips.sh" line_range="44-45" />
<code_context>
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); }
+// Sanity-check the matched literal before evaluating (must look like an array)
+if (m[1].length > 500000 || !/^\s*\[/.test(m[1])) {
+ process.stderr.write('Unexpected DEFAULT_TIPS_LIST shape — aborting\n');
+ process.exit(1);
+}
</code_context>
<issue_to_address>
**suggestion:** Consider making the size guard more intentional and visible rather than a hard‑coded magic number.
The `500000` byte limit works as a safeguard, but as a bare literal it’s hard to understand and adjust later. Consider extracting it to a named constant (e.g. `MAX_TIPS_LITERAL_LENGTH`) or basing it on `src.length` so future tuning and debugging of “shape” failures is clearer.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| if (m[1].length > 500000 || !/^\s*\[/.test(m[1])) { | ||
| process.stderr.write('Unexpected DEFAULT_TIPS_LIST shape — aborting\n'); |
There was a problem hiding this comment.
suggestion: Consider making the size guard more intentional and visible rather than a hard‑coded magic number.
The 500000 byte limit works as a safeguard, but as a bare literal it’s hard to understand and adjust later. Consider extracting it to a named constant (e.g. MAX_TIPS_LITERAL_LENGTH) or basing it on src.length so future tuning and debugging of “shape” failures is clearer.
Nitpicks 🔍
|
| # 3c) Kill anything blocking the bot port before starting | ||
| # --------------------------------------------------------- | ||
| DEPLOY_PORT="$(grep -E '^PORT=' "$PROJECT_DIR/.env" 2>/dev/null | head -1 | cut -d= -f2 | tr -d '"' | tr -d "'" | tr -d $'\r')" | ||
| DEPLOY_PORT="$(grep -E '^PORT=' "$PROJECT_DIR/.env" 2>/dev/null | head -1 | cut -d= -f2 | cut -d'#' -f1 | tr -d '"' | tr -d "'" | tr -d ' ' | tr -d $'\r')" |
There was a problem hiding this comment.
Suggestion: In the new port-unblocking step, grep is run unconditionally on $PROJECT_DIR/.env under set -euo pipefail, so if .env is missing or unreadable the command substitution can abort the entire deploy script before the service is restarted, potentially leaving the bot down instead of cleanly defaulting to port 3000. [possible bug]
Severity Level: Major ⚠️
- ⚠️ Deploy script can fail if .env is absent.
- ⚠️ Admins receive failure alerts despite code updating successfully.
- ⚠️ Port-unblocking becomes a hard dependency on .env presence.| DEPLOY_PORT="$(grep -E '^PORT=' "$PROJECT_DIR/.env" 2>/dev/null | head -1 | cut -d= -f2 | cut -d'#' -f1 | tr -d '"' | tr -d "'" | tr -d ' ' | tr -d $'\r')" | |
| if [[ -f "$PROJECT_DIR/.env" ]]; then | |
| DEPLOY_PORT="$(grep -E '^PORT=' "$PROJECT_DIR/.env" 2>/dev/null | head -1 | cut -d= -f2 | cut -d'#' -f1 | tr -d '"' | tr -d "'" | tr -d ' ' | tr -d $'\r')" | |
| fi |
Steps of Reproduction ✅
1. On the VPS where `deploy.sh` runs, go to the project directory `"$PROJECT_DIR"`
referenced at `deploy.sh:26` and temporarily remove or rename `.env` so that
`$PROJECT_DIR/.env` does not exist or is unreadable.
2. Trigger a deploy using this script (per usage comment at `deploy.sh:5-11`), for example
by running `bash /var/www/html/Runewager/deploy.sh bot` or invoking the GitHub/bot
integration that calls this script.
3. The script proceeds through step 1 (service stop at `deploy.sh:136-143`), step 2 (git
update at `deploy.sh:153-174`), and step 3 (npm install at `deploy.sh:183-212`)
successfully.
4. When it reaches the port-unblocking block at `deploy.sh:232-251`, the unguarded command
substitution `DEPLOY_PORT="$(grep -E '^PORT=' "$PROJECT_DIR/.env" …)"` at `deploy.sh:235`
executes; because `.env` is missing, `grep` exits with status 2, which under `set -euo
pipefail` (line `24`) triggers the `ERR` trap `_on_error` at `deploy.sh:88-102`, causing
the main deploy flow to abort before the normal "Step 4/4 — Starting bot via systemctl…"
path at `deploy.sh:256-261` runs and leading to a deploy reported as failed due solely to
the missing `.env` file.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** deploy.sh
**Line:** 235:235
**Comment:**
*Possible Bug: In the new port-unblocking step, `grep` is run unconditionally on `$PROJECT_DIR/.env` under `set -euo pipefail`, so if `.env` is missing or unreadable the command substitution can abort the entire deploy script before the service is restarted, potentially leaving the bot down instead of cleanly defaulting to port 3000.
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. |
User description
Summary by Sourcery
Harden tooltip generation and align onboarding documentation with the expanded step range.
Enhancements:
CodeAnt-AI Description
Redirect group commands to DM; show onboarding progress and one-time completion card; add batch tooltip import and safer startup flows
What Changed
Impact
✅ Fewer accidental group-triggered commands✅ Clearer onboarding progress and a visible welcome after setup✅ Fewer failed starts due to blocked bot ports💡 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.