feat(telegram): ADMIN_USER_ID may /impersonate another user

While on, the admin's updates are routed as the target user's (allow-list,
chat, workspace, session, settings, jobs) and the target chat's output goes
to the admin's chat. The target's own updates are dropped with a notice that
an admin is working with their account. In memory; /impersonate 0 stops.
This commit is contained in:
EugeneTes
2026-10-05 07:24:07 +00:00
parent 64ea6f7fa1
commit 92a0061fdd
20 changed files with 398 additions and 38 deletions

View File

@@ -29,6 +29,7 @@ settings use the `GoogleCalendar` section, the scheduler the `Scheduler` section
|---|---|---|
| `TELEGRAM_BOT_TOKEN` / `Telegram:BotToken` | — | Required |
| `TELEGRAM_ALLOWED_USER_IDS` / `Telegram:AllowedUserIds` | — | Required, comma separated. The bot refuses to start without it |
| `ADMIN_USER_ID` / `Telegram:AdminUserId` | — (off) | The Telegram user id that may use `/impersonate` (see [Impersonation](#impersonation)). Need not be on the allow-list. Must be positive |
| `Telegram:WorkspaceRoot` | `~/.volcanic/telegram-workspaces` | Chat `<id>` works in `<root>/<id>`; put `AGENTS.md`, `PERSONA.md`, `MEMORY.md` there |
| `Telegram:Model` | `claude-code/claude-opus-5-5` | Any `provider/model` the Claude Code provider serves |
| `Telegram:EditInterval` | `00:00:01.5` | Minimum time between edits of the "Working..." status message |
@@ -205,6 +206,22 @@ the current format, and keeps the original next to it as `jobs.json.secretary-<t
Copy the workspace's `scripts/` into the owner's workspace too: watcher commands such as `python scripts/check.py` run
from there. A one-off that is already due fires on the first start.
### Impersonation
The admin (`ADMIN_USER_ID`) can act as another user with `/impersonate <user_id>`; `/impersonate 0` (or the admin's
own id) stops. The command shows up in the admin's command menu only, and from anyone else it is an ordinary unknown
command passed to the agent.
While it is on, the bot treats the admin's messages, commands, button presses and answers exactly as the target user's:
the allow-list check (impersonating someone outside it means the bot ignores the admin until they stop), the chat, its
workspace, session, time zone, jobs and calendar. Everything the target's chat sends goes to the admin's chat instead:
replies, questions, files, notices and scheduled deliveries, including runs started by the target user's own messages.
Meanwhile the target user is blocked: whatever they send (messages, commands, button presses) is dropped, and the bot
answers "🔒 An admin is working with your account right now. Please try again later." The admin's own chat (their own jobs, for
example) keeps working as before.
Impersonation is kept in memory only: a restart ends it.
### Google Calendar
Each chat connects its own Google account with `/calendar_connect`, and the agent's `calendar` tool only uses that
@@ -215,7 +232,8 @@ storage and time zones are in [google-calendar.md](google-calendar.md).
## Security
The agent has the built-in tools, including `bash`, and runs them as the bot's OS user. Only allow-listed user ids
are served (everyone else is ignored and logged), and only in private chats. Run it in the container image
are served (everyone else is ignored and logged), and only in private chats. `ADMIN_USER_ID` can act as any
allow-listed user with `/impersonate`, so it gets that user's workspace, sessions, jobs and connected calendar. Run it in the container image
([docker.md](docker.md)), which is the recommended setup, or at least as a dedicated user, and treat the workspace root
as writable by anyone on the allow-list.