Telegram Bot Development for ChatGPT Apps: Complete Guide

Telegram's Bot Platform powers over 700 million monthly active users with rich interactive capabilities that make it the ideal messaging platform for ChatGPT app deployment. Unlike traditional chatbot platforms, Telegram bots can handle payments, inline queries, media uploads, and complex keyboard interactions—all through a developer-friendly HTTP API.

This guide provides production-ready code for building Telegram bots that integrate seamlessly with ChatGPT apps, covering everything from basic message handling to advanced payment processing and media management.

Understanding the Telegram Bot API Architecture

The Telegram Bot API operates on a webhook-based or polling architecture where your server receives updates from Telegram's servers. For ChatGPT app integration, webhooks provide the best performance and scalability.

Core Bot API Concepts

Bot Token Authentication: Every bot receives a unique token from @BotFather that authenticates API requests. Store this securely as an environment variable.

Update Objects: Telegram sends updates containing messages, callback queries, inline queries, or payment notifications. Your bot processes these updates and responds accordingly.

Chat Types: Bots can operate in private chats, groups, supergroups, and channels—each with different permission models and interaction patterns.

Inline Mode: Users can invoke your bot from any chat using @yourbotname query, enabling cross-chat functionality without adding the bot to every conversation.

Webhook vs. Polling Trade-offs

Aspect Webhooks Long Polling
Latency <100ms 1-3 seconds
Server Load Event-driven Constant polling
Scalability Horizontal Limited
SSL Requirement Required Optional
Best For Production Development

For ChatGPT apps targeting enterprise users, webhooks are mandatory to ensure sub-second response times and efficient resource utilization.

Production-Ready Telegram Client Implementation

This Telegram client handles authentication, webhook setup, and message sending with automatic retry logic and error handling.

// telegram-client.js (120 lines)
const axios = require('axios');
const crypto = require('crypto');

class TelegramClient {
  constructor(botToken, webhookUrl = null) {
    if (!botToken) {
      throw new Error('Bot token is required');
    }

    this.botToken = botToken;
    this.baseUrl = `https://api.telegram.org/bot${botToken}`;
    this.webhookUrl = webhookUrl;
    this.maxRetries = 3;
    this.retryDelay = 1000; // 1 second
  }

  /**
   * Initialize bot with webhook configuration
   * @param {string} secretToken - Random string for webhook validation
   */
  async initialize(secretToken = null) {
    try {
      // Get bot info
      const botInfo = await this.apiRequest('getMe');
      console.log(`Bot initialized: @${botInfo.username}`);

      // Set webhook if URL provided
      if (this.webhookUrl) {
        const webhookConfig = {
          url: this.webhookUrl,
          max_connections: 100,
          allowed_updates: [
            'message',
            'callback_query',
            'inline_query',
            'pre_checkout_query',
            'shipping_query'
          ]
        };

        if (secretToken) {
          webhookConfig.secret_token = secretToken;
        }

        await this.apiRequest('setWebhook', webhookConfig);
        console.log(`Webhook set: ${this.webhookUrl}`);
      }

      return botInfo;
    } catch (error) {
      console.error('Bot initialization failed:', error.message);
      throw error;
    }
  }

  /**
   * Make API request with automatic retry
   * @param {string} method - Telegram API method name
   * @param {object} params - Method parameters
   */
  async apiRequest(method, params = {}, retryCount = 0) {
    try {
      const response = await axios.post(
        `${this.baseUrl}/${method}`,
        params,
        {
          headers: { 'Content-Type': 'application/json' },
          timeout: 10000
        }
      );

      if (!response.data.ok) {
        throw new Error(response.data.description || 'API request failed');
      }

      return response.data.result;
    } catch (error) {
      // Retry on network errors or rate limits
      if (retryCount < this.maxRetries && this.shouldRetry(error)) {
        await this.sleep(this.retryDelay * (retryCount + 1));
        return this.apiRequest(method, params, retryCount + 1);
      }

      throw error;
    }
  }

