Telegram-бот для анонимной обратной связи сотрудников компании, работающий на платформе Cloudflare Workers.
- 🔐 Secure activation via secret deeplink
- 💬 Anonymous message forwarding to admin group
- 🎯 Message categorization (Ideas, Problems, Gratitude)
- 🏷️ Topic selection (Processes, Colleagues, Conditions, Salary, Management, Other)
- 💪 Inspirational responses to users
- 🚀 Serverless deployment on Cloudflare Workers
- Prerequisites
- Setup
- Deployment
- Environment Variables
- User Activation
- Usage
- TEST_MODE for Staging
- Maintenance
- Security
- Troubleshooting
- License
- Cloudflare account with Workers plan
- Telegram Bot Token (get from @BotFather)
- Telegram group for admins
- Node.js (v16 or higher) and npm installed locally
- Wrangler CLI (
npm install -g wrangler)
git clone <repository-url>
cd anonymous-feedback-bot
npm install- Open Telegram and find @BotFather
- Send
/newbotcommand - Follow instructions to create your bot
- Save the bot token (format:
123456789:ABCdefGHIjklMNOpqrsTUVwxyz) - Save your bot username (e.g.,
my_feedback_bot)
- Create a new Telegram group
- Add your bot to the group
- Make the bot an admin (optional, but recommended)
- Send a test message in the group
- Get the chat ID:
curl https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates- Look for
"chat":{"id":-1001234567890,...}in the response - The chat ID will be a negative number starting with
-100
# Login to Cloudflare
wrangler login
# Create production KV namespace
wrangler kv:namespace create "KV"
# Create preview KV namespace for development
wrangler kv:namespace create "KV" --previewCopy the namespace IDs from the output and update wrangler.toml:
[[kv_namespaces]]
binding = "KV"
id = "your-production-kv-namespace-id"
preview_id = "your-preview-kv-namespace-id"Generate a secure random token for user activation:
# On macOS/Linux
openssl rand -hex 32
# Or use Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Save this token - you'll use it for the deeplink and as the ACCESS_TOKEN environment variable.
Set required secrets using Wrangler:
# Required secrets
wrangler secret put TELEGRAM_TOKEN
# Enter your bot token when prompted
wrangler secret put ADMIN_CHAT_ID
# Enter your admin group chat ID (e.g., -1001234567890)
wrangler secret put ACCESS_TOKEN
# Enter the token you generated in step 5Optional secrets for staging/testing:
wrangler secret put ADMIN_CHAT_ID_TEST
# Enter your test group chat ID for staging environment- Update wrangler.toml with your worker name:
name = "anonymous-feedback-bot"
main = "src/index.js"
compatibility_date = "2024-01-01"
[env.production]
vars = { TEST_MODE = "false" }
[[kv_namespaces]]
binding = "KV"
id = "your-production-kv-namespace-id"- Deploy to Cloudflare Workers:
wrangler deploy --env production-
Note your Worker URL from the output (e.g.,
https://anonymous-feedback-bot.your-subdomain.workers.dev) -
Register Webhook with Telegram:
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://anonymous-feedback-bot.your-subdomain.workers.dev"}'Expected response:
{"ok":true,"result":true,"description":"Webhook was set"}- Verify Webhook Registration:
curl "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getWebhookInfo"Expected response should show:
{
"ok": true,
"result": {
"url": "https://anonymous-feedback-bot.your-subdomain.workers.dev",
"has_custom_certificate": false,
"pending_update_count": 0,
"max_connections": 40
}
}For testing in a staging environment:
- Create a test bot (optional, or reuse the same bot)
- Create a test admin group
- Update wrangler.toml with staging configuration:
[env.staging]
vars = { TEST_MODE = "true" }
[[env.staging.kv_namespaces]]
binding = "KV"
id = "your-staging-kv-namespace-id"- Set staging-specific secrets:
wrangler secret put ADMIN_CHAT_ID_TEST --env staging
# Enter your test group chat ID- Deploy to staging:
wrangler deploy --env staging- Register webhook for staging:
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://anonymous-feedback-bot-staging.your-subdomain.workers.dev"}'# Deploy to production
wrangler deploy --env production
# Deploy to staging
wrangler deploy --env staging
# View deployment logs
wrangler tail --env production
# View KV storage
wrangler kv:key list --binding KV --env production
# Delete a KV key (for testing)
wrangler kv:key delete "session:123456" --binding KV --env production| Variable | Description | Example | How to Set |
|---|---|---|---|
TELEGRAM_TOKEN |
Bot API token from @BotFather | 123456789:ABCdefGHI... |
wrangler secret put TELEGRAM_TOKEN |
ADMIN_CHAT_ID |
Admin group chat ID (negative number) | -1001234567890 |
wrangler secret put ADMIN_CHAT_ID |
ACCESS_TOKEN |
Secret activation token (min 32 chars) | a1b2c3d4e5f6... |
wrangler secret put ACCESS_TOKEN |
| Variable | Description | Example | How to Set |
|---|---|---|---|
TEST_MODE |
Enable test mode (set in wrangler.toml) | true / false |
Set in wrangler.toml vars |
ADMIN_CHAT_ID_TEST |
Test admin group chat ID | -1009876543210 |
wrangler secret put ADMIN_CHAT_ID_TEST |
REVOKE_ALL_ACCESS |
Revoke all user access | true / false |
wrangler secret put REVOKE_ALL_ACCESS |
Via Wrangler Secrets (recommended for sensitive data):
wrangler secret put VARIABLE_NAMEVia wrangler.toml (for non-sensitive configuration):
[env.production]
vars = { TEST_MODE = "false" }Via .env file (local development only):
cp .env.example .env
# Edit .env with your valuesThe deeplink format for user activation is:
https://t.me/<BOT_USERNAME>?start=<ACCESS_TOKEN>
Example:
https://t.me/my_feedback_bot?start=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
- Replace
<BOT_USERNAME>with your bot's username (without @) - Replace
<ACCESS_TOKEN>with the token you set in environment variables - Share this link with employees via email, intranet, or internal communication channels
- Keep the
ACCESS_TOKENsecret - only share the complete deeplink with authorized employees - The token should be at least 32 random characters
- Rotate the token periodically (quarterly recommended)
- When rotating, use
REVOKE_ALL_ACCESS=trueto force re-activation
- Employee clicks the deeplink
- Telegram opens the bot with the
/start <ACCESS_TOKEN>command - Bot validates the token against
ACCESS_TOKENenvironment variable - If valid: user is marked as trusted (stored in KV for 90 days)
- If invalid: user receives a neutral message without revealing bot purpose
- Activate the bot: Click the deeplink provided by your company
- Start conversation: The bot will show a welcome message
- Select category: Choose from:
- 💬 Идея / предложение (Idea / Suggestion)
⚠️ Проблема / жалоба (Problem / Complaint)- ❤️ Благодарность / признание (Gratitude / Recognition)
- Select topic: Choose from:
- Процессы (Processes)
- Коллеги (Colleagues)
- Условия (Conditions)
- Зарплата (Salary)
- Менеджмент (Management)
- Другое (Other)
- Write message: Type your message (text + optional photo/video/document)
- Confirm: Review and confirm sending
- Receive response: Get an inspirational message confirming submission
Messages appear in the admin group with this format:
📩 Новое анонимное сообщение
Категория: 💬 Идея / предложение
Тема: Процессы
Текст:
[User's message here]
TEST_MODE is a special environment variable that enables safe testing without affecting production data.
- Testing bot functionality before production deployment
- Developing new features
- Training administrators
- Debugging issues
When TEST_MODE=true:
- Separate Admin Group: Messages are sent to
ADMIN_CHAT_ID_TESTinstead ofADMIN_CHAT_ID - Message Logging: All messages are logged to KV storage with
test_log:prefix - No Production Impact: Production admin group receives no test messages
In wrangler.toml:
[env.staging]
vars = { TEST_MODE = "true" }
[[env.staging.kv_namespaces]]
binding = "KV"
id = "your-staging-kv-namespace-id"Set test admin group:
wrangler secret put ADMIN_CHAT_ID_TEST --env stagingDeploy staging environment:
wrangler deploy --env stagingProduction (TEST_MODE=false):
wrangler deploy --env productionStaging (TEST_MODE=true):
wrangler deploy --env staging# List all test logs
wrangler kv:key list --binding KV --env staging --prefix "test_log:"
# Get specific log
wrangler kv:key get "test_log:1234567890" --binding KV --env staging
# Clear test logs
wrangler kv:key delete "test_log:1234567890" --binding KV --env staging# Install dependencies
npm install
# Run locally with wrangler
npm run dev
# Or use wrangler directly
wrangler dev- Set up a local
.envfile (copy from.env.example) - Use
wrangler devto run locally - Use a tool like
ngrokto expose your local server:
ngrok http 8787- Register the ngrok URL as webhook:
curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-ngrok-url.ngrok.io"}'# Real-time logs for production
wrangler tail --env production
# Real-time logs for staging
wrangler tail --env staging
# Filter logs
wrangler tail --env production --format prettyTo change the access token (e.g., after employee departures):
- Generate new token:
openssl rand -hex 32- Update secret:
wrangler secret put ACCESS_TOKEN --env production- Revoke all existing access (optional):
wrangler secret put REVOKE_ALL_ACCESS --env production
# Enter: true- Deploy:
wrangler deploy --env production-
Distribute new deeplink with new token to employees
-
Reset REVOKE_ALL_ACCESS (after all users re-activate):
wrangler secret put REVOKE_ALL_ACCESS --env production
# Enter: falseCloudflare Dashboard:
- Navigate to Workers & Pages → Your Worker
- View Analytics: requests, errors, CPU time
- View Logs: real-time execution logs
KV Storage Monitoring:
# List all keys
wrangler kv:key list --binding KV --env production
# Check specific user session
wrangler kv:key get "session:123456789" --binding KV --env production
# Check trusted users
wrangler kv:key list --binding KV --env production --prefix "trusted:"- Make code changes
- Test locally with
wrangler dev - Deploy to staging:
wrangler deploy --env staging - Test in staging environment
- Deploy to production:
wrangler deploy --env production
- Access Token: Use at least 32 random characters (use
openssl rand -hex 32) - Token Rotation: Rotate access token quarterly or after employee departures
- Anonymity: User telegram IDs are never stored in forwarded messages
- Data Retention:
- Session data expires automatically (1 hour TTL)
- Trusted user status expires after 90 days
- Secrets Management: Always use
wrangler secret putfor sensitive data - Webhook Security: Telegram validates webhook requests from known IP ranges
- Environment Isolation: Use separate bots/groups for staging and production
- No user identifying information is stored beyond the trusted user flag
- Messages forwarded to admin group contain no sender information
- KV storage entries have automatic expiration (TTL)
- No persistent logging of message content (except in TEST_MODE)
Check webhook status:
curl "https://api.telegram.org/bot<TOKEN>/getWebhookInfo"Verify webhook is set correctly:
- URL should match your Worker URL
pending_update_countshould be 0- No errors in
last_error_message
Check Worker logs:
wrangler tail --env productionVerify admin chat ID:
- Must be a negative number starting with
-100 - Bot must be added to the group
- Bot should have permission to send messages
Test manually:
curl -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
-H "Content-Type: application/json" \
-d '{"chat_id": "<ADMIN_CHAT_ID>", "text": "Test message"}'Verify access token:
# Check if secret is set
wrangler secret list --env productionCheck deeplink format:
- Format:
https://t.me/<BOT_USERNAME>?start=<ACCESS_TOKEN> - Bot username should not include
@ - Access token must match exactly
Check KV storage:
# List trusted users
wrangler kv:key list --binding KV --env production --prefix "trusted:"Check namespace binding:
- Verify
wrangler.tomlhas correct KV namespace IDs - Ensure binding name is
KV
Check storage quota:
- Free tier: 1 GB storage, 100,000 reads/day, 1,000 writes/day
- Upgrade plan if limits exceeded
Common issues:
- URL must be HTTPS
- URL must be publicly accessible
- Telegram must be able to reach the URL
Re-register webhook:
# Delete existing webhook
curl -X POST "https://api.telegram.org/bot<TOKEN>/deleteWebhook"
# Set new webhook
curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-worker.workers.dev"}'- Cloudflare Workers Documentation
- Cloudflare KV Documentation
- Telegram Bot API Documentation
- Wrangler CLI Documentation
MIT