Instruction file imported from zuckdorsey/KDE-BOT (
.github/instructions/copilot.instructions.md). Copyright stays with the author.
AI agent guide for KDE-BOT
Purpose: help an AI coding agent be productive immediately in this repo by capturing the real architecture, conventions, and gotchas.
Big picture
- Two local components on the same machine:
- Telegram bot (Python, aiogram) in
bot/— UI via Reply Keyboard, long polling. - Local client (Python, Flask) in
client/— executes OS-level actions. Auth via Bearer token.
- Telegram bot (Python, aiogram) in
- Flow: Telegram ->
bot/bot.pyhandlers ->bot/client.SystemClientHTTP ->client/server.py/commanddispatcher -> OS commands -> JSON back to bot. - Single-user by design: only
OWNER_IDis authorized at the bot layer; Flask enforcesAUTH_TOKEN.
Key files and roles
bot/bot.py: main entry. Uses Reply Keyboard (not inline), registers message handlers, appliesErrorMiddleware, and enforces owner auth (authorize). UsesCommandManager.run_exclusive(...)to cancel previous in-flight tasks per chat.bot/client.py(SystemClient): async HTTP client with retries/backoff and consistent error messages. All requests includeAuthorization: Bearer <AUTH_TOKEN>.bot/command_manager.py: per-chat exclusivity for commands; always wrap long-running tasks with it to avoid duplicate work.bot/utils.py: safe message edits/deletes,chat_actioncontext, andresult_iconhelper.client/server.py: Flask app. Endpoints:/(health),/status,/command(switch oncommand),/upload,/getfile,/download/<filename>.- Command names implemented today:
lock,volume,mute,copy,paste,screenshot(Linux),sleep,shutdown,battery_status,network_info,network_stats. - Security:
require_authchecks Bearer token; server binds toHOST(default 127.0.0.1). - File download is restricted to
ALLOWED_DOWNLOAD_DIRS(defaults touploads/andscreenshots/).
- Command names implemented today:
- Legacy/alt UI:
bot/handlers/*andbot/keyboards.pyimplement an inline-keyboard flow not used bybot/bot.py. Current UI is the Reply Keyboard inbot/bot.py; unknown callbacks are handled bybot/fallbacks.pyto redirect users to/start.
Configuration and running
- Both sides use dotenv. Create
bot/.envandclient/.env;AUTH_TOKENmust match exactly.- Bot:
BOT_TOKEN,OWNER_ID,CLIENT_URL,AUTH_TOKEN, optionalLOG_LEVEL,REQUEST_TIMEOUT. - Client:
HOST,PORT,AUTH_TOKEN,UPLOAD_DIR,SCREENSHOT_DIR.
- Bot:
- First run requires system packages for features used by
client/server.py:- Linux:
scrot(screenshots),alsa-utils(volume),xclip/xsel(clipboard), plus Python deps from eachrequirements.txt.
- Linux:
- Typical dev loop: start Flask client first (
client/server.py), then the bot (bot/bot.py). The bot will surface clear errors if the client is down (fromSystemClient).
Implementation patterns that matter here
- Bot auth and exclusivity:
- Always gate handlers with
authorize(message)as inbot/bot.pyto enforceOWNER_ID. - For actions that can be spammed or take time (screenshot, sleep, volume), wrap the work in
CommandManager.run_exclusive(chat_id=..., coro_factory=..., on_cancel=...).
- Always gate handlers with
- Bot-to-client contract:
- POST
/commandwith JSON{ "command": "<name>", "params": { ... } }and expect JSON{ status: "success"|"error", message: string, ... }. - Example implemented calls in bot:
client.send_command('screenshot'),client.send_command('volume', { 'level': 50 }).
- POST
- Client command handler:
- Add cases inside
execute_command(command, params)and return the standard JSON shape above. - Keep OS-specific branches (Linux/Windows/macOS) together like the existing commands.
- Add cases inside
- Messaging UX:
- Prefer Reply Keyboard buttons in
bot/bot.py(main_keyboard,system_keyboard, etc.). Make text matches exact labels, e.g.'🔒 Lock Screen'. - Use
utils.safe_edit/safe_deleteto avoid Telegram edit/delete errors, andchat_actionto show typing/upload indicators.
- Prefer Reply Keyboard buttons in
Gotchas and guardrails
- AUTH is mandatory in both layers: missing or mismatched
AUTH_TOKENyields 401 from Flask;SystemClientsurfaces helpful messages (e.g., "Python client not running..."). - Screenshots on Linux need a valid
DISPLAY; server defaults to:0if missing, but ensure X session is available;scrotmust be installed. - File download via
/getfileis limited touploads/andscreenshots/by default; tests outside those paths will 403. - Some inline handlers reference commands like
process_*ormedia_*that are not currently implemented inclient/server.py. Either implement them under/commandor avoid wiring those routes when using the Reply Keyboard UI.
Adding a new feature (concrete example)
- Client (
client/server.py):- Inside
execute_command, add:elif command == 'reboot':run the appropriate OS command andreturn { 'status': 'success', 'message': '🔁 Rebooting...' }.
- Inside
- Bot (
bot/bot.py):- Add a button label (e.g.,
'🔁 Reboot') in the appropriate keyboard, register a handler with anauthorizecheck, and inside arun_exclusive(...)callclient.send_command('reboot')then edit the message usingresult_icon.
- Add a button label (e.g.,
When in doubt
- Follow the patterns in
bot/bot.pyandclient/server.py; keep the JSON contract and auth behavior consistent. - Prefer the Reply Keyboard path and exclusive command manager; treat the inline keyboard router files as legacy/optional.
- Keep OS commands inside the Flask side; the bot should remain async/IO-only.