diff --git a/index.js b/index.js index e560c9a..f2a5abb 100644 --- a/index.js +++ b/index.js @@ -138,6 +138,14 @@ let lastPersistAt = 0; // Absolute path to the project root (same directory as this file) const PROJECT_DIR = path.resolve(__dirname); +/** + * Sanitize a string for safe embedding inside a Telegram Markdown code span. + * Strips backticks (which would break the span) and truncates to maxLen chars. + */ +function sanitizeCmdOutput(str, maxLen = 200) { + return str.replace(/`/g, "'").slice(0, maxLen); +} + /** * Run a shell command and resolve with { ok, out, err }. * Never throws — callers decide what to do with failures. @@ -1233,8 +1241,8 @@ bot.command('deploy', async (ctx) => { // ── Step 1: git pull ────────────────────────────────────────── const gitResult = await runCmd('git', ['pull', 'origin', 'main'], PROJECT_DIR, 30000); const gitLine = gitResult.ok - ? `✅ *git pull:* \`${gitResult.out.split('\n')[0].slice(0, 200)}\`` - : `⚠️ *git pull failed:* \`${gitResult.err.slice(0, 200)}\``; + ? `✅ *git pull:* \`${sanitizeCmdOutput(gitResult.out.split('\n')[0])}\`` + : `⚠️ *git pull failed:* \`${sanitizeCmdOutput(gitResult.err)}\``; await edit( `🚀 *Deployment in progress* — ${ts()}\n\n` @@ -1245,16 +1253,27 @@ bot.command('deploy', async (ctx) => { const npmResult = await runCmd('npm', ['ci', '--omit=dev'], PROJECT_DIR, 180000); const npmLine = npmResult.ok ? `✅ *npm ci:* OK` - : `⚠️ *npm ci:* \`${npmResult.err.slice(0, 200)}\``; + : `⚠️ *npm ci:* \`${sanitizeCmdOutput(npmResult.err)}\``; const allOk = gitResult.ok && npmResult.ok; - const summary = allOk ? '✅ All steps succeeded' : '⚠️ Some steps had warnings — check below'; + + // ── Abort if any step failed — never restart into broken code ─ + if (!allOk) { + await edit( + `🚀 *Deployment summary* — ${ts()}\n\n` + + `${gitLine}\n` + + `${npmLine}\n\n` + + `⚠️ One or more steps failed — restart *aborted* to avoid loading broken code.\n` + + `③ Restart *skipped*.`, + ); + return; + } await edit( `🚀 *Deployment summary* — ${ts()}\n\n` + `${gitLine}\n` + `${npmLine}\n\n` - + `${summary}\n\n` + + `✅ All steps succeeded\n\n` + `③ Restarting bot now…`, ); @@ -2507,7 +2526,7 @@ bot.on('text', async (ctx) => { if ( text && !text.startsWith('/') && - /^[A-Za-z0-9_.\\-]{3,30}$/.test(text) && + /^[A-Za-z0-9_.-]{3,30}$/.test(text) && !user.runewagerUsername ) { const normalized = normalizeRunewagerUsername(text);