  /**
   * Send text message with optional inline keyboard
   * @param {number} chatId - Target chat ID
   * @param {string} text - Message text (supports Markdown/HTML)
   * @param {object} options - Additional options
   */
  async sendMessage(chatId, text, options = {}) {
    const params = {
      chat_id: chatId,
      text: text,
      parse_mode: options.parseMode || 'Markdown',
      disable_web_page_preview: options.disablePreview || false,
      ...options
    };

    return this.apiRequest('sendMessage', params);
  }

  /**
   * Edit existing message text
   */
  async editMessageText(chatId, messageId, text, options = {}) {
    return this.apiRequest('editMessageText', {
      chat_id: chatId,
      message_id: messageId,
      text: text,
      parse_mode: options.parseMode || 'Markdown',
      ...options
    });
  }

  /**
   * Answer callback query (inline button press)
   */
  async answerCallbackQuery(callbackQueryId, options = {}) {
    return this.apiRequest('answerCallbackQuery', {
      callback_query_id: callbackQueryId,
      text: options.text || '',
      show_alert: options.showAlert || false,
      cache_time: options.cacheTime || 0
    });
  }

  /**
   * Send invoice for payments
   */
  async sendInvoice(chatId, invoiceData) {
    return this.apiRequest('sendInvoice', {
      chat_id: chatId,
      ...invoiceData
    });
  }

  /**
   * Validate webhook request authenticity
   */
  validateWebhook(secretToken, requestToken) {
    if (!secretToken) return true; // No validation if secret not set
    return crypto.timingSafeEqual(
      Buffer.from(secretToken),
      Buffer.from(requestToken || '')
    );
  }

