feat: add language-specific code execution commands

- Add /js, /python, /py, /ts, /bash, /sh commands for quick code execution
- Add /run <lang> <code> for any supported language
- Update /status to show sandbox backend (docker/piston/none)
- Update /start and /help with new command documentation
- Update AI system prompts to know about available commands
  - Bot now guides users to use /js, /python etc. when they ask to run code
  - Fixes issue where AI was hallucinating non-existent commands

Supported languages via Piston API fallback:
python, javascript, typescript, bash, rust, go, c, cpp, java, ruby, php

https://claude.ai/code/session_015VqJ7gN4vaxtYfYc92UjLs
This commit is contained in:
Claude 2026-01-30 08:17:04 +00:00
parent 095d476acc
commit 64e840849f
No known key found for this signature in database
3 changed files with 208 additions and 31 deletions

View File

@ -46,7 +46,7 @@ const DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-20250514";
const DEFAULT_OPENAI_MODEL = "gpt-4o";
const DEFAULT_OPENROUTER_MODEL = "anthropic/claude-3.5-sonnet";
const DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant running as a secure, self-hosted bot.
const DEFAULT_SYSTEM_PROMPT = `You are AssureBot, a helpful AI assistant running as a secure Telegram bot.
You are direct, concise, and helpful. You can:
- Answer questions and have conversations
@ -54,7 +54,17 @@ You are direct, concise, and helpful. You can:
- Help with coding and technical tasks
- Summarize content and extract information
When you receive webhook notifications, summarize them helpfully for the user.
## Available Commands (tell users about these when relevant)
- /js <code> - Run JavaScript
- /python <code> - Run Python
- /ts <code> - Run TypeScript
- /bash <code> - Run shell commands
- /run <lang> <code> - Run code in any language (python, js, ts, bash, rust, go, c, cpp, java, ruby, php)
- /status - Check bot status
- /clear - Clear conversation history
When users ask to run or test code, guide them to use the appropriate command.
Example: "Use /js console.log('hello')" or "Try /python print('hello')"
Be security-conscious:
- Never reveal API keys, tokens, or secrets

View File

@ -96,7 +96,7 @@ export async function createPersonality(storage: Storage): Promise<Personality>
async getSystemPrompt(userId: number): Promise<string> {
const profile = await loadUserProfile(userId);
let prompt = `You are ${traits.name}, a helpful AI assistant.
let prompt = `You are ${traits.name}, a helpful AI assistant running as a Telegram bot.
## Personality
- Tone: ${profile.preferredTone}
@ -112,11 +112,28 @@ ${traits.expertiseAreas.map(e => `- ${e}`).join("\n")}
- Recent topics: ${profile.recentTopics.length > 0 ? profile.recentTopics.slice(-3).join(", ") : "None yet"}
${profile.notes.length > 0 ? `- Notes: ${profile.notes.slice(-3).join("; ")}` : ""}
## Available Commands (you can tell users about these)
- /js <code> - Run JavaScript code
- /python <code> or /py <code> - Run Python code
- /ts <code> - Run TypeScript code
- /bash <code> or /sh <code> - Run shell commands
- /run <language> <code> - Run code in any supported language (python, javascript, typescript, bash, rust, go, c, cpp, java, ruby, php)
- /status - Check bot and sandbox status
- /clear - Clear conversation history
- /schedule "<cron>" "<name>" <prompt> - Schedule recurring AI tasks
- /tasks - List scheduled tasks
- /deltask <id> - Delete a task
When a user asks to run code, you can either:
1. Tell them to use the appropriate command (e.g., "Use /js console.log('hello')")
2. Just answer their question directly if they don't need to execute code
## Guidelines
- Be helpful, accurate, and security-conscious
- Never reveal API keys, tokens, or secrets
- Adapt to the user's communication style
- Remember context from this conversation
- When users want to run code, guide them to use the right command
${traits.commonPhrases.length > 0 ? `- Phrases you like: ${traits.commonPhrases.join(", ")}` : ""}
${traits.avoidPhrases.length > 0 ? `- Avoid saying: ${traits.avoidPhrases.join(", ")}` : ""}`;

