---
name: telegram-content-approval-bot
category: software-development
description: A Telegram bot for content submission with captain approval workflow.
---

# Telegram Content Approval Bot

A skill for creating a Telegram bot that allows users to submit content (posts), generates hashtags/suggestions, and waits for captain approval before publishing to a website. Includes webhook setup, command handling, and deployment preparation.

## When to Use

- You need a Telegram bot for content submission workflow
- Users should be able to submit text for review
- An approver (captain) must approve before publishing
- The bot should simulate or actually publish to a site (e.g., Sailor.bar)
- You want to deploy the bot to a Node.js environment (own server, Railway, etc.)

## Prerequisites

- Telegram bot token from @BotFather
- Node.js >= 18 and npm
- Publicly accessible URL for webhook (own domain or service like Railway)
- GitHub repository for code storage (optional but recommended)
- Basic knowledge of Express and node-telegram-bot-api

## Step-by-Step Guide

### 1. Verify Bot Token
```bash
# Using curl
curl -s https://api.telegram.org/bot<TOKEN>/getMe
# Should return JSON with "ok":true and bot info
```

### 2. Create Project Directory
```bash
mkdir telegram-bot && cd telegram-bot
```

### 3. Initialize npm Project
```bash
npm init -y
```

### 4. Install Dependencies
```bash
npm install express node-telegram-bot-api axios dotenv
```

### 5. Create .env File
```env
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
WEBHOOK_URL=https://your-domain.com  # base URL where bot is accessible
PORT=3000  # or any port; Railway sets PORT automatically
```

### 6. Create server.js (Main Bot Logic)
```javascript
const express = require('express');
const TelegramBot = require('node-telegram-bot-api');
const axios = require('axios');
require('dotenv').config();

const TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const WEBHOOK_URL = process.env.WEBHOOK_URL || 'https://your-domain.com';
const PORT = process.env.PORT || 3000;

const bot = new TelegramBot(TOKEN, { webHook: { port: PORT } });

// Set webhook on startup
bot.setWebHook(`${WEBHOOK_URL}/bot${TOKEN}`);

// In-memory storage for pending posts (use Redis/DB in production)
const pendingPosts = [];

// Express app to receive updates from Telegram
const app = express();
app.use(express.json());

app.post(`/bot${TOKEN}`, (req, res) => {
  bot.processUpdate(req.body);
  res.sendStatus(200);
});

// Handle /start command
bot.onText(/\/start/, (msg) => {
  const chatId = msg.chat.id;
  bot.sendMessage(chatId, `Привет! Я бот для подачи постов. Отправь мне текст поста, и я подготовлю его для публикации. Используй /newpost <текст> или просто отправь сообщение.`);
});

// Handle /newpost command
bot.onText(/\/newpost(.+)/, (msg, match) => {
  const chatId = msg.chat.id;
  const postText = match[1].trim();
  if (!postText) {
    bot.sendMessage(chatId, 'Пожалуйста, укажите текст поста после команды /newpost');
    return;
  }
  const postId = Date.now();
  pendingPosts.push({ id: postId, text: postText, chatId, status: 'pending' });
  bot.sendMessage(chatId, `Пост получен! Ожидание одобрения...\n\n${postText}\n\nВыберите действие:\n/approve_${postId} - одобрить и publicar\n/edit_${postId} <новый текст> - редактировать\n/reject_${postId} - отклонить`);
});

// Handle plain text messages (treat as new post)
bot.on('message', (msg) => {
  if (msg.text && !msg.text.startsWith('/')) {
    const chatId = msg.chat.id;
    const postText = msg.text.trim();
    const postId = Date.now();
    pendingPosts.push({ id: postId, text: postText, chatId, status: 'pending' });
    bot.sendMessage(chatId, `Пост получен! Ожидание одобрения...\n\n${postText}\n\nВыберите действие:\n/approve_${postId} - одобрить и publicar\n/edit_${postId} <новый текст> - редактировать\n/reject_${postId} - отклонить`);
  }
});

// Approve command: show post with hashtags and await confirmation
bot.onText(/\/approve_(\d+)/, (msg, match) => {
  const postId = parseInt(match[1]);
  const post = pendingPosts.find(p => p.id === postId);
  if (!post) {
    bot.sendMessage(msg.chat.id, 'Пост не найден');
    return;
  }
  // Simulate hashtag generation (replace with AI call if desired)
  const hashtags = ['#SailorBar', '#Море', '#Приключения'];
  const finalPost = `${post.text}\n\n${hashtags.join(' ')}`;
  bot.sendMessage(chatId, `Пост готов к публикации:\n\n${finalPost}\n\nДля подтверждения отправьте /confirm_${postId}\nДля правки /edit_${postId} <новый текст>`);
  post.status = 'awaiting_confirmation';
});

// Confirm command: simulate publishing
bot.onText(/\/confirm_(\d+)/, (msg, match) => {
  const postId = parseInt(match[1]);
  const post = pendingPosts.find(p => p.id === postId);
  if (!post) {
    bot.sendMessage(msg.chat.id, 'Пост не найден');
    return;
  }
  // In real implementation, call your site's API or trigger git commit
  bot.sendMessage(chatId, `Пост опубликован на сайте! (симуляция)\n\n${post.text}`);
  post.status = 'published';
  // Remove from pending (or move to archive)
  const index = pendingPosts.indexOf(post);
  if (index > -1) pendingPosts.splice(index, 1);
});

// Edit command
bot.onText(/\/edit_(\d+)(.+)/, (msg, match) => {
  const postId = parseInt(match[1]);
  const newText = match[2].trim();
  const post = pendingPosts.find(p => p.id === postId);
  if (!post) {
    bot.sendMessage(msg.chat.id, 'Пост не найден');
    return;
  }
  post.text = newText;
  post.status = 'pending';
  bot.sendMessage(chatId, `Пост обновлен:\n\n${newText}\n\nТеперь используйте /approve_${postId} для повторного одобрения.`);
});

// Reject command
bot.onText(/\/reject_(\d+)/, (msg, match) => {
  const postId = parseInt(match[1]);
  const index = pendingPosts.findIndex(p => p.id === postId);
  if (index === -1) {
    bot.sendMessage(msg.chat.id, 'Пост не найден');
    return;
  }
  const [removed] = pendingPosts.splice(index, 1);
  bot.sendMessage(chatId, `Пост отклонен и удален.`);
});

console.log(`Telegram bot running on port ${PORT}`);
```

