Skip to main content
PriceSignal integrates with Telegram to deliver instant notifications when your price rules are triggered. This guide walks you through setting up the Telegram bot and connecting it to your account.

Architecture Overview

The Telegram integration consists of:
  1. Go-based Telegram bot (src/telegram-bot/main.go) - Handles message sending/receiving
  2. NATS JetStream - Message broker for reliable notification delivery
  3. Backend API - Manages notification channels and triggers alerts

Prerequisites

  • Telegram account
  • Bot token from @BotFather
  • NATS server running and accessible
  • Authenticated PriceSignal account

Bot Configuration

1

Create a Telegram bot

  1. Open Telegram and message @BotFather
  2. Send /newbot command
  3. Follow prompts to name your bot
  4. Save the bot token provided (format: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz)
2

Configure environment variables

Create a .env file in the src/telegram-bot/ directory:
TELEGRAM_BOT_TOKEN=your_bot_token_here
NATS_URL=nats://localhost:4222
Environment Variables:
  • TELEGRAM_BOT_TOKEN: Your bot token from BotFather
  • NATS_URL: Connection string for NATS server
3

Start the bot service

Run the Telegram bot:
cd src/telegram-bot
go run main.go
You should see:
Authorized on account YourBotName
4

Link your Telegram account

  1. Get your PriceSignal user ID from the backend (visible after login)
  2. Send a message to your bot with your user ID:
    /start YOUR_USER_ID
    
  3. The bot will respond:
    Hello, your chat ID has been recorded!
    
Your Telegram account is now linked to PriceSignal.

How It Works

Registration Flow

When you message the bot, the following happens:
  1. Bot receives message with your user ID (main.go:99-128):
    chatID := update.Message.Chat.ID
    username := update.Message.From.UserName
    userId := update.Message.CommandArguments()
    
  2. Message published to NATS on notifications.init.telegram subject:
    chatIDMessage := ChatIDMessage{
        ChatID:   chatID,
        Username: username,
        UserId:   userId,
    }
    data, _ := json.Marshal(chatIDMessage)
    js.PublishAsync("notifications.init.telegram", data)
    
  3. Backend receives registration (Program.cs:216-240):
    natsService.Subscribe<TelegramInit>("notifications", message =>
    {
        var user = dbContext.Users.FirstOrDefault(u => 
            u.Id == message.User_Id &&
            u.NotificationChannels.FirstOrDefault(nc => 
                nc.TelegramChatId == message.Chat_Id) == null);
        
        if (user != null)
        {
            var channel = new UserNotificationChannel
            {
                User = user,
                ChannelType = NotificationChannelType.telegram,
                TelegramChatId = message.Chat_Id,
                TelegramUsername = message.Username
            };
            dbContext.UserNotificationChannels.Add(channel);
            dbContext.SaveChanges();
        }
    }, "notifications.init.telegram");
    

Notification Delivery

When a price rule is triggered:
  1. Backend publishes notification to NATS on notifications.telegram subject
  2. Bot consumes message via JetStream consumer (main.go:72-92):
    c, _ := s.CreateOrUpdateConsumer(ctx, jetstream.ConsumerConfig{
        Durable:       "telegram",
        FilterSubject: "notifications.telegram",
        AckPolicy:     jetstream.AckExplicitPolicy,
    })
    
    c.Consume(func(msg jetstream.Msg) {
        var notification Notification
        json.Unmarshal(msg.Data(), &notification)
        
        telegramMsg := tgbotapi.NewMessage(notification.ChatID, notification.Message)
        bot.Send(telegramMsg)
        msg.Ack()
    })
    
  3. Message sent to your Telegram chat

Message Format

Registration Message

Sent from backend to bot on notifications.init.telegram:
{
  "chat_id": 123456789,
  "username": "your_username",
  "user_id": "firebase_user_id"
}

Notification Message

Sent from backend to bot on notifications.telegram:
{
  "chat_id": 123456789,
  "message": "Alert: BTC has reached $50,000"
}

NATS Configuration

The bot creates a JetStream stream for reliable message delivery:
js, _ := jetstream.New(nc)
s, _ := js.CreateOrUpdateStream(ctx, jetstream.StreamConfig{
    Name:     "notifications",
    Subjects: []string{"notifications.>"},
})
Stream configuration:
  • Name: notifications
  • Subjects: notifications.* (wildcard for all notification types)
  • Consumer: Durable consumer named telegram for persistent subscription

Managing Notification Channels

You can manage your notification channels via GraphQL:

Query Notification Channels

query GetMyNotificationChannels {
  notificationChannels {
    edges {
      node {
        id
        channelType
        telegramChatId
        telegramUsername
        createdAt
      }
    }
  }
}

Delete Notification Channel

To unlink your Telegram account:
mutation DeleteNotificationChannel($id: UUID!) {
  deleteNotificationChannel(id: $id) {
    id
  }
}

Multiple Accounts

You can link multiple Telegram accounts or chats to the same PriceSignal user:
  1. Send the bot command from different Telegram accounts
  2. Each account will be registered as a separate notification channel
  3. Alerts will be sent to all registered channels

Troubleshooting

Bot not responding

  • Verify bot token is correct in .env
  • Check bot is running: ps aux | grep telegram-bot
  • Ensure bot is not blocked in Telegram settings

Not receiving notifications

  • Confirm notification channel is registered:
    query { notificationChannels { edges { node { telegramChatId } } } }
    
  • Check NATS server is running and accessible
  • Verify price rules are enabled
  • Check bot logs for errors

Chat ID not recorded

  • Ensure user ID is passed correctly in the command
  • Verify user exists in database
  • Check backend logs for NATS subscription errors

Production Deployment

The Telegram bot should be deployed as a long-running service with automatic restart on failure.

Systemd Service Example

Create /etc/systemd/system/pricesignal-telegram.service:
[Unit]
Description=PriceSignal Telegram Bot
After=network.target nats.service

[Service]
Type=simple
User=pricesignal
WorkingDirectory=/opt/pricesignal/telegram-bot
EnvironmentFile=/opt/pricesignal/telegram-bot/.env
ExecStart=/opt/pricesignal/telegram-bot/telegram-bot
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable pricesignal-telegram
sudo systemctl start pricesignal-telegram

Docker Deployment

FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o telegram-bot main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/telegram-bot .
CMD ["./telegram-bot"]

Security Considerations

  • Never commit bot tokens to version control
  • Use environment variables for all sensitive configuration
  • Validate user IDs before creating notification channels
  • Consider implementing rate limiting for bot commands
  • Use NATS authentication in production environments
  • Enable TLS for NATS connections

Next Steps

Creating Rules

Set up price rules to trigger notifications

Real-time Charts

Monitor prices with live charting

Build docs developers (and LLMs) love