View File

@ -73,19 +73,25 @@ export function createTelegramBot(deps: TelegramDeps): TelegramBot {
You are authorized to use this bot.
Commands:
/start - Show this message
/clear - Clear conversation history
Code Execution:
/js <code> - Run JavaScript
/python <code> - Run Python
/ts <code> - Run TypeScript
/bash <code> - Run shell commands
/run <lang> <code> - Run any language
Other Commands:
/status - Check bot status
/sandbox <code> - Run code in sandbox
/schedule <cron> <task> - Schedule a task
/clear - Clear conversation history
/schedule - Schedule AI tasks
/tasks - List scheduled tasks
/help - Show help
/help - Show full help
Features:
- Send text messages to chat
- Send images for analysis
- Forward content for analysis`
- Chat with AI
- Image analysis (send photos)
- Document analysis (send PDFs)
- Code execution (15+ languages)`
);
});
@ -108,11 +114,15 @@ Features:
}
const history = conversations.get(userId);
const sandboxStatus = sandbox
? `${sandbox.backend} (${await sandbox.isAvailable() ? "ready" : "unavailable"})`
: "not configured";
await ctx.reply(
`Status:
- AI Provider: ${agent.provider}
- Conversation: ${history.length} messages
- Sandbox: ${config.sandbox.enabled ? "enabled" : "disabled"}
- Sandbox: ${sandboxStatus}
- Webhooks: ${config.webhooks.enabled ? "enabled" : "disabled"}
- Scheduler: ${config.scheduler.enabled ? "enabled" : "disabled"}`
);
@ -130,27 +140,32 @@ Features:
A secure, self-hosted AI assistant.
Features:
- Chat with AI (text messages)
- Image analysis (send photos)
- Forward content for analysis
- Run code in isolated sandbox
- Schedule recurring AI tasks
CODE EXECUTION:
/js <code> - Run JavaScript
/python <code> or /py <code> - Run Python
/ts <code> - Run TypeScript
/bash <code> or /sh <code> - Run shell
/run <lang> <code> - Run any language
Commands:
/start - Welcome message
/clear - Clear conversation history
/status - Bot status
/sandbox <code> - Run code in sandbox
/schedule "<cron>" "<name>" <prompt> - Schedule task
/tasks - List scheduled tasks
/deltask <id> - Delete a task
Supported: python, javascript, typescript, bash, rust, go, c, cpp, java, ruby, php
SCHEDULING:
/schedule "<cron>" "<name>" <prompt>
/tasks - List tasks
/deltask <id> - Delete task
Example: /schedule "0 9 * * *" "Morning" Good morning!
OTHER:
/status - Bot & sandbox status
/clear - Clear conversation
/help - This message
Security:
- Only authorized users can interact
- All interactions are logged
- Sandbox runs in isolated Docker (no network)`
FEATURES:
- Chat naturally with AI
- Send images for analysis
- Send PDFs/docs for analysis
- Code runs in isolated sandbox`
);
});
@ -206,6 +221,141 @@ Security:
}
});
// Helper for language-specific code execution
async function runCodeCommand(
ctx: Context,
language: string,
commandName: string
): Promise<void> {
const userId = ctx.from?.id;
if (!userId || !isUserAllowed(userId, config.telegram.allowedUsers)) {
return;
}
if (!sandbox) {
await ctx.reply("Sandbox is not configured.");
return;
}
const isAvailable = await sandbox.isAvailable();
if (!isAvailable) {
await ctx.reply(`Sandbox unavailable. Backend: ${sandbox.backend}`);
return;
}
const code = ctx.message?.text?.replace(new RegExp(`^/${commandName}\\s*`), "").trim() ?? "";
if (!code) {
await ctx.reply(`Usage: /${commandName} <code>\n\nExample: /${commandName} console.log("Hello!")`);
return;
}
await ctx.replyWithChatAction("typing");
try {
const result = await sandbox.runCode(language, code);
const output = result.stdout || result.stderr || "(no output)";
const status = result.exitCode === 0 ? "✓" : `✗ (exit ${result.exitCode})`;
const timeout = result.timedOut ? " [TIMED OUT]" : "";
const backend = sandbox.backend === "piston" ? " [Piston]" : "";
await ctx.reply(
`**${language}** ${status}${timeout}${backend}\n\`\`\`\n${output.slice(0, 3000)}\n\`\`\`\nDuration: ${result.durationMs}ms`,
{ parse_mode: "Markdown" }
).catch(async () => {
await ctx.reply(`${language} ${status}${timeout}${backend}\n\n${output.slice(0, 3500)}\n\nDuration: ${result.durationMs}ms`);
});
audit.sandbox({
command: `[${language}] ${code.slice(0, 100)}`,
exitCode: result.exitCode,
durationMs: result.durationMs,
});
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
audit.error({ error: `Code execution error: ${errorMsg}`, metadata: { userId, language, code } });
await ctx.reply(`Error: ${errorMsg}`);
}
}
// Command: /js <code> - Run JavaScript
bot.command("js", (ctx) => runCodeCommand(ctx, "javascript", "js"));
// Command: /python <code> - Run Python
bot.command("python", (ctx) => runCodeCommand(ctx, "python", "python"));
bot.command("py", (ctx) => runCodeCommand(ctx, "python", "py"));
// Command: /ts <code> - Run TypeScript
bot.command("ts", (ctx) => runCodeCommand(ctx, "typescript", "ts"));
// Command: /bash <code> - Run Bash
bot.command("bash", (ctx) => runCodeCommand(ctx, "bash", "bash"));
bot.command("sh", (ctx) => runCodeCommand(ctx, "bash", "sh"));
// Command: /run <language> <code> - Run code in any supported language
bot.command("run", async (ctx) => {
const userId = ctx.from?.id;
if (!userId || !isUserAllowed(userId, config.telegram.allowedUsers)) {
return;
}
if (!sandbox) {
await ctx.reply("Sandbox is not configured.");
return;
}
const isAvailable = await sandbox.isAvailable();
if (!isAvailable) {
await ctx.reply(`Sandbox unavailable. Backend: ${sandbox.backend}`);
return;
}
const text = ctx.message?.text?.replace(/^\/run\s*/, "").trim() ?? "";
const match = text.match(/^(\w+)\s+([\s\S]+)$/);
if (!match) {
await ctx.reply(
`Usage: /run <language> <code>
Supported languages:
- javascript, js
- typescript, ts
- python, py
- bash, sh
- rust, go, c, cpp, java, ruby, php
Example: /run python print("Hello!")`
);
return;
}
const [, language, code] = match;
await ctx.replyWithChatAction("typing");
try {
const result = await sandbox.runCode(language, code);
const output = result.stdout || result.stderr || "(no output)";
const status = result.exitCode === 0 ? "✓" : `✗ (exit ${result.exitCode})`;
const timeout = result.timedOut ? " [TIMED OUT]" : "";
const backend = sandbox.backend === "piston" ? " [Piston]" : "";
await ctx.reply(
`**${language}** ${status}${timeout}${backend}\n\`\`\`\n${output.slice(0, 3000)}\n\`\`\`\nDuration: ${result.durationMs}ms`,
{ parse_mode: "Markdown" }
).catch(async () => {
await ctx.reply(`${language} ${status}${timeout}${backend}\n\n${output.slice(0, 3500)}\n\nDuration: ${result.durationMs}ms`);
});
audit.sandbox({
command: `[${language}] ${code.slice(0, 100)}`,
exitCode: result.exitCode,
durationMs: result.durationMs,
});
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
audit.error({ error: `Code execution error: ${errorMsg}`, metadata: { userId, language, code } });
await ctx.reply(`Error: ${errorMsg}`);
}
});
// Command: /schedule <cron> <name> <prompt>
bot.command("schedule", async (ctx) => {
const userId = ctx.from?.id;