Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c3ce323
feat(v3.0): full production upgrade — menu lifecycle, pagination, sec…
claude Feb 28, 2026
1c1d19c
Merge remote-tracking branch 'origin/main' into claude/plan-v3-deploy…
claude Feb 28, 2026
92e93e6
fix(codex-recovery): restore missing codex branch changes overwritten…
claude Feb 28, 2026
3d4befe
fix(pr110): address all reviewer comments from PR #110
claude Feb 28, 2026
41c2083
feat(vnext): Helpful Tooltips overhaul, giveaway v3.0+ upgrades
claude Feb 28, 2026
c279f5f
feat(v3.1): group command guard, onboarding progress bar, admin group…
claude Feb 28, 2026
81ff2af
fix(pr112+audit): address all 6 PR review comments and 4 audit duplic…
claude Feb 28, 2026
86a6e1d
merge: sync with origin/main; keep PR #112 fixes over regressed versions
claude Feb 28, 2026
b6e211c
feat(scripts): auto git-pull + tooltip-refresh + port-free before eve…
claude Feb 28, 2026
5c27692
fix(pr113): resolve all PR #113 review comments
claude Feb 28, 2026
efe20d6
docs: add per-feature documentation system and central index
claude Mar 1, 2026
6ab6734
merge: sync with origin/main (PRs #112-114); keep SIGTERM→SIGKILL and…
claude Mar 1, 2026
49efa74
fix(pr115): resolve 3 review comments + implement T-01/T-02/T-03/T-15
claude Mar 1, 2026
ee1184c
fix(pr115): tooltip skip guard, shared port helper, gated auto-update
claude Mar 1, 2026
ad306be
fix(pr115): address 4 reviewer hardening findings
claude Mar 1, 2026
c865189
fix(pr115): 3 nitpick hardening fixes
claude Mar 1, 2026
f0f53aa
fix(pr115): temp-file trap + non-destructive git update path
claude Mar 1, 2026
9108134
Merge remote-tracking branch 'origin/main' into claude/plan-v3-deploy…
claude Mar 4, 2026
8307907
fix(audit): remove dead adminKeyboard() + merge main + docs sync
claude Mar 4, 2026
f0bcd13
fix(prod-run): fallback on systemctl failure, fix disown, drop parse_…
claude Mar 4, 2026
3f9ae8f
fix(group-guard): ignore commands not owned by this bot in groups
claude Mar 4, 2026
b516247
docs(contract): mandate BOT_KNOWN_COMMANDS sync on every command add/…
claude Mar 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ Every coding session must begin by reading RUNEWAGER_FUNCTIONALITY_MAP.md and mu

All AI agent instruction files in this repo must enforce this baseline workflow: (1) read and understand `RUNEWAGER_FUNCTIONALITY_MAP.md` before changing code, (2) update the map after any code change, (3) run a full verification pass after the map update, and (4) do not consider work complete until docstrings, the map, and agent instruction files are synchronized.

**BOT_KNOWN_COMMANDS (mandatory):** Whenever a `bot.command()` registration is added or removed from `index.js`, the `BOT_KNOWN_COMMANDS` set (defined near the Group Command Guard middleware, ~line 279) **must be updated in the same change set**. This set is the authoritative list of commands Runewager owns; it gates the group command guard so the bot never intercepts commands belonging to other bots in shared groups. Forgetting to update it will either cause Runewager to silently ignore its own new command in groups, or continue intercepting a removed command. No command addition or removal is complete until `BOT_KNOWN_COMMANDS` is synchronized.

- Added operational script `load_tooltips.sh` (root) to populate `/var/www/html/Runewager/data/tooltips.json` with 15 approved HTML tooltips; bot now loads this system file on restart when present.
- Added 30 SC manual-review menu hardening with explicit user/admin submenus and admin audit logging to `/var/www/html/Runewager/logs/bonus_admin.log`.

Expand Down
14 changes: 11 additions & 3 deletions RUNEWAGER_FUNCTIONALITY_MAP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RUNEWAGER_FUNCTIONALITY_MAP.md

_Last audited: 2026-02-28 (v3.1 pass)_
_Last audited: 2026-03-04 (/runewager-audit pass — 0 critical, 0 warnings after fix)_
_Source of truth files: `index.js`, `test/*.test.js`, scripts under `scripts/`, deployment/runtime docs in repo root._

---
Expand Down Expand Up @@ -288,10 +288,14 @@ Pending-action timeout handling is enforced in the text input router: each pendi
## 22. Internal Utilities & Shared Modules

Repo modules:
- `index.js` main app.
- `index.js` main app (15,148 lines after v3.1 merge).
- `telegramSafe.js` — rate-limited Telegram API wrapper; patches `bot.telegram` on init.
- `rateLimiter.js` — global (~28 req/sec) + per-chat (1 msg/sec) queue enforcer.
- `backend.js` — companion HTTP service on port 3001: autofix webhook, admin bridge, `/health/full`.
- `promo-message.js` promo copy helper.
- `scripts/*.sh` deployment/runtime ops (backup, restore, smoke, rollback, diagnostics).
- `scripts/*.sh` deployment/runtime ops (backup, restore, smoke, rollback, diagnostics, redeploy, cpu-guard).
- `test/*.test.js` smoke/unit/runtime tests.
- `runewager-endpoint.service` — systemd unit for `backend.js` (separate from `runewager.service`).

Key shared helper families in `index.js`:
- Markdown escape helpers.
Expand Down Expand Up @@ -397,11 +401,15 @@ Mandatory rules for any AI agent touching this repo:

2. **After generating or modifying any functionality, the AI must run a follow-up audit to ensure the `RUNEWAGER_FUNCTIONALITY_MAP.md` file is fully updated and accurate. No coding session is complete until the map is updated and verified.**

3. **BOT_KNOWN_COMMANDS (mandatory):** Whenever a `bot.command()` registration is added or removed from `index.js`, the `BOT_KNOWN_COMMANDS` set (defined near the Group Command Guard middleware, ~line 279) **must be updated in the same change set**. This set gates the group command guard — it is the authoritative list of commands this bot owns. Forgetting to update it will cause either silent group-command failure (new command ignored in groups) or continued interception of a removed command. No `bot.command()` add/remove is complete until `BOT_KNOWN_COMMANDS` is synchronized.

- 2026-02-26: Added `load_tooltips.sh` to seed `/var/www/html/Runewager/data/tooltips.json` (15 UTF-8 HTML tooltips) and wired runtime tooltip loading to prefer that system JSON on restart.
- 2026-02-26: Added deterministic 30 SC user submenu (`How It Works`, `Check My Eligibility`, `Request My Bonus`, `Check Bonus Status`) and Admin submenu (`View Pending Requests`, `Approve Bonus`, `Deny Bonus`, `View User History`, `Reset Attempts`) with manual-review copy and admin action logging to `/var/www/html/Runewager/logs/bonus_admin.log`.
- 2026-02-27: Added QA tester scaffolding (`qa/context/bot_capabilities.json`, `qa/context/repo_info.json`, `qa/state/provider_status.json`, `qa/README_QA.md`), with runtime refresh via `/qa_*` commands and 10-minute provider cooldown reset.
- 2026-02-27: Hardened SSHV Run prompt flow so admin text in private DM executes against active SSHV sessions if pending state desynchronizes.
- 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 after 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).
- 2026-03-04: Merged main branch additions — `telegramSafe.js` (global rate-limit patch via `telegramSafe.init(bot)`), `rateLimiter.js` (per-chat + global Telegram API rate limiter), `backend.js` (companion HTTP service on port 3001, `runewager-endpoint.service` systemd unit), updated `prod-run.sh`, `scripts/runewager_redeploy.sh`, `scripts/rw_cpu_guard.sh`.
- 2026-03-04: /runewager-audit — 17-phase full audit. Findings: 0 critical, 1 warning resolved (dead `adminKeyboard()` function removed — legacy promo keyboard with no callers). Auto-fix applied. 60/60 tests pass post-fix.
- 2026-03-01: Created `docs/` feature documentation system — 15 per-feature `.md` files, central `docs/INDEX.md` with full callback + pending-action cross-reference, and `docs/TODO_FUNCTIONALITY_UPGRADE.md` tracking 14 open upgrade/stale-menu items. Future Claude sessions must consult `docs/INDEX.md` first, then the relevant feature `.md`, before reading `index.js`.
- 2026-03-01: Phase implementation — resolved T-01/T-02/T-03/T-15 from TODO list. (1) Walkthrough: `sendWalkthroughStep()` upgraded with `clearOldMenus()`, Back disabled on step 1, Finish on last step, `walk_done` on last step returns to main menu. New doc: `16-walkthrough.md`. (2) Menu stacking: `clearOldMenus()` added to `sendOnboardingReferralPrompt`, `renderSshvConsole`, `renderGroupLinkingTools`, `tips_cmd_edit`, `tips_cmd_remove`. (3) Tooltip view: `tips_cmd_view` selector + `tip_view_{id}` handler with Prev/Next/Edit/Toggle/Delete/Back/AdminMenu — "👁 View Tooltip" button added to dashboard. (4) Broadcast failures: 500-item cap removed; all failures logged via `adminLog()`; `/broadcast_failed` shows chunks of 30 with overflow note; >20% failure rate triggers admin DM warnings. PR comments fixed: `add_tooltip.sh` array validation hardened; `docs/12-group-linking.md` entry-point callback corrected to `admin_sys_group_linking`; merge conflicts (PRs #112-114) resolved keeping SIGTERM→SIGKILL safety improvements. 60/60 tests pass.
- 2026-02-28: PR #112 review + audit pass — fixed 10 issues: (R1) `await_tip_import_batch` dedicated pending type with JSON-array router; (R2) `generate_tooltips.sh` command-substitution pollution fixed via `RUNEWAGER_APP` env var; (R3) `add_tooltip.sh` shell-injection fixed via `TOOLTIP_TEXT_ENV`/`TOOLTIP_TMP_FILE` env vars and `<<'EOF'`; (R4) `catchAllCases` test extended with multiline patterns + `CATCH_ALL_CORES` updated; (R5) `extractCommandHandlerNames` test extended with `let`/`var`/no-semicolon fixtures; (R6) typo "auto-deletes 8s" → "auto-deletes after 8s"; (A1) dead `buildGiveawayAnnouncementText(giveaway,remainingStr)` removed; (A2) simplified `buildGiveawayAnnouncementKeyboard` with wrong callback removed; (A3+A4) duplicate `bot.action('admin_cat_system')` and `bot.action('admin_cat_support')` first registrations removed. All 60 tests pass.
Expand Down
73 changes: 37 additions & 36 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,30 @@ bot.catch((err, ctx) => {
// =========================

/**
* Commands that have explicit group-aware logic and should NOT be intercepted.
* All other commands sent in a group get a "DM redirect" response.
* Complete set of commands this bot handles.
* Any command NOT in this list is silently ignored in groups (could belong to another bot).
*/
const BOT_KNOWN_COMMANDS = new Set([
'start', 'menu', 'help', 'commands', 'settings', 'language',
'link', 'linkrunewager', 'walkthrough',
'admin', 'sshv', 'qa_on', 'qa_off', 'qa_mode', 'qa_status',
'a', 'announce', 'giveaway', 'start_giveaway', 'cancel',
'wager30_admin', 'admin_backup', 'deploy', 'whois', 'bonusstatus', 'refreshuser',
'health', 'admin_notify', 'deploy_status', 'logs', 'version', 'resolvebug', 'exportbugs',
'bonus', 'startapp', 'claim_history', 'profile', 'leaderboard', 'leaderboard_weekly',
'boost_referrals', 'linkaccount', 'status', 'referral', 'on', 'off',
'bugreport', 'bugreports', 'play', 'signup', 'affiliate', 'discord',
'promo', 'setpromo', 'join', 'pmapprove', 'pmdeny',
'tips', 't', 'tp', 'tiplist', 'tipadd', 'tipremove', 'tipedit', 'tiptoggle', 'tiptest', 'tipsettings',
'testall', 'testgiveaway',
'gw_pause', 'gw_resume', 'scan_eligibility', 'funnel',
'broadcast_retry', 'broadcast_failed', 'pick_winner',
'register_chat', 'verify_bot_setup', 'approve_group', 'unapprove_group', 'list_groups',
]);

/**
* Commands that have explicit group-aware logic and should NOT be redirected to DM.
* Must be a subset of BOT_KNOWN_COMMANDS.
*/
const GROUP_PASSTHROUGH_COMMANDS = new Set([
'link', 'linkrunewager', // handled: group username inline confirm
Expand All @@ -291,8 +313,19 @@ bot.use(async (ctx, next) => {
const text = ctx.message.text;
if (!text.startsWith('/')) return next();

// Extract command name, stripping bot @mention and arguments
const rawCmd = text.slice(1).split(/[\s@]/)[0].toLowerCase();
// Extract command name and optional @mention (e.g. "/warn@otherbot" → "warn", "otherbot")
const cmdToken = text.slice(1).split(/\s/)[0]; // "warn@otherbot" or "warn"
const atIdx = cmdToken.indexOf('@');
const rawCmd = (atIdx === -1 ? cmdToken : cmdToken.slice(0, atIdx)).toLowerCase();
const mentionedBot = atIdx === -1 ? '' : cmdToken.slice(atIdx + 1).toLowerCase();

// If the command targets a specific bot that is not us, ignore it entirely
const ourUsername = (ctx.botInfo?.username || '').toLowerCase();
if (mentionedBot && mentionedBot !== ourUsername) return next();

// If we don't own this command, ignore it (prevents responding to other bots' commands)
if (!BOT_KNOWN_COMMANDS.has(rawCmd)) return next();

if (GROUP_PASSTHROUGH_COMMANDS.has(rawCmd)) return next();

// Redirect all other commands to DM
Expand Down Expand Up @@ -2944,38 +2977,6 @@ function linkPrefKeyboard() {
]);
}

/**

* adminKeyboard executes its scoped Runewager logic and participates in menu/command or utility flow composition.

* Parameters: See the function signature for exact argument names and accepted values.

* Returns: Returns the computed value or a Promise resolving to the operation result; may return void for side-effect handlers.

* Side effects: May mutate runtime stores, pendingAction state, menu state, persistence files, logs, and callback progression.

* Validation/safety: Uses existing guard utilities (admin checks, input checks, path checks, cooldown checks) where applicable.

* Timeouts/fallbacks: Timeout and fallback behavior are controlled by the calling flow and global handler/state machine conventions.

* Errors: Surfaces user-facing error replies and/or logs when inputs, permissions, or dependencies are invalid.

* System fit: This function is part of the Runewager command/callback/state orchestration pipeline.

*/

function adminKeyboard() {
return Markup.inlineKeyboard([
[Markup.button.callback('📄 View Promo', 'admin_view')],
[Markup.button.callback('✏️ Edit Promo Code', 'admin_edit_code')],
[Markup.button.callback('💰 Edit SC Amount', 'admin_edit_amount')],
[Markup.button.callback('🔢 Edit Claim Limit', 'admin_edit_limit')],
[Markup.button.callback('⏸ Pause Promo', 'admin_pause')],
[Markup.button.callback('▶️ Unpause Promo', 'admin_unpause')],
[Markup.button.callback('🗑 Remove Promo', 'admin_remove')],
[Markup.button.callback('📢 Push Update to Users', 'admin_broadcast')],
]);
}

/**

Expand Down
24 changes: 16 additions & 8 deletions prod-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -499,11 +499,18 @@ if [[ -n "$PID" ]]; then
say "Safe restart of running bot (PID: $PID)"
fi
if command -v systemctl >/dev/null 2>&1 && [[ -f "$SERVICE_FILE" ]]; then
systemctl restart "${APP_NAME}.service" 2>/dev/null || true
if ! systemctl restart "${APP_NAME}.service" 2>/dev/null; then
warn "systemctl restart failed — falling back to manual kill+nohup"
[[ -n "$PID" ]] && kill "$PID" 2>/dev/null || true
sleep 1
nohup node "$PROJECT_DIR/index.js" >> "$MAIN_LOG" 2>> "$ERROR_LOG" < /dev/null &
disown || true
say "Bot started via nohup fallback"
fi
else
[[ -n "$PID" ]] && kill "$PID" 2>/dev/null || true
nohup node "$PROJECT_DIR/index.js" >> "$MAIN_LOG" 2>> "$ERROR_LOG" < /dev/null &
disown
disown || true
fi
# Always refresh PID after restart so step 11 knows the live process
sleep 3
Expand Down Expand Up @@ -546,17 +553,18 @@ fi

is_port_listening "$HEALTH_PORT" && PORT_STATUS="listening"

# Telegram admin notification
# Telegram admin notification (plain text — no parse_mode to avoid Markdown rendering issues
# with unescaped log content or special chars in env values)
_ADMIN_IDS="$(read_env_value ADMIN_IDS || true)"
_BOT_TOKEN="$(read_env_value TELEGRAM_BOT_TOKEN || true)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The deploy notification now reads only TELEGRAM_BOT_TOKEN from .env, but the project treats BOT_TOKEN as the canonical name and TELEGRAM_BOT_TOKEN as a legacy alias, so in environments where only BOT_TOKEN is set (the default), Telegram deploy notifications will silently never send. [logic error]

Severity Level: Major ⚠️
- ⚠️ Prod-run Telegram deploy summaries never send with only BOT_TOKEN.
- ⚠️ Admins lose automated deploy/health visibility; must check logs manually.
Suggested change
_BOT_TOKEN="$(read_env_value TELEGRAM_BOT_TOKEN || true)"
_BOT_TOKEN="$(read_env_value BOT_TOKEN || true)"
[[ -z "$_BOT_TOKEN" ]] && _BOT_TOKEN="$(read_env_value TELEGRAM_BOT_TOKEN || true)"
Steps of Reproduction ✅
1. Configure `.env` in the project root (`/workspace/Runewager/.env`) using `.env.example`
as a template (`.env.example:12` shows `BOT_TOKEN=`, `:37` shows `TELEGRAM_BOT_TOKEN=`)
and set a valid `BOT_TOKEN` plus a comma‑separated `ADMIN_IDS` (as documented by
`README.md:15` and `:26`), but leave `TELEGRAM_BOT_TOKEN` unset or empty.

2. Run the deployment runner `prod-run.sh` (the main production deploy script whose
post‑deploy health + Telegram reporting logic starts around the `# 11) POST-START HEALTH
CHECK + TELEGRAM REPORT` section in `prod-run.sh`), so it reaches the end of the script
after starting/restarting the bot.

3. At the Telegram notification block in `prod-run.sh` lines `558–563`, `_ADMIN_IDS` is
populated from `.env` via `read_env_value ADMIN_IDS` (line `558`), but `_BOT_TOKEN` is set
only from `read_env_value TELEGRAM_BOT_TOKEN` (line `559`), which returns an empty string
because only `BOT_TOKEN` is defined in `.env`.

4. Because `_BOT_TOKEN` is empty, the guard `if [[ -n "$_BOT_TOKEN" && -n "$_ADMIN_IDS"
]]; then` (line `560`) evaluates to false, the `curl` loop to
`https://api.telegram.org/bot${_BOT_TOKEN}/sendMessage` (lines `564–567`) is never
executed, and no Telegram deploy/health notification is sent to any `ADMIN_IDS`, despite
the project treating `BOT_TOKEN` as canonical (see `index.js:14` and documentation notes
in `CLAUDE_FIX_PR.md:2474–2483`).
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** prod-run.sh
**Line:** 559:559
**Comment:**
	*Logic Error: The deploy notification now reads only `TELEGRAM_BOT_TOKEN` from `.env`, but the project treats `BOT_TOKEN` as the canonical name and `TELEGRAM_BOT_TOKEN` as a legacy alias, so in environments where only `BOT_TOKEN` is set (the default), Telegram deploy notifications will silently never send.

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.
👍 | 👎

if [[ -n "$_BOT_TOKEN" && -n "$_ADMIN_IDS" ]]; then
_REPORT="$(( tail -n 30 "$MAIN_LOG"; tail -n 20 "$ERROR_LOG" ) 2>/dev/null \
| sed 's/"/\\"/g' | head -c 3500)"
_HEALTH_LABEL="$( [[ "$HEALTH_STATUS" == healthy ]] && echo OK || echo FAILED )"
_MSG="Deploy complete: Health ${_HEALTH_LABEL} | Port: ${HEALTH_PORT} | PID: ${PID:-none} | Systemd: ${SYSTEMD_ACTIVE}"
for _AID in ${_ADMIN_IDS//,/ }; do
curl -s -X POST "https://api.telegram.org/bot${_BOT_TOKEN}/sendMessage" \
-d chat_id="$_AID" \
-d parse_mode="Markdown" \
-d text="Deploy complete: Health $( [[ "$HEALTH_STATUS" == healthy ]] && echo OK || echo FAILED) Port: $HEALTH_PORT PID: ${PID:-none}" >/dev/null 2>&1 || true
--data-urlencode "chat_id=${_AID}" \
--data-urlencode "text=${_MSG}" \
>/dev/null 2>&1 || true
done
fi

Expand Down
5 changes: 4 additions & 1 deletion todolist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Runewager Bot — Improvement Task Board

_Last updated: 2026-02-28PR #112 review fixes + codebase audit pass_
_Last updated: 2026-03-04/runewager-audit pass + merge main (backend.js, rateLimiter.js, telegramSafe.js, prod-run.sh, scripts)_

---

Expand All @@ -11,6 +11,9 @@ _Last updated: 2026-02-28 — PR #112 review fixes + codebase audit pass_

## BUGS (confirmed code defects)

- [x] **Dead code: `adminKeyboard()` function** `index.js`
- Fixed: 2026-03-04. Legacy promo keyboard function with no callers (belonged to removed `/admin_menu` command). Deleted entire block (JSDoc + function body). All 60 tests pass.

- [x] **`gw.endsAt` / `endTime` field inconsistency** `index.js`
- Fixed: all three occurrences replaced with `gw.endTime`; extend action now calls `resetGiveawayTimer(gw)` to re-arm the timer.
- Also fixed `gw.winnersCount` → `gw.maxWinners` in the admin panel display.
Expand Down