TG-GithubBot is a self-hosted Telegram bot that links GitHub repositories to Telegram chats. It delivers repository activity as formatted notifications, manages GitHub webhooks automatically, and lets connected users comment on, close, and approve issues and pull requests without leaving Telegram.
Note
One-click deploys still require an external MongoDB (e.g. MongoDB Atlas) and a GitHub OAuth App — see Cloud Platforms.
- Features
- Tech Stack
- How It Works
- Requirements
- Quick Start
- Setup
- Configuration
- Deployment
- Usage
- Supported GitHub Events
- Security
- Backups
- Troubleshooting
- Development
- Project Structure
- License
- Real-time notifications — pushes, pull requests, issues, CI/CD runs, releases, discussions, and 70+ other GitHub events delivered to Telegram.
- GitHub OAuth login — connect accounts with encrypted token storage (AES-256-GCM).
- Automatic webhook management — repository webhooks are created, updated, and removed from Telegram.
- Repository discovery — browse and link repositories with inline buttons.
- Per-repository event settings — choose exactly which events each chat receives.
- Two-way interaction — reply to notifications to comment on issues and PRs; use
/close,/reopen, and/approvefor quick actions. - Group-friendly — admin-only repository management in group chats.
- Persistent state — MongoDB storage for users, chats, linked repositories, and webhook IDs.
- Easy deployment — Docker Compose with MongoDB included, plus blueprints for Render, Heroku, and Koyeb.
- Telegram updates — received via long polling (
USE_POLLING=true, recommended for cloud platforms and local development) or Telegram webhooks (USE_POLLING=false, recommended for a dedicated VPS). - GitHub activity — always delivered via repository webhooks for real-time performance.
- Security — every GitHub payload is validated against an HMAC signature (
GITHUB_WEBHOOK_SECRET).
TELEGRAM_WEBHOOK_URL is the public HTTPS base URL of the bot. It is used for the GitHub OAuth callback and GitHub webhook delivery, and for Telegram webhooks when polling is disabled. It must be reachable by GitHub even in polling mode.
Important
When USE_POLLING=false, the bot registers its Telegram webhook at startup. If you later switch to polling, you may need to delete the webhook manually via the Telegram API before updates arrive.
- Telegram bot token — from @BotFather.
- GitHub OAuth App — see Setup.
- Public HTTPS URL — required for GitHub webhooks and the OAuth callback.
- MongoDB — any instance; Docker Compose includes one.
- Docker and Docker Compose — for the recommended deployment.
- Go 1.27 or newer — only for manual builds.
git clone https://github.com/bisug/TG-GithubBot.git
cd TG-GithubBot
cp sample.env .env # then edit .env — see Configuration
docker compose up -d --buildOpen a private chat with your bot, send /start, then /connect to link your GitHub account. Full walkthrough: Usage.
- Open @BotFather in Telegram.
- Run
/newbotand follow the prompts. - Copy the bot token and set it as
TELEGRAM_TOKEN.
Optionally register the command list so Telegram autocompletes it:
/setcommands
start - Start the bot
help - Show help
connect - Connect GitHub account
addrepo - Link a repository
removerepo - Unlink a repository
repos - List linked repositories
settings - Configure repository events
privacy - Show privacy policy
logout - Disconnect GitHub account
reload - Refresh admin cache
close - Close an issue or PR
reopen - Reopen an issue or PR
approve - Approve a PR
Create an OAuth App under GitHub, then Settings, Developer settings, OAuth Apps, New OAuth App, with:
| Field | Value |
|---|---|
| Application name | TG-GithubBot |
| Homepage URL | https://your-domain.com |
| Authorization callback URL | https://your-domain.com/oauth/callback |
Copy the Client ID and Client Secret into GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET.
The bot requests these OAuth scopes:
| Scope | Purpose |
|---|---|
repo | Access public and private repositories; required to manage webhooks and perform PR actions (approve/close/reopen). |
admin:repo_hook | Create, update, and delete the repository webhooks that deliver notifications. |
read:user | Identify the GitHub account and link it to a Telegram ID. |
Copy the sample environment file and edit it:
cp sample.env .env# Windows PowerShellCopy-Item sample.env .env| Variable | Required | Default | Description |
|---|---|---|---|
TELEGRAM_TOKEN | Yes | — | Bot token from @BotFather. |
TELEGRAM_WEBHOOK_URL | Yes | — | Public HTTPS base URL, no trailing slash. Used for the OAuth callback and GitHub webhooks. |
GITHUB_CLIENT_ID | Yes | — | GitHub OAuth App client ID. |
GITHUB_CLIENT_SECRET | Yes | — | GitHub OAuth App client secret. |
ENCRYPTION_KEY | Yes | — | 64-character hex string (32 bytes) used to encrypt OAuth tokens. Must remain stable. |
MONGODB_URI | Yes | — | MongoDB connection string. |
GITHUB_WEBHOOK_SECRET | Recommended | — | Shared secret for validating GitHub webhook payloads. |
DATABASE_NAME | No | github_bot | MongoDB database name. |
PORT | No | 8080 | HTTP server port. Use 10000 on Render. |
USE_POLLING | No | false | true receives Telegram updates via long polling. Docker Compose defaults this to true. |
openssl rand -hex 32Use one generated value for ENCRYPTION_KEY and another for GITHUB_WEBHOOK_SECRET.
PowerShell alternative:
-join ((1..32) |ForEach-Object { '{0:x2}'-f (Get-Random-Minimum 0-Maximum 256) })Caution
GITHUB_WEBHOOK_SECRET must be a strong, random string — a weak or default secret lets anyone inject fake GitHub activity into your chats.
ENCRYPTION_KEY must never change after deployment; stored OAuth tokens cannot be recovered without the original key.
Never commit .env to version control.
Telegram Bot API 9.4 supports custom emoji icons and visual button styles. Button styles are enabled automatically; custom emoji icons are optional and configured via custom emoji IDs. Without them, the bot renders styled text buttons.
TG_BUTTON_ICON_ADD=TG_BUTTON_ICON_ALL=TG_BUTTON_ICON_BACK=TG_BUTTON_ICON_CANCEL=TG_BUTTON_ICON_CHOOSE=TG_BUTTON_ICON_CONFIRM=TG_BUTTON_ICON_CONNECT=TG_BUTTON_ICON_GITHUB=TG_BUTTON_ICON_NEXT=TG_BUTTON_ICON_PREVIOUS=TG_BUTTON_ICON_PUSH=TG_BUTTON_ICON_SETTINGS=TG_BUTTON_ICON_STOP=Use the raw custom emoji ID as the value — not the emoji character.
Clone the repository:
git clone https://github.com/bisug/TG-GithubBot.git cd TG-GithubBotCreate and edit
.env:cp sample.env .env nano .env
Start the bot and MongoDB:
docker compose up -d --build
Verify:
docker compose logs -f bot
The health endpoint should respond at
http://your-server-ip:8080.
For production, put a reverse proxy (Caddy, Nginx, Traefik, Cloudflare Tunnel, or a platform HTTPS proxy) in front of port 8080.
Install Caddy and point your domain's DNS record at the server.
Caddyfile:
your-domain.com {
reverse_proxy127.0.0.1:8080
}Set TELEGRAM_WEBHOOK_URL=https://your-domain.com and restart with docker compose up -d --build.
Expose port 8080 through a tunnel such as ngrok:
docker compose up -d --build
ngrok http 8080Then:
- Set
TELEGRAM_WEBHOOK_URLto the HTTPS tunnel URL. - Update the GitHub OAuth App callback URL to
https://<tunnel-url>/oauth/callback. - Restart the bot:
docker compose up -d --build.
Requires a running MongoDB instance and Go 1.27+.
go mod download
go run ./cmd/botBuild a binary:
go build -o bot ./cmd/bot
./bot# Windows PowerShell
go build -o bot.exe ./cmd/bot
.\bot.exeCloud platforms host the bot as a web service, but they have ephemeral filesystems and may spin down when idle. Follow these rules for a stable deployment:
- External MongoDB is required. Use MongoDB Atlas (generous free tier) or a managed add-on from your platform, and set its connection string as
MONGODB_URI. - Use polling. Set
USE_POLLING=truefor maximum reliability on free tiers — the bot starts receiving commands the moment the container wakes, with no extra networking setup. - Match the OAuth callback. Point your GitHub OAuth App's authorization callback URL at
https://<your-service-url>/oauth/callback. - Expect sleep on free tiers. Accessing the public URL or receiving a GitHub event wakes the bot.
- Never change
ENCRYPTION_KEYafter deployment, or stored GitHub tokens are lost. - Webhook paths are automatic. GitHub webhooks are created at
https://<your-service-url>/webhook/<token>.
The repo ships a Render Blueprint (render.yaml) that configures the Docker runtime, a /healthz health check, port 10000, and USE_POLLING=true.
- Push the repo to GitHub or GitLab.
- On Render, click New, then Blueprint, and select the repo.
- Fill in the secrets when prompted (
TELEGRAM_TOKEN,TELEGRAM_WEBHOOK_URL,GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,GITHUB_WEBHOOK_SECRET,ENCRYPTION_KEY,MONGODB_URI). - After the first deploy, set
TELEGRAM_WEBHOOK_URLto the service URL, e.g.https://tg-githubbot.onrender.com. - Update the GitHub OAuth App callback URL to
https://tg-githubbot.onrender.com/oauth/callback.
Deploys run automatically on every push; manual deploys can be triggered from the dashboard.
The repo includes heroku.yml (container stack) and app.json (one-click Deploy to Heroku button with all env vars pre-wired).
heroku create your-app-name
heroku stack:set container
heroku config:set TELEGRAM_TOKEN=... TELEGRAM_WEBHOOK_URL=https://your-app-name.herokuapp.com \
GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... GITHUB_WEBHOOK_SECRET=... \
ENCRYPTION_KEY=$(openssl rand -hex 32) MONGODB_URI=... USE_POLLING=true
git push heroku masterOr deploy the Docker image directly:
heroku container:push web
heroku container:release webHeroku injects PORT automatically, and app.json defaults USE_POLLING=true — keep it on. Update the OAuth callback URL to https://your-app-name.herokuapp.com/oauth/callback.
Koyeb builds the Dockerfile from the Git repo and exposes a public *.koyeb.app URL.
koyeb app init tg-githubbot \
--git github.com/<YOUR_USERNAME>/TG-GithubBot \
--git-branch master \
--git-builder docker \
--ports 8080:http \
--routes /:8080 \
--env PORT=8080 \
--env USE_POLLING=true \
--env TELEGRAM_TOKEN=... \
--env TELEGRAM_WEBHOOK_URL=https://tg-githubbot-<your-org>.koyeb.app \
--env GITHUB_CLIENT_ID=... \
--env GITHUB_CLIENT_SECRET=... \
--env GITHUB_WEBHOOK_SECRET=... \
--env ENCRYPTION_KEY=... \
--env MONGODB_URI=...Or via the Koyeb dashboard: Create App, choose GitHub, select the repo, builder Dockerfile, port 8080, then add the environment variables listed above.
Set TELEGRAM_WEBHOOK_URL to the Koyeb public URL and update the OAuth callback URL to https://<your-koyeb-url>/oauth/callback. Koyeb's free instance sleeps after inactivity; polling makes wake-ups seamless.
Open a private chat with the bot and send
/start.Send
/connectand complete the GitHub OAuth flow to link your account.Add the bot to a group if you want notifications there.
In the target chat, link a repository:
/addrepo owner/repoOr send
/addrepowithout arguments to browse your repositories with inline buttons.Fine-tune which events the chat receives with
/settings.
Note
Existing linked repositories keep their current webhook event settings. To switch a repository to wildcard delivery ("Send me everything"), use /settings or remove and re-add the repository.
| Command | Where | Description |
|---|---|---|
/start | Any chat | Start the bot. |
/help | Any chat | Show help. |
/connect | Private chat | Connect your GitHub account via OAuth. |
/addrepo [owner/repo] | Any chat | Link a repository; without arguments, browse with inline buttons. |
/removerepo [owner/repo] | Any chat | Unlink a repository. |
/repos | Any chat | List repositories linked to this chat. |
/settings | Any chat | Configure which events each linked repository delivers. |
/privacy | Any chat | Show the privacy policy. |
/logout | Private chat | Clear your stored GitHub token. |
/reload | Groups | Refresh the cached group admin list. |
/close | Reply to a notification | Close the linked issue or PR. |
/reopen | Reply to a notification | Reopen the linked issue or PR. |
/approve | Reply to a notification | Approve the linked PR. |
/connectand/logoutwork only in private chat.- In groups, only Telegram admins with the Change Group Info permission can add or remove repositories and change settings.
- The GitHub account running
/addrepomust have permission to create repository webhooks (repo admin). - Reply actions (
/close,/reopen,/approve, and comment replies) use the GitHub token of the Telegram user who sends them. - OAuth tokens are encrypted with AES-256-GCM before being stored in MongoDB.
git pull
docker compose up -d --build
docker compose logs -f botThe bot formats and delivers 78 GitHub webhook events, including:
- Push — commits and branch updates
- Pull requests — open, close, review, approve, merge
- Issues — open, close, comment, label, milestone
- CI/CD — workflow runs and jobs, check suites, deployments
- Community — stars, forks, watches, releases, discussions
- Security — code scanning, Dependabot, and secret scanning alerts
For the complete list, see internal/github/events.go.
- OAuth tokens are encrypted at rest with AES-256-GCM; the key never leaves your
.env. - GitHub webhook payloads are validated with HMAC signatures (
GITHUB_WEBHOOK_SECRET). - Group administration is restricted to Telegram admins; reply actions run as the replying user's own GitHub token.
Operational checklist:
- Serve only behind HTTPS.
- Keep
.envprivate and never commit it. - Keep
ENCRYPTION_KEYstable and backed up. - Use a persistent MongoDB volume.
- Rotate GitHub OAuth and webhook secrets immediately if leaked.
- Use a dedicated GitHub OAuth App for this bot.
MongoDB stores linked chats, repository webhook IDs, and encrypted OAuth tokens.
Back up the Docker volume (verify the exact name with docker volume ls):
docker run --rm \
-v tg-githubbot_mongodb_data:/data/db \
-v "$PWD:/backup" \
alpine tar czf /backup/mongo-data-backup.tar.gz /data/dbKeep copies of .env, the MongoDB data, and ENCRYPTION_KEY. Without the original ENCRYPTION_KEY, encrypted tokens cannot be recovered.
TELEGRAM_WEBHOOK_URLis correct and has no path — usehttps://your-domain.com, nothttps://your-domain.com/oauth/callback.- The GitHub OAuth App callback URL is exactly
https://your-domain.com/oauth/callback. - The public URL actually reaches the bot.
ENCRYPTION_KEYdid not change between sending/connectand opening the callback.- On Render free, open the service URL first to wake the instance, then retry.
If the browser shows Invalid or expired state, run /connect again — OAuth links are intentionally short-lived.
- The domain uses HTTPS.
- The reverse proxy forwards to port
8080. GITHUB_WEBHOOK_SECRETmatches the secret configured on the GitHub webhook.- The bot logs show no signature validation errors.
The connected GitHub user must have admin rights on the repository — the bot creates repository webhooks.
Check the webhook under Repository, then Settings, then Webhooks, and confirm:
- The payload URL starts with
https://your-domain.com/webhook/. - Content type is
application/json. - A secret is set.
- The webhook is active.
- Recent deliveries show HTTP
200.
Telegram admin status is cached. Run /reload and retry.
go test ./... -count=1 # run tests
gofmt -w ./cmd ./internal # format code
go vet ./... # static analysisCI runs formatting checks, build, vet, and tests on every push (see .github/workflows/ci.yml).
cmd/bot/main.go Application entry point
internal/config Environment loading
internal/db MongoDB access
internal/cache In-memory TTL cache
internal/bot/commands Telegram commands
internal/bot/callbacks Telegram inline callbacks
internal/bot/middleware Chat tracking middleware
internal/github OAuth, webhook parsing, event formatting
internal/models Shared data models
internal/utils Crypto and Telegram helpers
Released under the MIT License. See LICENSE.
Open a GitHub issue with logs and reproduction steps.