| config | ||
| handlers | ||
| patches | ||
| pkg/utils | ||
| scripts | ||
| telegram-join-approval-bot@98e6a4a100 | ||
| .gitignore | ||
| .gitmodules | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
Telegram Join Approval Bot
A Telegram bot for managing and approving join requests to private groups with approval workflows, canned responses, and admin controls.
Overview
This bot automates the process of approving new members joining a private Telegram group. When a user requests to join, the bot:
- Sends them a prompt to explain why they want to join
- Forwards their request to the admin chat with approve/decline/ban buttons
- Allows admins to manage requests with customizable decline reasons
- Notifies users of the approval decision
Features
- Automated Join Requests: Intercepts and manages join requests to your target group
- Admin Approval Workflow: Approve, decline, or ban users with a single click
- Join Reason Collection: Users must provide a reason before their request reaches admins
- Canned Responses: Pre-configured decline messages for common rejection reasons
- Flexible Admin Interface:
- Approve, decline, or ban users
- Add custom decline reasons
- Configure bot messages and settings
- Organize requests in topic threads
- Approval Notifications: Optional message sent to approved users
- Configurable Messages: Customize entry and approval messages
- User Banning: Ban users for 24 hours with a single action
How It Works
Join Request Flow
User requests to join group
↓
Bot sends entry prompt to user
↓
Bot notifies admins with join request
(showing user info and join reason placeholder)
↓
User sends their join reason
(message to bot in private chat)
↓
Bot updates admin message with user's reason
↓
Admin clicks Approve/Decline/Ban
↓
User notified of decision (if configured)
↓
User added/rejected/banned from group
Architecture
The bot consists of several key components:
Main Entry Point (main.go)
- Initializes the bot with config from
config.yaml - Starts the long polling update loop
- Routes updates to appropriate handlers
Configuration (config/config.go)
- Loads settings from
config.yaml - Auto-generates template config if missing
- Supports hot-reloading configuration changes
Handlers
join.go: Manages join request flow and user responsescallbacks.go: Handles inline button actions (approve/decline/ban)admin.go: Processes admin commands for configurationhandlers.go: Shared types and utilities
Key Data Structures
// ExtendedChatJoinRequest tracks a user's approval request
type ExtendedChatJoinRequest struct {
*ChatJoinRequest // Telegram join request
JoinReason string // User's explanation
JoinRequestMessageID int // Admin notification message ID
}
// Bot maintains runtime state
type Bot struct {
API BotAPI // Telegram API client
Config Config // Configuration
WaitingForApproval map // In-memory user request tracking
}
Configuration
The bot reads from config.yaml in its working directory. On first run, it creates a template:
bot_token: "YOUR_BOT_TOKEN_HERE" # @BotFather token
admin_chat_id: 0 # Chat ID where admins review requests
admin_chat_topic_id: 0 # Optional: Topic ID in admin chat (0 = main)
target_chat_id: 0 # Private group to protect
entry_message: "Please explain why..." # Prompt shown to joining users
approval_message: "" # Message sent after approval (optional)
send_approval_message: false # Whether to send approval message
delete_request_after_decision: false # Auto-delete admin messages after decision
canned_decline_responses: # Pre-configured decline reasons
- "Your profile doesn't meet our criteria"
- "We're at capacity"
Getting Required IDs
Bot Token: Create a bot with @BotFather
Chat IDs:
# Forward a message from the chat to @getidsbot
# It will show you the chat ID
Admin Commands
Admins can use these commands in the admin chat:
/setentrymessage <message>
Change the message shown when users request to join.
/setentrymessage Tell us about yourself and why you want to join!
/setapprovalmessage <message>
Set a message sent to approved users. Must be set before enabling approval notifications.
/setapprovalmessage Welcome to our group! We're excited to have you.
/togglesendapproval
Enable/disable sending the approval message to approved users.
/setadmintopic <topic_id>
Direct admin messages to a specific topic thread (0 = main chat).
/setadmintopic 42
/info
Display current bot configuration.
/edit <new_text>
Reply to a message and use this to edit it. Useful for correcting admin notifications.
/edit User was banned incorrectly
Approval Workflow
Admin Actions
When a user requests to join, an admin sees:
New join #request from @username [123456789]
Join reason: "I'm interested in this community"
With inline buttons:
- Approve - Add user to group, optionally send approval message
- Decline - Reject request, show decline reason button panel
- Ban - Ban user for 24 hours (shows confirmation)
Decline Responses
When declining with canned responses configured:
- Admin clicks Decline
- Message shows available canned responses as buttons
- Admin clicks a canned response
- User receives the selected reason
- Admin message updates with the reason
Or, admin can reply to a decline message with custom text (with or without / prefix):
[Reply to decline message]
/We can only accept members with 2+ years experience
Deployment
Docker
Build and run the container:
docker build -t telegram-approval-bot .
docker run -it \
--mount type=bind,source=$PWD/config.yaml,target=/opt/config.yaml \
telegram-approval-bot
The Dockerfile:
- Builds a static binary with minimal dependencies
- Uses Alpine for small image size
- Runs as unprivileged user (UID 65532)
- Mounts config as a volume for persistence
Compose Example
version: '3'
services:
bot:
build: .
volumes:
- ./config.yaml:/opt/config.yaml
restart: unless-stopped
Implementation Details
State Management
Join requests are tracked in memory using a mutex-protected map:
WaitingForApproval map[int64]*ExtendedChatJoinRequest
When a user provides their join reason, the bot:
- Updates the request with their reason
- Edits the admin message to show the reason
- Keeps the message indexed by message ID for button handling
Update Processing
The bot receives Telegram updates via long polling:
- ChatJoinRequest: User requests to join → calls
HandleJoinRequest - CallbackQuery: Admin clicks button → calls
HandleCallbackQuery - Message: User sends reason or admin command → routes accordingly
Message Formatting
Admin notifications use HTML formatting with:
- User display name with link
- Numeric user ID
- Join reason (HTML-escaped for safety)
- Admin action history
- Timestamp information
Error Handling
- Failed API requests restore the original message
- Missing user state after restart shows helpful error
- Invalid callback data is logged without crashing
Development
Dependencies
github.com/OvyFlash/telegram-bot-api- Telegram Bot API clientgo.yaml.in/yaml/v3- YAML configuration parsing
Go Version
Requires Go 1.25.3+
Building Locally
go build -o telegram-approval-bot ./...
./telegram-approval-bot
Troubleshooting
Bot doesn't respond to join requests
- Verify
target_chat_idis correct - Check bot has admin rights in the target group
- Ensure bot is configured to receive join request updates
Admin messages don't appear
- Verify
admin_chat_idis correct - Check bot has message permission in admin chat
- If using topics, verify
admin_chat_topic_idis set
Approve/Decline buttons don't work
- Bot needs admin rights in target group
- Join request may have expired (Telegram timeout)
- Check logs for API errors
Config changes don't apply
- Some changes require bot restart
- Use
/infoto verify current settings - Check config.yaml for syntax errors
License
MIT License - Copyright (c) 2026 astra.blue