A single Go binary that runs your personal Telegram account (MTProto user client) and an admin control bot in one process. When someone DMs you outside your working hours (default 09:30–19:00 local), it auto-replies, mutes the sender, and logs the event. At the start of working hours (and on startup) it unmutes everyone again. You toggle the whole feature on/off from a private bot.
- User client — gotgproto (over gotd/td), acts as you.
- Control bot — telego (Bot API), answers only to your admin user ID.
- Storage — one small SQLite table (pure Go, no cgo).
Pick one of:
- Docker + Docker Compose (recommended), or
- Go 1.25+ to build/run locally.
You need four things. Put them in .env (step 4).
- Go to https://my.telegram.org and log in with your phone number.
- Open API development tools.
- Create an app (any title/short-name). Copy
api_idandapi_hash.
The phone number of the account you want to automate, in international format, e.g. +15551234567.
- In Telegram, open @BotFather.
- Send
/newbot, follow the prompts, and copy the bot token (123456:ABC...). - Open a chat with your new bot and press Start so it can message you.
- Message @userinfobot (or @myidbot) in Telegram.
- Copy your numeric user ID. This is the only account the control bot obeys.
git clone https://github.com/yiromo/tg-afterhours.git
cd tg-afterhoursCopy the example and fill it in:
cp .env.example .envEdit .env:
API_ID=123456
API_HASH=your_api_hash
PHONE=+15551234567
BOT_TOKEN=123456:ABC-your-bot-token
ADMIN_ID=7258936037 # your numeric user ID from step 2d
TIMEZONE=Europe/Moscow # IANA name — required for correct local-time
WORK_START=09:30
WORK_END=19:00
REPLY_TEXT=I'm currently outside my working hours and will get back to you during the day.
TIMEZONEmatters. Working hours are evaluated in this timezone. Use an IANA name (Europe/Moscow,America/New_York, …), not an offset.
The user client must authenticate once. This is interactive (Telegram sends a login code to your account), so the first run needs a terminal.
docker compose run --rm afterhoursYou'll be prompted for:
- the login code Telegram sends you (in the Telegram app), and
- your 2FA password if you have one enabled.
After a successful login the session is saved to ./data/session.db. Press Ctrl+C once you see it running, then start it detached:
docker compose up -dSubsequent restarts reuse the saved session — no code needed.
go run ./cmd/appSet DB_PATH and SESSION_PATH to local paths first if you don't want them under /data, e.g.:
DB_PATH=./data/afterhours.db SESSION_PATH=./data/session.db go run ./cmd/app(Environment variables can come from your shell; the binary itself doesn't read .env — Docker Compose loads it for you.)
Open the chat with your bot (the one from step 2c) and send:
| Command | Action |
|---|---|
/start or /admin |
Show the menu |
/on |
Enable auto-reply |
/off |
Disable auto-reply |
/analytics or /stats |
List recent senders who were auto-replied & muted |
/schedule |
Show the working-hours window for each weekday |
/set <day> <HH:MM> <HH:MM> |
Set a day's working hours, e.g. /set mon 09:30 19:00 |
/set <day> off |
Mark a day off (auto-reply applies all day) |
/settext <text> |
Set the auto-reply text (send with no argument to view the current one) |
/resettext |
Restore the default auto-reply text (REPLY_TEXT) |
/setlimit <n> |
Max auto-replies per sender per day (default 3; no argument shows the current value) |
/ignore <@username | phone | id> |
Exempt a sender from auto-reply & muting |
/unignore <@username | phone | id> |
Remove the exemption |
Days are mon–sun. Every day starts from the .env window (WORK_START/WORK_END); per-day changes made with /set are saved to the database and survive restarts.
Auto-reply text. The message sent to people defaults to REPLY_TEXT from .env. Change it at runtime with /settext <text> (it's saved to the database and survives restarts) and revert to the env default with /resettext.
Ignore list. Senders you /ignore never get an auto-reply or mute. The argument can be @username, a phone (+77001234567), or a numeric user id; usernames/phones are resolved to a stable user id when added (they must be resolvable at that moment). The /start//admin menu has an Ignored list button that shows current entries with a tap-to-remove button each.
Auto-ignore people you message. When you send a DM to someone, they're automatically added to the ignore list and unmuted — you're clearly active in that chat, so they won't get the after-hours auto-reply and you'll keep seeing their messages. Remove them later with /unignore or the menu if you want the rules to apply again.
The bot ignores everyone except ADMIN_ID. Auto-reply is ON by default when the process starts.
- A DM arrives outside
WORK_START–WORK_ENDand the feature is ON → you reply (up to the per-day limit, default 3, per sender), the sender is muted, and a row is logged. Once a sender hits the daily cap they stay muted but get no further replies until the next day. - Inside working hours → nothing happens; a background check (every minute, and on startup) unmutes anyone still muted.
- Only the minimum is stored per sender:
user_id, username, message_text, muted_at, muted. No message history or media.
go build ./... # build
go test ./... # run tests
CGO_ENABLED=0 go build -o app ./cmd/app # static binary (matches the Docker build)The binary is static (no cgo); both SQLite uses are pure Go. See CLAUDE.md for architecture details.
Everything lives under ./data (mounted to /data in the container):
session.db— MTProto login session (keep it; deleting it forces re-login).afterhours.db— the mute registry / analytics table.