The bug report was already written.
It was just written in the wrong place.
Someone dropped it in a Telegram group. A few people replied "same here". Maybe someone added a screenshot. Then the conversation moved on, and the useful report disappeared into the scrollback.
That is the small but annoying gap gitgram tries to close:
React to a Telegram message, get a GitHub issue.
No slash command. No copy-paste. No bot reading every message in the group. Just a reaction on the message that already contains the problem.
The Problem
Issue trackers are where work should end up, but chat is where work usually starts.
That is especially true for small teams, self-hosted projects, hobby communities, open source maintainers, and Telegram-heavy groups. People do not always think in "tickets". They think in:
- "This button is broken."
- "The deployment failed again."
- "Same issue on my phone."
- "Can someone look at this later?"
Those messages are often good enough to become issues. The friction is making someone open GitHub, choose a repo, write a title, quote the context, add labels, and paste the chat link.
Most of the time, nobody does that.
What Gitgram Does
Gitgram is a self-hosted Telegram to GitHub issue bot.
When someone reacts to a configured Telegram message with an emoji, Gitgram creates a GitHub issue from that message.
The created issue includes:
- the original message text or caption
- the original author when Telegram exposes it
- the Telegram group name
- the original message link when available
- the person who reacted
- optional labels from config
- a hidden marker used to announce when the issue closes
When the GitHub issue is closed, Gitgram can reply back to the original Telegram message so the chat knows the loop is closed.
The short version:
Telegram message + reaction -> GitHub issue
GitHub issue closed -> Telegram reply
Why Reactions?
Slash commands are precise, but they are noisy.
If a group already has a message saying "the invoice export is broken", replying with /issue invoice export broken is just ceremony. It repeats the useful information and asks humans to behave like a ticketing system.
A reaction is lighter.
It lets the group keep its normal chat flow while still giving maintainers a reliable capture mechanism. Someone spots a message worth tracking, reacts to it, and the issue exists.
Gitgram uses standard Telegram reactions, because Telegram does not allow arbitrary emoji reactions for everyone. For example, custom emoji require Telegram Premium, and some obvious ticket-style emoji are not in the standard reaction set.
By default:
- trigger reaction:
:eyes: - acknowledgement reaction:
:ok_hand:
Both are configurable, but Gitgram validates them on startup so you do not end up with a bot that runs successfully and then silently never fires.
The Weird Telegram Constraint
The interesting part is not creating a GitHub issue. That is straightforward with a GitHub App.
The interesting part is getting the original Telegram message.
Telegram's message_reaction update tells the bot that someone reacted to a message. It includes the chat, the message id, the user, the date, and the reaction.
It does not include the message text.
And the Telegram Bot API does not have a "fetch message by id" endpoint.
That leaves two realistic designs:
- Store every group message in a database, then look it up later when someone reacts.
- Fetch the message indirectly only when a reaction happens.
Gitgram uses the second approach.
When a configured reaction arrives, Gitgram forwards the reacted-to message into a private archive channel. Telegram's forwardMessage response contains the full forwarded Message, including the text or caption. Gitgram reads that response and uses it to create the GitHub issue.
That gives Gitgram a useful privacy property:
- it does not need to read every group message
- Telegram privacy mode can stay enabled
- it does not store a database of chat history
- it can work on messages from earlier in the visible chat history
The trade-off is that the group must allow forwarding. If "restrict saving content" is enabled, Telegram blocks the forward and Gitgram cannot read the message.
That is a Telegram limitation, not a Gitgram preference.
Why There Is No Database
Gitgram stores the issue-to-message link inside the GitHub issue body as an HTML comment.
It looks conceptually like this:
<!-- gitgram: chat=-1001234567890; msg=8842 -->
That marker lets Gitgram map a closed GitHub issue back to the original Telegram chat and message.
This keeps GitHub as the only durable store:
- no database
- no volume
- no migration story
- no backup process
- no state to preserve when redeploying
The bot does keep a short in-memory deduplication set. That prevents two people reacting at almost the same time from creating duplicate issues. For longer-term duplicate prevention, it can also search GitHub for an existing marker.
The design is deliberately boring operationally. A Docker container, a config file, a Telegram bot token, and a GitHub App are enough.
Quick Setup
The moving parts are:
- a Telegram bot created with BotFather
- a Telegram supergroup
- a private archive channel
- a GitHub App with issue permissions
- a
gitgram.yamlconfig file - a public HTTPS endpoint for GitHub webhooks
A minimal Telegram to GitHub issue routing config looks like this:
chats:
-1001234567890:
repo: owner/repo
labels:
- from-telegram
Run the self-hosted Telegram GitHub issue bot with Docker:
docker run --rm \
--env-file .env \
-v "$PWD/gitgram.yaml:/app/gitgram.yaml:ro" \
-p 3000:3000 \
ghcr.io/robbeverhelst/gitgram:latest
Telegram uses long polling, so it does not need an inbound webhook. GitHub does need to reach POST /gh/webhook so issue close announcements can flow back into Telegram.
For local testing, a tunnel such as Cloudflare Tunnel is enough. For production, any stable HTTPS endpoint works.
Requirements Worth Knowing
Gitgram is intentionally narrow, but Telegram has a few hard rules:
- the group must be a supergroup
- the bot must be a group administrator
- the trigger emoji must be enabled in the group
- the group must not restrict saving content
- the bot must be an admin in the private archive channel
The admin requirement is not about power. Telegram simply does not deliver reaction updates to the bot otherwise.
Silent Mode
Some groups do not want bot chatter.
Gitgram can be configured to create issues silently:
chats:
-1001234567890:
repo: owner/repo
announce_created: false
announce_closed: false
ack_reaction: true
With this setup, the bot still reacts to the original message as acknowledgement, but it does not post routine success messages. Issue links are also private by default: include_link defaults to false, so chat announcements do not expose the repository URL unless you opt in.
Errors still reply in chat. That is deliberate. If someone reacts to create a ticket and something fails, that person needs to know immediately.
Known Limits
Gitgram is small on purpose, so the limits are explicit:
- media cannot be attached to GitHub issues because GitHub does not expose a public issue attachment API
- close announcements can be missed if the GitHub webhook fails while the bot is down
- removing the reaction does not close the issue
- per-topic repository routing is not possible because Telegram reaction updates do not expose the original forum topic id
For me, those trade-offs are acceptable because the core workflow stays simple:
Turn the chat message into an issue before it disappears.
When This Is Useful
Gitgram fits best when your real coordination already happens in Telegram, but your durable work tracking lives in GitHub Issues.
Good examples:
- open source communities
- small product teams
- homelab and self-hosted projects
- support groups
- internal operations chats
- family or volunteer projects that still need an issue tracker
It is not trying to replace Linear, Jira, or GitHub Projects. It is just a bridge from "someone said the useful thing in chat" to "the useful thing now exists somewhere durable".
Closing The Loop
The smallest workflow improvements are often the ones people actually use.
Gitgram is not a full ChatOps platform. It does not try to turn Telegram into a project management UI. It only does one thing:
React to a Telegram message and file it as a GitHub issue.
That turns out to be enough to save a lot of useful context from disappearing.
Repository: github.com/robbeverhelst/gitgram

Top comments (0)