  shouldRetry(error) {
    // Retry on network errors or 429 rate limits
    return (
      !error.response ||
      error.response.status === 429 ||
      error.response.status >= 500
    );
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

module.exports = TelegramClient;

Key Implementation Details

Retry Logic: The client automatically retries failed requests up to 3 times with exponential backoff, handling Telegram's rate limits gracefully.

Webhook Validation: The validateWebhook() method uses timing-safe comparison to prevent timing attacks when validating the X-Telegram-Bot-Api-Secret-Token header.

Flexible Message Sending: Supports both Markdown and HTML parsing modes, web page preview control, and optional reply keyboards.

Building Interactive Inline Keyboards

Inline keyboards provide rich interactive UI directly in Telegram messages, enabling ChatGPT apps to offer guided conversations and quick actions.

// keyboard-handler.js (130 lines)
class KeyboardHandler {
  /**
   * Create inline keyboard with callback buttons
   * @param {Array} buttons - Array of button rows
   * @returns {object} Inline keyboard markup
   */
  static createInlineKeyboard(buttons) {
    return {
      inline_keyboard: buttons.map(row =>
        row.map(btn => ({
          text: btn.text,
          callback_data: btn.callback || btn.text,
          url: btn.url || undefined,
          login_url: btn.loginUrl || undefined,
          switch_inline_query: btn.switchInline || undefined,
          pay: btn.pay || undefined
        }))
      )
    };
  }

  /**
   * Create navigation keyboard for multi-page content
   */
  static createPaginationKeyboard(currentPage, totalPages, baseCallback) {
    const buttons = [];

    // Previous button
    if (currentPage > 1) {
      buttons.push({
        text: '◀️ Previous',
        callback: `${baseCallback}:${currentPage - 1}`
      });
    }

    // Page indicator
    buttons.push({
      text: `${currentPage} / ${totalPages}`,
      callback: 'noop'
    });

    // Next button
    if (currentPage < totalPages) {
      buttons.push({
        text: 'Next ▶️',
        callback: `${baseCallback}:${currentPage + 1}`
      });
    }

    return this.createInlineKeyboard([buttons]);
  }

  /**
   * Create ChatGPT app selection menu
   */
  static createAppSelectionKeyboard(apps, action = 'select') {
    const buttons = apps.map(app => [{
      text: `${app.icon || '🤖'} ${app.name}`,
      callback: `${action}:${app.id}`
    }]);

    // Add cancel/back button
    buttons.push([{
      text: '❌ Cancel',
      callback: 'cancel'
    }]);

    return this.createInlineKeyboard(buttons);
  }

  /**
   * Create confirmation dialog keyboard
   */
  static createConfirmationKeyboard(action, itemId = null) {
    const confirmCallback = itemId
      ? `confirm:${action}:${itemId}`
      : `confirm:${action}`;

    const cancelCallback = itemId
      ? `cancel:${action}:${itemId}`
      : 'cancel';

    return this.createInlineKeyboard([
      [
        { text: '✅ Confirm', callback: confirmCallback },
        { text: '❌ Cancel', callback: cancelCallback }
      ]
    ]);
  }

  /**
   * Create settings menu keyboard
   */
  static createSettingsKeyboard(userSettings) {
    const buttons = [
      [{
        text: `🔔 Notifications: ${userSettings.notifications ? 'ON' : 'OFF'}`,
        callback: 'toggle:notifications'
      }],
      [{
        text: `🌐 Language: ${userSettings.language.toUpperCase()}`,
        callback: 'settings:language'
      }],
      [{
        text: `🎨 Theme: ${userSettings.theme}`,
        callback: 'settings:theme'
      }],
      [{
        text: '🔙 Back to Menu',
        callback: 'menu:main'
      }]
    ];

    return this.createInlineKeyboard(buttons);
  }

  /**
   * Create payment keyboard with buy button
   */
  static createPaymentKeyboard(productId, price) {
    return this.createInlineKeyboard([
      [{
        text: `💳 Pay $${price}`,
        pay: true
      }],
      [{
        text: '❌ Cancel',
        callback: 'cancel:payment'
      }]
    ]);
  }

  /**
   * Parse callback data into structured object
   * @param {string} callbackData - Callback data string
   * @returns {object} Parsed callback object
   */
  static parseCallback(callbackData) {
    const parts = callbackData.split(':');

    return {
      action: parts[0],
      subaction: parts[1] || null,
      params: parts.slice(2) || []
    };
  }

  /**
   * Create dynamic keyboard from ChatGPT response
   * @param {Array} actions - Array of action objects from ChatGPT
   */
  static fromChatGPTActions(actions) {
    const buttons = actions.map(action => [{
      text: action.label,
      callback: action.callback || action.id,
      url: action.url || undefined
    }]);

    return this.createInlineKeyboard(buttons);
  }
}

module.exports = KeyboardHandler;

Keyboard Design Best Practices

Callback Data Limits: Telegram limits callback data to 64 bytes. Use compact encoding like action:subaction:id instead of JSON strings.

Button Grouping: Group related actions in rows (max 8 buttons per row, 100 buttons total) to create scannable interfaces.

Visual Hierarchy: Use emojis sparingly to highlight primary actions (✅ for confirm, ❌ for cancel, 💳 for payments).

Handling Callback Queries and State Management

Callback queries require stateful processing to maintain conversation context across button interactions.

// callback-handler.js (110 lines)
class CallbackHandler {
  constructor(telegramClient, stateManager) {
    this.client = telegramClient;
    this.state = stateManager;
  }

  /**
   * Process callback query from inline button
   * @param {object} callbackQuery - Telegram callback query object
   */
  async handleCallback(callbackQuery) {
    const { id, from, message, data } = callbackQuery;
    const chatId = message.chat.id;
    const messageId = message.message_id;

    try {
      // Parse callback data
      const callback = KeyboardHandler.parseCallback(data);

      // Get user state
      const userState = await this.state.get(from.id);

      // Route to appropriate handler
      let response;
      switch (callback.action) {
        case 'select':
          response = await this.handleAppSelection(
            chatId,
            messageId,
            callback.params[0],
            userState
          );
          break;

        case 'confirm':
          response = await this.handleConfirmation(
            chatId,
            messageId,
            callback.subaction,
            callback.params,
            userState
          );
          break;

        case 'toggle':
          response = await this.handleToggle(
            chatId,
            messageId,
            callback.subaction,
            userState
          );
          break;

        case 'menu':
          response = await this.handleMenuNavigation(
            chatId,
            messageId,
            callback.subaction,
            userState
          );
          break;

        case 'cancel':
          response = await this.handleCancel(chatId, messageId);
          break;

        default:
          response = { text: 'Unknown action' };
      }

      // Answer callback query
      await this.client.answerCallbackQuery(id, {
        text: response.notification || '✅',
        showAlert: response.showAlert || false
      });

      // Update message if needed
      if (response.updateMessage) {
        await this.client.editMessageText(
          chatId,
          messageId,
          response.text,
          { reply_markup: response.keyboard }
        );
      }

    } catch (error) {
      console.error('Callback handling error:', error);

      await this.client.answerCallbackQuery(id, {
        text: '❌ Error processing request',
        showAlert: true
      });
    }
  }

  async handleAppSelection(chatId, messageId, appId, userState) {
    // Update user state with selected app
    await this.state.set(userState.userId, {
      ...userState,
      selectedApp: appId,
      step: 'app_selected'
    });

    return {
      notification: 'App selected',
      updateMessage: true,
      text: '✅ *App Selected*\n\nWhat would you like to do with this app?',
      keyboard: KeyboardHandler.createInlineKeyboard([
        [{ text: '▶️ Launch', callback: `launch:${appId}` }],
        [{ text: '⚙️ Configure', callback: `configure:${appId}` }],
        [{ text: '🗑️ Delete', callback: `delete:${appId}` }],
        [{ text: '🔙 Back', callback: 'menu:apps' }]
      ])
    };
  }

  async handleConfirmation(chatId, messageId, action, params, userState) {
    // Execute confirmed action
    const result = await this.executeAction(action, params, userState);

    return {
      notification: result.success ? '✅ Done' : '❌ Failed',
      showAlert: !result.success,
      updateMessage: true,
      text: result.message,
      keyboard: result.nextKeyboard || null
    };
  }

  async handleToggle(chatId, messageId, setting, userState) {
    // Toggle setting value
    const newValue = !userState.settings[setting];

    await this.state.update(userState.userId, {
      [`settings.${setting}`]: newValue
    });

    return {
      notification: `${setting} ${newValue ? 'enabled' : 'disabled'}`,
      updateMessage: true,
      text: '⚙️ *Settings Updated*',
      keyboard: KeyboardHandler.createSettingsKeyboard({
        ...userState.settings,
        [setting]: newValue
      })
    };
  }

  async executeAction(action, params, userState) {
    // Implement action-specific logic here
    // This would integrate with your ChatGPT app backend
    return {
      success: true,
      message: `Action ${action} executed successfully`
    };
  }
}

module.exports = CallbackHandler;

State Management Strategies

Redis for Session Storage: Store user conversation state in Redis with 24-hour TTL to maintain context across interactions:

const redis = require('redis');
const client = redis.createClient();

class StateManager {
  async get(userId) {
    const state = await client.get(`user:${userId}`);
    return state ? JSON.parse(state) : this.defaultState(userId);
  }

  async set(userId, state) {
    await client.setex(`user:${userId}`, 86400, JSON.stringify(state));
  }

  defaultState(userId) {
    return {
      userId,
      step: 'start',
      selectedApp: null,
      settings: {
        notifications: true,
        language: 'en',
        theme: 'light'
      }
    };
  }
}

Telegram Payments Integration

Telegram's native payment system supports 15+ payment providers including Stripe, enabling in-chat purchases for ChatGPT app subscriptions and premium features.

// payment-processor.js (100 lines)
class PaymentProcessor {
  constructor(telegramClient, providerToken) {
    this.client = telegramClient;
    this.providerToken = providerToken; // From payment provider
  }

  /**
   * Create and send invoice to user
   * @param {number} chatId - User chat ID
   * @param {object} product - Product details
   */
  async sendInvoice(chatId, product) {
    const invoiceData = {
      title: product.name,
      description: product.description,
      payload: JSON.stringify({
        productId: product.id,
        userId: chatId,
        timestamp: Date.now()
      }),
      provider_token: this.providerToken,
      currency: product.currency || 'USD',
      prices: product.prices.map(price => ({
        label: price.label,
        amount: price.amount * 100 // Amount in cents
      })),
      max_tip_amount: product.maxTip ? product.maxTip * 100 : undefined,
      suggested_tip_amounts: product.suggestedTips?.map(tip => tip * 100),
      photo_url: product.imageUrl,
      need_name: product.collectName || false,
      need_phone_number: product.collectPhone || false,
      need_email: product.collectEmail || false,
      need_shipping_address: product.needsShipping || false,
      is_flexible: product.flexibleShipping || false
    };

    return this.client.sendInvoice(chatId, invoiceData);
  }

  /**
   * Handle pre-checkout query (validate before payment)
   * @param {object} preCheckoutQuery - Telegram pre-checkout query
   */
  async handlePreCheckout(preCheckoutQuery) {
    const { id, from, invoice_payload, total_amount } = preCheckoutQuery;

    try {
      // Parse payload
      const payload = JSON.parse(invoice_payload);

      // Validate payment (check inventory, user eligibility, etc.)
      const isValid = await this.validatePayment(
        payload.productId,
        from.id,
        total_amount / 100
      );

      if (!isValid.success) {
        // Reject payment with error message
        return this.client.apiRequest('answerPreCheckoutQuery', {
          pre_checkout_query_id: id,
          ok: false,
          error_message: isValid.error
        });
      }

      // Approve payment
      return this.client.apiRequest('answerPreCheckoutQuery', {
        pre_checkout_query_id: id,
        ok: true
      });

    } catch (error) {
      console.error('Pre-checkout error:', error);

      return this.client.apiRequest('answerPreCheckoutQuery', {
        pre_checkout_query_id: id,
        ok: false,
        error_message: 'Payment validation failed'
      });
    }
  }

  /**
   * Handle successful payment
   * @param {object} message - Telegram message with successful_payment field
   */
  async handleSuccessfulPayment(message) {
    const payment = message.successful_payment;
    const chatId = message.chat.id;
    const userId = message.from.id;

    try {
      // Parse payload
      const payload = JSON.parse(payment.invoice_payload);

      // Record payment in database
      await this.recordPayment({
        userId,
        productId: payload.productId,
        amount: payment.total_amount / 100,
        currency: payment.currency,
        providerChargeId: payment.provider_payment_charge_id,
        telegramChargeId: payment.telegram_payment_charge_id,
        timestamp: payload.timestamp
      });

      // Activate purchased product/subscription
      await this.activateProduct(userId, payload.productId);

      // Send confirmation message
      await this.client.sendMessage(
        chatId,
        '✅ *Payment Successful!*\n\n' +
        `Thank you for your purchase of *${payload.productId}*.\n\n` +
        'Your subscription is now active.',
        {
          reply_markup: KeyboardHandler.createInlineKeyboard([
            [{ text: '🚀 Get Started', callback: 'start:app' }],
            [{ text: '📧 Email Receipt', callback: 'receipt:email' }]
          ])
        }
      );

    } catch (error) {
      console.error('Payment processing error:', error);

      await this.client.sendMessage(
        chatId,
        '⚠️ Payment received but activation failed. Support has been notified.'
      );
    }
  }

  async validatePayment(productId, userId, amount) {
    // Implement validation logic (inventory, pricing, user limits)
    return { success: true };
  }

  async recordPayment(paymentData) {
    // Store in database
    console.log('Payment recorded:', paymentData);
  }

  async activateProduct(userId, productId) {
    // Activate subscription or unlock features
    console.log(`Activated ${productId} for user ${userId}`);
  }
}

module.exports = PaymentProcessor;

Payment Security Considerations

Payload Validation: Always validate the invoice_payload timestamp to prevent replay attacks (reject if older than 10 minutes).

Amount Verification: In handlePreCheckout(), verify the total_amount matches your expected pricing to prevent price manipulation.

Provider Webhook Reconciliation: Cross-reference Telegram payment notifications with your payment provider's webhooks for authoritative payment confirmation.

Media Handling and File Management

Telegram bots can send and receive photos, videos, documents, and audio files up to 50MB (2GB for documents).

// media-manager.js (80 lines)
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

class MediaManager {
  constructor(telegramClient) {
    this.client = telegramClient;
  }

  /**
   * Send photo with caption
   * @param {number} chatId - Target chat ID
   * @param {string|Buffer} photo - File path, URL, or Buffer
   * @param {object} options - Additional options
   */
  async sendPhoto(chatId, photo, options = {}) {
    const params = {
      chat_id: chatId,
      photo: photo,
      caption: options.caption || '',
      parse_mode: options.parseMode || 'Markdown',
      reply_markup: options.replyMarkup || undefined
    };

    // Handle file upload vs file_id
    if (typeof photo === 'string' && photo.startsWith('http')) {
      // Send via URL
      return this.client.apiRequest('sendPhoto', params);
    } else if (Buffer.isBuffer(photo) || fs.existsSync(photo)) {
      // Upload file
      return this.uploadMedia('sendPhoto', params, photo);
    } else {
      // Assume file_id
      return this.client.apiRequest('sendPhoto', params);
    }
  }

  /**
   * Send document (any file type)
   */
  async sendDocument(chatId, document, options = {}) {
    const params = {
      chat_id: chatId,
      document: document,
      caption: options.caption || '',
      disable_content_type_detection: options.disableTypeDetection || false,
      reply_markup: options.replyMarkup || undefined
    };

    if (Buffer.isBuffer(document) || fs.existsSync(document)) {
      return this.uploadMedia('sendDocument', params, document);
    } else {
      return this.client.apiRequest('sendDocument', params);
    }
  }

  /**
   * Get file download URL
   * @param {string} fileId - Telegram file_id
   */
  async getFileUrl(fileId) {
    const file = await this.client.apiRequest('getFile', { file_id: fileId });
    return `https://api.telegram.org/file/bot${this.client.botToken}/${file.file_path}`;
  }

  /**
   * Download file from Telegram
   */
  async downloadFile(fileId, outputPath) {
    const fileUrl = await this.getFileUrl(fileId);
    const response = await axios.get(fileUrl, { responseType: 'stream' });

    return new Promise((resolve, reject) => {
      const writer = fs.createWriteStream(outputPath);
      response.data.pipe(writer);
      writer.on('finish', resolve);
      writer.on('error', reject);
    });
  }

  /**
   * Upload media file to Telegram
   */
  async uploadMedia(method, params, file) {
    const form = new FormData();

    // Add file to form
    if (Buffer.isBuffer(file)) {
      form.append(method === 'sendPhoto' ? 'photo' : 'document', file, 'file');
    } else {
      form.append(method === 'sendPhoto' ? 'photo' : 'document', fs.createReadStream(file));
    }

    // Add other params
    Object.keys(params).forEach(key => {
      if (key !== 'photo' && key !== 'document' && params[key] !== undefined) {
        form.append(key, typeof params[key] === 'object'
          ? JSON.stringify(params[key])
          : params[key]
        );
      }
    });

    const response = await axios.post(
      `${this.client.baseUrl}/${method}`,
      form,
      { headers: form.getHeaders() }
    );

    return response.data.result;
  }
}

module.exports = MediaManager;

Media Optimization Best Practices

Image Compression: Compress images to <200KB before sending to reduce bandwidth and improve load times. Use libraries like sharp:

const sharp = require('sharp');

async function compressImage(inputPath, outputPath) {
  await sharp(inputPath)
    .resize(1280, 720, { fit: 'inside' })
    .jpeg({ quality: 80 })
    .toFile(outputPath);
}

File ID Reuse: After uploading a file once, Telegram returns a file_id. Store this ID in your database and reuse it to avoid re-uploading the same file.

CDN Integration: For frequently accessed media (e.g., app screenshots, tutorial videos), host on a CDN and send via URL instead of uploading to Telegram's servers.

Connecting Telegram Bots to ChatGPT Apps

Integration with ChatGPT apps requires an MCP server that bridges Telegram updates with your conversational AI logic.

Webhook Handler Integration

// webhook-handler.js
const express = require('express');
const TelegramClient = require('./telegram-client');
const CallbackHandler = require('./callback-handler');
const PaymentProcessor = require('./payment-processor');

const app = express();
app.use(express.json());

const bot = new TelegramClient(
  process.env.TELEGRAM_BOT_TOKEN,
  process.env.WEBHOOK_URL
);

const callbackHandler = new CallbackHandler(bot, stateManager);
const paymentProcessor = new PaymentProcessor(bot, process.env.PAYMENT_TOKEN);

// Webhook endpoint
app.post('/webhook/telegram', async (req, res) => {
  const update = req.body;

  try {
    // Validate webhook secret
    if (!bot.validateWebhook(
      process.env.WEBHOOK_SECRET,
      req.headers['x-telegram-bot-api-secret-token']
    )) {
      return res.status(401).send('Unauthorized');
    }

    // Route update to appropriate handler
    if (update.message) {
      await handleMessage(update.message);
    } else if (update.callback_query) {
      await callbackHandler.handleCallback(update.callback_query);
    } else if (update.pre_checkout_query) {
      await paymentProcessor.handlePreCheckout(update.pre_checkout_query);
    } else if (update.message?.successful_payment) {
      await paymentProcessor.handleSuccessfulPayment(update.message);
    }

    res.status(200).send('OK');
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).send('Internal Server Error');
  }
});

async function handleMessage(message) {
  const chatId = message.chat.id;
  const text = message.text;

  // Send to ChatGPT for processing
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
    },
    body: JSON.stringify({
      model: 'gpt-4',
      messages: [
        { role: 'system', content: 'You are a helpful assistant for a ChatGPT app.' },
        { role: 'user', content: text }
      ]
    })
  });

  const data = await response.json();
  const reply = data.choices[0].message.content;

  await bot.sendMessage(chatId, reply);
}

