Skip to content

About

AI-powered .NET Telegram bot that drives the agy (Google Antigravity) CLI agent via a Rust ACP adapter.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

244 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pacos Telegram Bot

master branch - test, build, push, deploy Ask DeepWiki

Pacos is a .NET-based Telegram bot designed to interact in group chats. It drives the agy (Google Antigravity) CLI agent through the agy-acp ACP adapter to produce chat responses, run tools, and deliver generated files.

The alpaca was scientifically described by Carl Linnaeus in his System of Nature (1758) under the Latin name Camelus pacos.

scr1

Features

  • AI-Powered Chat: Responds to mentions (e.g., "pacos", "пакос") or direct messages by driving the agy agent over ACP. Each chat gets its own working directory holding a persona steering file (AGENTS.md) and a set of agy Agent Skills (.agents/skills/<skill>/SKILL.md) that carry the tool-specific instructions and are loaded on demand instead of on every turn.
  • Image Generation: Generate or modify images via the !drawx <prompt> command. An optional source image can be supplied with the command or by replying to a message that contains one.
  • Movie Releases: Answers "what is new in cinemas" per chat. The movieadvisor MCP server asks the movieadvisor-api sidecar which current releases this chat has not been offered yet; the sidecar refreshes the catalogue on its own daily schedule and remembers what it handed out, so nothing is suggested twice. The chat it answers for comes from TELEGRAM_CHAT_ID, injected per chat into the agent process — never from a tool argument the model could get wrong.
  • Web Search: The websearch MCP server hands the agent a single web_search / web_extract tool pair backed by several search providers (Exa, Parallel, Tavily, Linkup, Brave, Firecrawl) with automatic failover, instead of one tool per provider. It is web-search-plus-mcp running in the websearch-mcp sidecar behind an mcp-proxy stdio-to-Streamable-HTTP bridge (both resolved by uvx from the official uv image, no custom build); agy reaches it via serverUrl over the compose network. Provider keys are the sidecar's environment variables (EXA_API_KEY, PARALLEL_API_KEY, TAVILY_API_KEY, LINKUP_API_KEY, BRAVE_API_KEY, FIRECRAWL_API_KEY; every one optional), the provider order and routing preferences are websearch-mcp.config.json.
  • File Delivery: The agent can return generated files (images, documents, etc.) by moving them into a per-turn output directory via the filemcp MCP server, which the bot then forwards back to the user.
  • Chat Management:
    • Reset History: Users can clear the agent's session for a specific chat with the !resetx command.
  • Language Identification: Detects the language of incoming messages to tailor responses (using NTextCat with Core14.profile.xml).
  • Sandboxed Execution: An enforced agy permission policy (written at startup) denies all shell commands and restricts the agent's file access to its per-chat workspace and the brain staging dir; file delivery is handled by the filemcp MCP server, not the shell. The real isolation boundary is the container (non-root user, restricted volume).
  • Asynchronous Processing: Handles incoming Telegram updates and agent interactions asynchronously using a background task queue to ensure responsiveness.

Core Technologies

  • Framework: .NET (Worker Service)
  • Telegram API: Telegram.Bot library
  • AI Agent: agy (Google Antigravity CLI), driven via the agy-acp ACP adapter
  • Logging: NLog (configured via nlog.config)
  • Configuration: Standard .NET configuration (e.g., appsettings.json, environment variables)
  • Language Detection: NTextCat

Configuration

The bot reads its settings from environment variables or an appsettings.json file under the Pacos section.

Required:

  • TelegramBotApiKey: Your Telegram Bot API token.
  • AllowedChatIds: An array of Telegram chat IDs where the bot is permitted to operate.
  • ChatModel: The model name written into the agy permission policy (e.g. Gemini 3.5 Flash (High)).

