Why Telegram for Signal Delivery?
Telegram is the platform of choice for signal delivery for good reasons: it has a powerful bot API, supports rich message formatting, allows channels with unlimited subscribers, and has no algorithmic feed that buries your content. When you send a signal, every subscriber sees it immediately.
Compared to Discord (complex, gaming-oriented), email (slow, spam-filtered), or Twitter (algorithm-dependent), Telegram gives you the most direct line to your audience. This guide walks you through building a complete signal bot from scratch in about 30 minutes.
Step 1: Create Your Bot via BotFather
Every Telegram bot starts with BotFather, Telegram's official bot-creation tool:
- Open Telegram and search for @BotFather
- Send the command
/newbot - Enter a display name for your bot (e.g., "Crypto Signal Pro")
- Enter a username for your bot (must end in "bot", e.g., "CryptoSignalProBot")
- BotFather will respond with your bot token — save this securely. It looks like:
7123456789:AAF1kD3..._xG4qwY
Configure Bot Settings
While still in BotFather, configure these settings:
/setdescription— Set a description that appears when users first find your bot/setabouttext— Brief text shown in the bot's profile/setcommands— Define the commands your bot supports. Add at minimum:start - Subscribe to signals stop - Unsubscribe from signals status - Check your subscription status
Step 2: Set Up the Node.js Project
Create your project directory and install dependencies:
mkdir signal-bot && cd signal-bot
npm init -y
npm install node-telegram-bot-api node-cron better-sqlite3 dotenv
Create a .env file for your configuration:
BOT_TOKEN=your-bot-token-here
ADMIN_CHAT_ID=your-personal-chat-id
Project Structure
signal-bot/
.env
bot.js # Main bot logic
data.js # Data source connection
scheduler.js # Signal scheduling
db.js # Subscriber database
package.json
Step 3: Build the Bot Core
Create bot.js with the basic subscriber management:
import TelegramBot from 'node-telegram-bot-api';
import Database from 'better-sqlite3';
import 'dotenv/config';
const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true });
const db = new Database('subscribers.db');
// Create subscriber table
db.exec(`
CREATE TABLE IF NOT EXISTS subscribers (
chat_id INTEGER PRIMARY KEY,
username TEXT,
subscribed_at TEXT DEFAULT (datetime('now')),
active INTEGER DEFAULT 1
)
`);
// /start command - subscribe
bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
const username = msg.from.username || 'unknown';
db.prepare(
'INSERT OR REPLACE INTO subscribers (chat_id, username, active) VALUES (?, ?, 1)'
).run(chatId, username);
bot.sendMessage(chatId,
'Welcome! You are now subscribed to signals.\n' +
'You will receive automated alerts as they are generated.\n\n' +
'Commands:\n/stop - Unsubscribe\n/status - Check subscription'
);
});
// /stop command - unsubscribe
bot.onText(/\/stop/, (msg) => {
db.prepare('UPDATE subscribers SET active = 0 WHERE chat_id = ?')
.run(msg.chat.id);
bot.sendMessage(msg.chat.id, 'You have been unsubscribed. Use /start to resubscribe.');
});
// /status command
bot.onText(/\/status/, (msg) => {
const sub = db.prepare('SELECT * FROM subscribers WHERE chat_id = ?')
.get(msg.chat.id);
if (sub && sub.active) {
bot.sendMessage(msg.chat.id, 'Active subscriber since ' + sub.subscribed_at);
} else {
bot.sendMessage(msg.chat.id, 'Not subscribed. Use /start to subscribe.');
}
});
// Broadcast function - send signal to all active subscribers
export function broadcastSignal(message) {
const subs = db.prepare('SELECT chat_id FROM subscribers WHERE active = 1').all();
let sent = 0;
for (const sub of subs) {
try {
bot.sendMessage(sub.chat_id, message, { parse_mode: 'HTML' });
sent++;
} catch (err) {
console.error('Failed to send to ' + sub.chat_id, err.message);
}
}
return sent;
}
export default bot;
Step 4: Connect to Your Data Source
The bot needs data to generate signals. This depends on your niche, but here is the general pattern using a data.js module:
// Example: Fetch crypto price data
export async function fetchSignalData() {
const response = await fetch(
'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd&include_24hr_change=true'
);
const data = await response.json();
return data;
}
// Example: Generate a signal from the data
export function generateSignal(data) {
const btcChange = data.bitcoin.usd_24h_change;
const ethChange = data.ethereum.usd_24h_change;
// Simple momentum signal
if (btcChange > 5) {
return {
type: 'BULLISH',
asset: 'BTC',
message: 'BTC up ' + btcChange.toFixed(1) + '% in 24h. Strong momentum.'
};
}
if (btcChange < -5) {
return {
type: 'BEARISH',
asset: 'BTC',
message: 'BTC down ' + btcChange.toFixed(1) + '% in 24h. Risk off.'
};
}
return null; // No signal
}
Replace this with your actual data source: market data APIs, web scrapers, database queries, or AI analysis pipelines. The key is that fetchSignalData() returns raw data and generateSignal() turns it into an actionable message.
Step 5: Schedule Signal Delivery
Use node-cron to run your signal generation on a schedule:
import cron from 'node-cron';
import { fetchSignalData, generateSignal } from './data.js';
import { broadcastSignal } from './bot.js';
// Run every hour
cron.schedule('0 * * * *', async () => {
console.log('Checking for signals...');
const data = await fetchSignalData();
const signal = generateSignal(data);
if (signal) {
const message =
'<b>' + signal.type + ' SIGNAL: ' + signal.asset + '</b>\n\n' +
signal.message + '\n\n' +
'<i>Generated at ' + new Date().toISOString() + '</i>';
const sent = broadcastSignal(message);
console.log('Signal sent to ' + sent + ' subscribers');
} else {
console.log('No signal generated this cycle');
}
});
// Daily summary at 8 AM UTC
cron.schedule('0 8 * * *', async () => {
const data = await fetchSignalData();
const summary =
'<b>Daily Market Summary</b>\n\n' +
'BTC: $' + data.bitcoin.usd.toLocaleString() +
' (' + data.bitcoin.usd_24h_change.toFixed(1) + '%)\n' +
'ETH: $' + data.ethereum.usd.toLocaleString() +
' (' + data.ethereum.usd_24h_change.toFixed(1) + '%)';
broadcastSignal(summary);
});
Step 6: Add Subscriber Management
For a production bot, you need admin controls. Add these commands that only respond to your admin chat ID:
// Admin: get subscriber count
bot.onText(/\/stats/, (msg) => {
if (String(msg.chat.id) !== process.env.ADMIN_CHAT_ID) return;
const total = db.prepare('SELECT COUNT(*) as c FROM subscribers').get().c;
const active = db.prepare('SELECT COUNT(*) as c FROM subscribers WHERE active = 1').get().c;
bot.sendMessage(msg.chat.id,
'Subscribers: ' + active + ' active / ' + total + ' total'
);
});
// Admin: manually broadcast a message
bot.onText(/\/broadcast (.+)/, (msg, match) => {
if (String(msg.chat.id) !== process.env.ADMIN_CHAT_ID) return;
const sent = broadcastSignal(match[1]);
bot.sendMessage(msg.chat.id, 'Broadcast sent to ' + sent + ' subscribers');
});
Deploying Your Bot
Your bot needs to run 24/7 to respond to subscriber commands and send scheduled signals. The simplest deployment:
- Spin up a $6/month DigitalOcean droplet (Ubuntu 22.04)
- Clone your project and install dependencies
- Create a systemd service:
[Unit]
Description=Telegram Signal Bot
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/signal-bot
ExecStart=/usr/bin/node bot.js
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start: sudo systemctl enable signal-bot && sudo systemctl start signal-bot
Monetization Options
Once you have subscribers receiving value from your signals, here are the monetization paths:
- Free tier + paid premium: Basic signals free, advanced analysis behind a paywall ($29-99/month via Stripe or Gumroad)
- Fully paid: All signals behind a subscription. Use a payment bot like @tribute_bot to manage access.
- Affiliate revenue: Include relevant affiliate links in your signals (e.g., exchange referral links in trading signals)
- Sponsorships: Once you have 500+ subscribers, companies will pay $200-$1,000/month for sponsored mentions in your signal feed
The best signal bots focus on one niche and deliver consistent, actionable value. Do not try to cover everything. A bot that sends 2-3 high-quality signals per day will retain subscribers better than one that sends 20 mediocre alerts.