# 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: 1. Sends them a prompt to explain why they want to join 2. Forwards their request to the admin chat with approve/decline/ban buttons 3. Allows admins to manage requests with customizable decline reasons 4. 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 responses - **`callbacks.go`**: Handles inline button actions (approve/decline/ban) - **`admin.go`**: Processes admin commands for configuration - **`handlers.go`**: Shared types and utilities #### Key Data Structures ```go // 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: ```yaml 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](https://t.me/botfather) **Chat IDs**: ```bash # 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 ` Change the message shown when users request to join. ``` /setentrymessage Tell us about yourself and why you want to join! ``` ### `/setapprovalmessage ` 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 ` Direct admin messages to a specific topic thread (0 = main chat). ``` /setadmintopic 42 ``` ### `/info` Display current bot configuration. ### `/edit ` 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: 1. Admin clicks **Decline** 2. Message shows available canned responses as buttons 3. Admin clicks a canned response 4. User receives the selected reason 5. 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: ```bash 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 ```yaml 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: ```go WaitingForApproval map[int64]*ExtendedChatJoinRequest ``` When a user provides their join reason, the bot: 1. Updates the request with their reason 2. Edits the admin message to show the reason 3. Keeps the message indexed by message ID for button handling ### Update Processing The bot receives Telegram updates via long polling: 1. **ChatJoinRequest**: User requests to join → calls `HandleJoinRequest` 2. **CallbackQuery**: Admin clicks button → calls `HandleCallbackQuery` 3. **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 client - `go.yaml.in/yaml/v3` - YAML configuration parsing ### Go Version Requires Go 1.25.3+ ### Building Locally ```bash go build -o telegram-approval-bot ./... ./telegram-approval-bot ``` ## Troubleshooting ### Bot doesn't respond to join requests - Verify `target_chat_id` is 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_id` is correct - Check bot has message permission in admin chat - If using topics, verify `admin_chat_topic_id` is 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 `/info` to verify current settings - Check config.yaml for syntax errors ## License MIT License - Copyright (c) 2026 astra.blue