Telegram Bot for GPU Management
What We're Building
Prerequisites
Step 1: Bot Setup
# clore_telegram_bot.py
"""Telegram bot for Clore.ai GPU management."""
import os
import logging
from datetime import datetime
from typing import Dict, List
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
Application, CommandHandler, CallbackQueryHandler,
ContextTypes, ConversationHandler, MessageHandler, filters
)
import requests
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# States for conversation
SELECTING_GPU, CONFIRMING_RENTAL = range(2)
class CloreBot:
"""Telegram bot for Clore.ai management."""
BASE_URL = "https://api.clore.ai"
def __init__(self, clore_api_key: str, telegram_token: str):
self.clore_api_key = clore_api_key
self.telegram_token = telegram_token
self.headers = {"auth": clore_api_key}
# User state
self.user_selections: Dict[int, dict] = {}
def _clore_request(self, method: str, endpoint: str, **kwargs) -> dict:
"""Make Clore API request."""
url = f"{self.BASE_URL}{endpoint}"
response = requests.request(method, url, headers=self.headers, **kwargs)
return response.json()
async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /start command."""
welcome = """
🚀 *Clore.ai GPU Manager Bot*
I can help you:
• 🔍 Browse available GPUs
• 💰 Check prices
• 🖥️ Rent servers
• 📊 Monitor your orders
• ⚡ Get alerts
*Commands:*
/browse - Browse available GPUs
/orders - View your active orders
/balance - Check wallet balance
/cancel <order_id> - Cancel an order
/help - Show this help
"""
await update.message.reply_text(welcome, parse_mode="Markdown")
async def browse(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Browse available GPUs."""
await update.message.reply_text("🔍 Fetching available GPUs...")
try:
data = self._clore_request("GET", "/v1/marketplace")
servers = data.get("servers", [])
# Filter available servers
available = [s for s in servers if not s.get("rented")]
# Group by GPU type
by_gpu: Dict[str, List] = {}
for server in available:
gpus = server.get("gpu_array", [])
for gpu in gpus:
# Normalize GPU name
gpu_name = gpu.split()[0] + " " + gpu.split()[-1] if " " in gpu else gpu
if gpu_name not in by_gpu:
by_gpu[gpu_name] = []
by_gpu[gpu_name].append(server)
# Build message
message = "🖥️ *Available GPUs:*\n\n"
for gpu_name, servers in sorted(by_gpu.items()):
prices = [s.get("price", {}).get("usd", {}).get("on_demand_clore", 999) for s in servers]
min_price = min(prices)
count = len(servers)
message += f"• *{gpu_name}*: {count} available from ${min_price:.2f}/hr\n"
message += "\nUse /rent <gpu_type> to see specific options."
await update.message.reply_text(message, parse_mode="Markdown")
except Exception as e:
await update.message.reply_text(f"❌ Error: {e}")
async def rent_gpu(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show rental options for specific GPU."""
if not context.args:
await update.message.reply_text("Usage: /rent <gpu_type>\nExample: /rent RTX 4090")
return
gpu_type = " ".join(context.args)
await update.message.reply_text(f"🔍 Searching for {gpu_type}...")
try:
data = self._clore_request("GET", "/v1/marketplace")
servers = data.get("servers", [])
# Filter matching GPUs
matching = []
for server in servers:
if server.get("rented"):
continue
gpus = server.get("gpu_array", [])
if any(gpu_type.lower() in g.lower() for g in gpus):
price = server.get("price", {}).get("usd", {}).get("on_demand_clore", 999)
matching.append({
"id": server["id"],
"gpus": gpus,
"price": price,
"reliability": server.get("reliability", 0)
})
if not matching:
await update.message.reply_text(f"No {gpu_type} available right now. Try /browse to see options.")
return
# Sort by price
matching.sort(key=lambda x: x["price"])
# Create inline keyboard with top 5 options
keyboard = []
for server in matching[:5]:
text = f"${server['price']:.2f}/hr - {server['reliability']:.0f}% reliable"
callback = f"rent_{server['id']}"
keyboard.append([InlineKeyboardButton(text, callback_data=callback)])
keyboard.append([InlineKeyboardButton("❌ Cancel", callback_data="cancel")])
message = f"🖥️ *Available {gpu_type}:*\n\n"
message += "Select a server to rent:\n"
await update.message.reply_text(
message,
parse_mode="Markdown",
reply_markup=InlineKeyboardMarkup(keyboard)
)
except Exception as e:
await update.message.reply_text(f"❌ Error: {e}")
async def handle_rental_selection(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle rental selection from inline keyboard."""
query = update.callback_query
await query.answer()
if query.data == "cancel":
await query.edit_message_text("Cancelled.")
return
if query.data.startswith("rent_"):
server_id = int(query.data.replace("rent_", ""))
# Store selection
self.user_selections[query.from_user.id] = {"server_id": server_id}
# Show confirmation
keyboard = [
[
InlineKeyboardButton("✅ Confirm", callback_data=f"confirm_{server_id}"),
InlineKeyboardButton("❌ Cancel", callback_data="cancel")
]
]
await query.edit_message_text(
f"🖥️ Rent server {server_id}?\n\nThis will start an on-demand rental.",
reply_markup=InlineKeyboardMarkup(keyboard)
)
elif query.data.startswith("confirm_"):
server_id = int(query.data.replace("confirm_", ""))
await query.edit_message_text("⏳ Creating order...")
try:
order = self._clore_request("POST", "/v1/create_order", json={
"renting_server": server_id,
"type": "on-demand",
"currency": "CLORE-Blockchain",
"image": "nvidia/cuda:12.8.0-base-ubuntu22.04",
"ports": {"22": "tcp"},
"env": {"NVIDIA_VISIBLE_DEVICES": "all"},
"ssh_password": "TelegramRent123!"
})
order_id = order.get("order_id")
message = f"""
✅ *Order Created!*
Order ID: `{order_id}`
Server: {server_id}
Your server will be ready in 1-2 minutes.
Use /orders to check status.
Use /cancel {order_id} to stop rental.
"""
await query.edit_message_text(message, parse_mode="Markdown")
except Exception as e:
await query.edit_message_text(f"❌ Failed: {e}")
async def orders(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show active orders."""
try:
data = self._clore_request("GET", "/v1/my_orders")
orders = data.get("orders", [])
if not orders:
await update.message.reply_text("No active orders.")
return
message = "📊 *Your Orders:*\n\n"
for order in orders:
status_emoji = {
"running": "🟢",
"creating_order": "🟡",
"paused": "🟠",
"expired": "🔴"
}.get(order.get("status"), "⚪")
message += f"{status_emoji} Order `{order['order_id']}`\n"
message += f" Status: {order.get('status')}\n"
message += f" Server: {order.get('renting_server')}\n"
if order.get("connection", {}).get("ssh"):
message += f" SSH: `{order['connection']['ssh']}`\n"
message += "\n"
await update.message.reply_text(message, parse_mode="Markdown")
except Exception as e:
await update.message.reply_text(f"❌ Error: {e}")
async def balance(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show wallet balance."""
try:
data = self._clore_request("GET", "/v1/wallets")
wallets = data.get("wallets", [])
message = "💰 *Wallet Balances:*\n\n"
for wallet in wallets:
name = wallet.get("name", "Unknown")
balance = wallet.get("balance", 0)
if "CLORE" in name:
message += f"🔹 CLORE: {balance:.2f}\n"
elif "bitcoin" in name.lower():
message += f"🟠 BTC: {balance:.6f}\n"
elif "USD" in name:
message += f"💵 USD: ${balance:.2f}\n"
await update.message.reply_text(message, parse_mode="Markdown")
except Exception as e:
await update.message.reply_text(f"❌ Error: {e}")
async def cancel_order(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Cancel an order."""
if not context.args:
await update.message.reply_text("Usage: /cancel <order_id>")
return
order_id = int(context.args[0])
try:
self._clore_request("POST", "/v1/cancel_order", json={"id": order_id})
await update.message.reply_text(f"✅ Order {order_id} cancelled.")
except Exception as e:
await update.message.reply_text(f"❌ Failed to cancel: {e}")
async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show help."""
await self.start(update, context)
def run(self):
"""Run the bot."""
app = Application.builder().token(self.telegram_token).build()
# Command handlers
app.add_handler(CommandHandler("start", self.start))
app.add_handler(CommandHandler("browse", self.browse))
app.add_handler(CommandHandler("rent", self.rent_gpu))
app.add_handler(CommandHandler("orders", self.orders))
app.add_handler(CommandHandler("balance", self.balance))
app.add_handler(CommandHandler("cancel", self.cancel_order))
app.add_handler(CommandHandler("help", self.help_command))
# Callback handler for inline buttons
app.add_handler(CallbackQueryHandler(self.handle_rental_selection))
# Start bot
logger.info("Starting Telegram bot...")
app.run_polling()
if __name__ == "__main__":
import sys
clore_key = os.environ.get("CLORE_API_KEY") or sys.argv[1]
telegram_token = os.environ.get("TELEGRAM_TOKEN") or sys.argv[2]
bot = CloreBot(clore_key, telegram_token)
bot.run()Step 2: Alert System
Quick Start
Bot Commands
Command
Description
Features
Next Steps
Last updated
Was this helpful?