Optional:

  • FallbackChatModel: Model retried once when the primary model fails with a quota error (HTTP 429 / RESOURCE_EXHAUSTED). Pick one billed against a different quota pool than ChatModel (e.g. Claude Sonnet 4.6 (Thinking) when the primary is a Gemini model); the value must exactly match a label from agy models. Empty disables the fallback.
  • WebhookUrl: Public HTTPS URL for receiving updates via a Telegram webhook (e.g. https://pacos.example.com/telegram/webhook). The bot registers it on startup with a per-run secret token and serves the URL's path on its HTTP port (8080 in the Docker image), so a reverse proxy must forward that path unchanged. Empty (default) deletes any registered webhook and uses long polling.
  • AgyAcpCommand: Executable used to spawn the agy-acp adapter (default: agy-acp).
  • AgyAcpArgs: Extra command-line arguments passed to the agy-acp process.
  • WorkingDirectoryRoot: Root directory under which per-chat working directories are created (default: a folder under the system temp directory).
  • AgyExtraArgs: Extra arguments forwarded to every underlying agy invocation (via AGY_EXTRA_ARGS). Must not contain --print-timeout (startup validation rejects it); use PromptTimeoutSeconds.
  • GeminiApiKey: Optional Gemini API key passed to the agy subprocess for non-interactive auth. When empty, agy relies on its own persisted OAuth credentials (e.g. ~/.gemini).
  • PromptTimeoutSeconds: Per-turn budget (default: 300). agy-acp ends a turn that runs this long and reports it as timed out (anything produced so far is still delivered; a turn with nothing usable fails once, without retries). agy's --print-timeout (+5%) and the Pacos session/prompt watchdog (+15%, and always at least 10s beyond the budget for the adapter's cleanup) are derived from it, so they can never fire first.
  • SessionIdleTimeoutMinutes: When a chat has been idle (no prompt to the agent) for longer than this, the next prompt starts a fresh session instead of resuming — and paying the tokens for — the stale conversation (default: 180; 0 disables).
  • AgyCommandRuleMode: Which set of agy command-permission rules to write (denyall (default) or off).

Setup and Running

  1. Ensure you have the .NET SDK and the agy / agy-acp executables available on PATH.
  2. Configure the required settings (see Configuration section).
  3. Create Core14.profile.xml (for NTextCat language identification) in the application's root directory.
  4. Run the application:
    dotnet run

When running in Docker, the agy state directory (/home/agent/.gemini) should be backed by a named volume so the agent's OAuth credentials and state persist across deployments (see the deploy workflow). The movieadvisor-api sidecar keeps its SQLite database on the movieadvisor-state volume for the same reason: without it every chat's movie history resets on redeploy and the whole cinema line-up looks new again.

The Docker image ships a patched agy: Pacos/scripts/patch_agy_recovery_notice.py (run in the agy-patcher stage of Pacos/Dockerfile) locates cortex.(*CascadeManager).sendRecoveryMessage in the Go binary by signature and turns it into a ret, so agy stops appending the "[Notice] All your subagents and background tasks have been stopped due to server restart" step to the context on every resumed turn. The build fails if the signature is not found; after bumping AGY_VERSION re-check the patcher (a failing image build is the signal).

In production the webhook is reached through nginx on the host, which terminates TLS and proxies the webhook path to the container's port published on 127.0.0.1:${PACOS_HTTP_PORT} (docker-compose.deploy.yml). Set the PACOS__WEBHOOKURL and PACOS_HTTP_PORT variables of each GitHub environment (a distinct port per environment) to enable it; leaving PACOS__WEBHOOKURL empty keeps that environment on long polling.

For local development the web search provider keys go into a git-ignored .env file next to docker-compose.yml (EXA_API_KEY=..., one per line); in production the deploy workflow exports them from GitHub secrets of the same names. To change the provider order, regenerate websearch-mcp.config.json with the aggregator's own CLI and commit the result, e.g. uvx web-search-plus-mcp==4.1.1 config --config-path websearch-mcp.config.json set-priority exa,parallel,tavily,linkup,brave,firecrawl.

Bot Commands

  • pacos, <message>: Engage in a conversation with the agent.
  • !drawx <prompt>: Generate an image based on the provided text prompt (optionally using an attached or replied-to image as the source).
  • !resetx: Reset the agent's session for the current chat.

About

AI-powered .NET Telegram bot that drives the agy (Google Antigravity) CLI agent via a Rust ACP adapter.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages