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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion RUNEWAGER_FUNCTIONALITY_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,13 @@ The bot uses `user.pendingAction.type` as its input state machine. Key families:
- **Forward registration**: `await_register_chat_forward`.

### Timeouts
- No global pendingAction auto-timeout was found for most flows.
- Global pending-action timeout now enforced in text flow: `PENDING_ACTION_TIMEOUT_MS = 15 minutes`; expired wait states are reset with a user-facing timeout message and `Main Menu` recovery buttons.
- SSHV sessions have TTL-based GC (`SSHV_SESSION_TTL_MS`), with stale-state normalization and recovery.

## 8. Fallback Logic & Error Handling

- Permission failures: `requireAdmin` + `sendCommandError`.
- Pending slash escapes: when a pending state exists, `/cancel` clears state; `/menu`, `/start`, and `/help` clear pending state and return user to main menu flow.
- Invalid pending input: explicit prompts to retry (numeric checks, yes/no checks, IDs).
- Missing entities (promo/giveaway not found): user-visible explanatory replies.
- Announce/test flows return actionable follow-up messages.
Expand Down Expand Up @@ -323,6 +324,7 @@ Admin starts wizard (gwiz)
- 2026-02-26: Added repo-wide baseline docstrings to named function declarations (runtime, helper, and test modules) and re-verified command/callback coverage.
- 2026-02-26: Re-verified removal of `/language` command/callback surface, reran smoke/full tests, and synchronized agent-contract wording in `CLAUDE.md` with map-first/update-map/verify requirements.
- 2026-02-26: Completed a menu-integrity pass by adding explicit `Main Menu` + `Cancel` exits to admin category keyboards and settings submenu, then revalidated smoke/full tests.
- 2026-02-26: Added global 15-minute pending-action timeout and slash-command escape fallbacks (`/cancel`, `/menu`, `/start`, `/help`) for wait-for-input state safety.

## 25. AI Coder Contract Requirements

Expand Down
112 changes: 72 additions & 40 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,8 @@ const NON_USERNAME_WORDS = new Set([
]);

// Slash commands implemented in this file. Used for unknown-command fallback.
const PENDING_ACTION_TIMEOUT_MS = 15 * 60 * 1000; // 15m timeout for wait-for-input states

const REGISTERED_COMMANDS = new Set([
'A',
'a',
Expand Down Expand Up @@ -4178,46 +4180,6 @@ function escapeMarkdownFull(text) {
return String(text).replace(/[_*[\]()~`>#+\-=|{}.!\\]/g, '\\$&');
}

/**

* ensureDataDir 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.

*/

/**

* ensureDataDir 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.

*/

/**

* ensureDataDir executes its scoped Runewager logic and participates in menu/command or utility flow composition.
Expand Down Expand Up @@ -7792,6 +7754,50 @@ bot.action('admin_cat_support', async (ctx) => {
});


/**


* adminTestsToolsKeyboard 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.


*/


bot.action('admin_cat_system', async (ctx) => {
if (!requireAdmin(ctx)) return;
await ctx.answerCbQuery();
const user = getUser(ctx);
await replaceCallbackPanel(ctx, '⚙️ *System Tools*', { parse_mode: 'Markdown', ...adminSystemToolsKeyboard(user) });
});

bot.action('admin_cat_support', async (ctx) => {
if (!requireAdmin(ctx)) return;
await ctx.answerCbQuery();
await replaceCallbackPanel(ctx, '🛟 *Support Tools*', { parse_mode: 'Markdown', ...adminSupportToolsKeyboard() });
});


/**


Expand Down Expand Up @@ -9010,6 +9016,32 @@ bot.on('text', async (ctx) => {

const text = (ctx.message.text || '').trim();

if (user.pendingAction && !user.pendingAction.createdAt) user.pendingAction.createdAt = Date.now();

if (user.pendingAction && (Date.now() - Number(user.pendingAction.createdAt || 0)) > PENDING_ACTION_TIMEOUT_MS) {
const expiredType = user.pendingAction.type || 'current action';
user.pendingAction = null;
await ctx.reply(
`⏱️ Your pending step (${expiredType}) timed out after 15 minutes. Please start again from the menu.`,
Markup.inlineKeyboard([[Markup.button.callback('🏠 Main Menu', 'to_main_menu')], [Markup.button.callback('❌ Cancel', 'to_main_menu')]]),
);
return;
}

if (user.pendingAction && text.startsWith('/')) {
const cmd = text.slice(1).split(/\s+/)[0].split('@')[0].toLowerCase();
if (cmd === 'cancel') {
user.pendingAction = null;
await ctx.reply('✅ Pending action cancelled.', Markup.inlineKeyboard([[Markup.button.callback('🏠 Main Menu', 'to_main_menu')]]));
return;
}
if (cmd === 'menu' || cmd === 'start' || cmd === 'help') {
user.pendingAction = null;
await sendMainMenu(ctx, user, 1);
return;
}
}

if (!user.pendingAction && text.startsWith('/')) {
const cmd = text.slice(1).split(/\s+/)[0].split('@')[0];
if (!REGISTERED_COMMANDS.has(cmd)) {
Expand Down
Loading