### 7. Create README.md
```markdown
# Telegram Content Approval Bot

Telegram bot for content submission with captain approval workflow.

## Setup

1. Copy `.env.example` to `.env` and fill in:
   - TELEGRAM_BOT_TOKEN
   - WEBHOOK_URL
   - PORT (optional)

2. Install dependencies:
   ```bash
   npm install
   ```

3. Start the bot:
   ```bash
   node server.js
   ```
   The bot will automatically set the webhook on startup.

## Deployment

### On Own Server
- Ensure Node.js is installed
- Clone repository
- Run `npm install`
- Start with PM2 for persistence:
  ```bash
  pm2 start server.js --name contentbot
  pm2 save
  pm2 startup
  ```

### On Railway
- Push repo to GitHub
- Create Railway project from GitHub
- Set environment variables in Railway dashboard
- Deploy! Railway will detect Node.js and run `npm start` (add start script if needed)

## How It Works

1. User sends message or uses `/newpost <text>`
2. Bot stores the post and notifies user it's pending
3. User can:
   - `/approve_<id>` to see post with hashtags and confirm
   - `/edit_<id> <new text>` to edit
   - `/reject_<id>` to delete
4. After confirmation, bot simulates publishing (replace with actual API call)
5. For spam/daily checks: implement separate process to scan pending/posts and remove inappropriate content

## Customization

- Replace hashtag generation with AI API call (Groq, Claude, etc.)
- Replace in-memory storage with Redis or database
- Implement actual publishing to your site via webhook or git commit
- Add rate limiting and error handling

## Files

- `server.js` - Main bot logic
- `package.json` - Dependencies and scripts
- `.env` - Environment variables (not committed)
- `README.md` - This documentation

## License

MIT
```

### 8. Create .env.example
```env
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
WEBHOOK_URL=https://your-domain.com
PORT=3000
```

### 9. Initialize Git and Push to GitHub (Optional but Recommended)
```bash
git init
git add .
git commit -m "Initial commit: Telegram content approval bot"
# Create repo on GitHub first, then:
git remote add origin https://github.com/yourusername/your-repo.git
git push -u origin main
```

### 10. Set Webhook (if not set automatically on start)
```bash
# Using curl with your bot token and webhook URL
curl -F "url=https://your-domain.com/bot<TOKEN>" https://api.telegram.org/bot<TOKEN>/setWebhook
```

### 11. Daily Spam/Moderation Check (Example)
Create a separate script to run daily (via cron) that:
- Scans pending posts for spam/off-topic content
- Uses simple keyword filtering or AI moderation
- Notifies admin or auto-rejects inappropriate posts
- Example cron: `0 9 * * * /usr/bin/node /path/to/moderate.js`

## Verification

- Bot responds to `/start`
- Accepts new posts via `/newpost` or plain text
- Shows approval flow with `/approve_<id>`
- Simulates publishing on `/confirm_<id>`
- Handles edit and reject correctly

## Troubleshooting

- **Webhook not set**: Ensure WEBHOOK_URL is publicly accessible and correct
- **Bot not responding**: Check server logs, ensure port is open and not blocked by firewall
- **GitHub push errors**: Verify PAT has repo scope and correct remote URL
- **Memory leaks**: For long-running, consider using a process manager like PM2

## Notes

- This skill creates a basic bot; for production, add:
  - Proper database (Redis, PostgreSQL)
  - AI-powered hashtag generation
  - Actual publishing integration
  - Comprehensive error logging
  - Input sanitization
- The bot is designed to work with static sites like Sailor.bar where publishing can be triggered via git push or API endpoint.
- Always keep your bot token secure; never commit .env to public repo.

## Author

Created by Hermes Agent for Sailor.bar workflow.