app.listen(3000, () => console.log('Webhook server running on port 3000'));

Advanced Features: Inline Mode and Chat Administration

Inline Mode enables users to invoke your bot from any chat without adding it:

// Handle inline queries
app.post('/webhook/telegram', async (req, res) => {
  if (req.body.inline_query) {
    const query = req.body.inline_query;

    // Search ChatGPT apps
    const results = await searchApps(query.query);

    await bot.apiRequest('answerInlineQuery', {
      inline_query_id: query.id,
      results: results.map((app, index) => ({
        type: 'article',
        id: String(index),
        title: app.name,
        description: app.description,
        input_message_content: {
          message_text: `🤖 *${app.name}*\n\n${app.description}\n\nLaunch App`
        }
      }))
    });
  }
});

Chat Administration allows bots to manage groups:

async function restrictSpammer(chatId, userId) {
  await bot.apiRequest('restrictChatMember', {
    chat_id: chatId,
    user_id: userId,
    permissions: {
      can_send_messages: false,
      can_send_media_messages: false,
      can_send_polls: false,
      can_send_other_messages: false
    },
    until_date: Math.floor(Date.now() / 1000) + 3600 // 1 hour
  });
}

Production Deployment Checklist

  • HTTPS Webhook: Telegram requires SSL/TLS. Use Let's Encrypt or Cloudflare.
  • Rate Limiting: Implement token bucket or sliding window to respect Telegram's 30 messages/second limit.
  • Error Monitoring: Integrate Sentry or similar for production error tracking.
  • Database Indexing: Index user state lookups by userId for <10ms query times.
  • Webhook Retries: Telegram retries failed webhook deliveries for 24 hours—ensure idempotent handlers.
  • Secret Rotation: Rotate webhook secret tokens every 90 days.
  • GDPR Compliance: Implement /deletedata command to purge user data on request.

