Skip to content

Rebuilding My TG Forward Bot

On this page

The forwarding bot I used before was Node Forward Bot. I used it for quite a while, but I kept getting flooded with ads, which got really annoying. Taking another look at it, the project had already been dead for a year. NFD2 wasn’t open source (I think? Couldn’t find a repo), so I didn’t try it, and I’ve no idea whether it has ad-blocking features. So I decided to reinvent the wheel and build my own.


My first attempt was stuffing it with a huge list of sensitive words to auto-block on match. Then I got mysterious results like blocking x86_64 (for “64”), Steam platform exclusive (substring matching “Taiwan independence” in Chinese), Python scripts (matching “cheats”), and listening port (matching “surveillance”).


Ugh… clearly that road went nowhere. So I listened to some group chat friends and tried writing a bunch of regex expressions. But as everyone knows, Chinese spam is incredibly cryptic: 丅子, 微 P 嗯, weird emoji concatenations… Can regex really defend against all this? Maybe, but my brain clearly isn’t that powerful (sweat), so in the end I decided to bring in an LLM for moderation.


I needed a model that was as fast as possible, didn’t need to be super smart, but could understand the text. As everyone knows, Google has a model called gemini-3-flash, which felt suitable for content moderation. So I used it, wrote a simple prompt, and had it judge user input — outputting either SAFE or UNSAFE.

const MODERATION_PROMPT = `
# Role
Content Moderator API. Output one word only.
# Rules
UNSAFE if:
- Real human nudity/sex
- QR codes/spam/ads/gambling promotion
- Real gore/shock content
- Illegal content promotion
- Scam/phishing attempts
SAFE if:
- 2D/Anime/Cartoon (even suggestive)
- Normal photos/text/screenshots
- Regular conversation
# Output
One word: "SAFE" or "UNSAFE"
Analyze the content:`;

There’s a small amount of latency, but it’s basically negligible. Compared to traditional rule-based matching, an LLM can understand context and semantics, so the false-positive rate is much lower. The downside is probably cost, but Gemini has a free quota — not a generous one though. With three API keys, that’s about 60 calls per day, which is just barely enough for ad blocking.


The whole project uses a modular structure:

.
├── src
│ ├── ai.ts
│ ├── config.ts
│ ├── handlers
│ │ ├── admin
│ │ │ ├── callbacks.ts
│ │ │ ├── commands.ts
│ │ │ ├── index.ts
│ │ │ ├── replies.ts
│ │ │ └── shared.ts
│ │ └── guest.ts
│ ├── i18n
│ │ ├── en.ts
│ │ └── zh.ts
│ ├── i18n.ts
│ ├── index.ts
│ ├── storage.ts
│ ├── telegram.ts
│ └── types.ts
├── tsconfig.json
└── wrangler.toml

Implemented these features:

FeatureDescription
LLM Content ModerationLLM-based harmful content detection
Ban ListView all banned users
Content Hash CachePrevent wasted tokens from repeated spam
Blacklist SystemSame — multiple blocks trigger an auto-ban
Whitelist SystemStop moderation after consecutive clean messages
Stats SystemMessage count, user count, AI block count
Multi API Key RotationGoogle’s API quota is way too stingy

Who is 20 API calls a day enough for? It used to be 100 calls a day. Gemini CLI and Antigravity are all-you-can-eat, but the API is just miserly.
Edit: Now even Gemini CLI and Antigravity aren’t enough anymore.

The whole project runs on Cloudflare Workers (NFD works the same way — convenient, useful, and free), a completely zero-cost solution. The LLM usage is free-tier too.


GitHub: kokosa-forward-bot