Performance Benchmarks

Well-architected Telegram bots achieve:

  • <100ms webhook processing time (p95)
  • <200ms end-to-end message response time (including ChatGPT API call)
  • 99.9% uptime with proper health check monitoring
  • 10,000+ concurrent users per server instance (Node.js cluster mode)

Related Resources

  • Build ChatGPT Apps Without Code: Complete No-Code Guide - Comprehensive ChatGPT app development guide
  • Instant App Wizard: 48-Hour ChatGPT App Deployment - Launch your Telegram bot in 2 days
  • ChatGPT App Templates - Pre-built Telegram bot templates
  • WhatsApp Business API Integration for ChatGPT Apps - Multi-platform messaging strategy
  • Slack Bot Development with OpenAI Apps SDK - Enterprise messaging integration
  • Discord Bot Integration for ChatGPT Apps - Gaming and community bots
  • Messaging Platform API Comparison - Choose the right platform
  • Real-Time Webhook Architecture for Conversational AI - Production webhook patterns
  • Payment Integration Best Practices for ChatGPT Apps - Monetization strategies
  • Get Started with MakeAIHQ - Build your Telegram bot today

External References


Ready to build production-grade Telegram bots for your ChatGPT apps? Start your free trial with MakeAIHQ and deploy your first bot in 48 hours with our Instant App Wizard.