Spaces:
Configuration error
Configuration error
Download telegram_bot.py from D3AMON/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 12.8 kB
-
https://huggingface.co/spaces/D3AMON/hermes-agent/resolve/main/telegram_bot.py
- Command line
-
hf download hf://spaces/D3AMON/hermes-agent/telegram_bot.py
-
curl -L -o telegram_bot.py https://huggingface.co/spaces/D3AMON/hermes-agent/resolve/main/telegram_bot.py
12.8 kB
| """ | |
| Telegram Bot Integration for Hermes Agent | |
| Supports both webhook and polling modes | |
| """ | |
| import os | |
| import asyncio | |
| import logging | |
| from typing import Optional | |
| from telegram import Update, BotCommand | |
| from telegram.ext import ( | |
| Application, | |
| CommandHandler, | |
| MessageHandler, | |
| filters, | |
| ContextTypes, | |
| ) | |
| import config | |
| from hermes_agent import process_query, get_agent | |
| logger = logging.getLogger("telegram-bot") | |
| # Bot commands | |
| COMMANDS = [ | |
| BotCommand("start", "Start the bot and see welcome message"), | |
| BotCommand("help", "Show available commands and features"), | |
| BotCommand("weather", "Get weather for a city - /weather London"), | |
| BotCommand("crypto", "Get crypto price - /crypto bitcoin"), | |
| BotCommand("joke", "Get a random joke"), | |
| BotCommand("fact", "Get a random interesting fact"), | |
| BotCommand("quote", "Get an inspirational quote"), | |
| BotCommand("pokemon", "Get Pokemon info - /okemon pikachu"), | |
| BotCommand("anime", "Search anime - /anime attack on titan"), | |
| BotCommand("clear", "Clear conversation history"), | |
| BotCommand("about", "About Hermes Agent"), | |
| ] | |
| # Welcome message | |
| WELCOME_MESSAGE = """ | |
| ๐ *Welcome to Hermes Agent!* ๐ | |
| I'm your AI assistant powered by *Nous Research Hermes* with access to **hundreds of real-world APIs** and tools! | |
| ๐ *What I can do:* | |
| โข ๐ค๏ธ Weather forecasts for any city worldwide | |
| โข ๐ฐ Crypto prices & market data | |
| โข ๐ Book searches & quotes | |
| โข ๐ฎ Pokemon, anime, games & D&D info | |
| โข ๐ Recipes & cocktail instructions | |
| โข ๐ Jokes, memes & fun facts | |
| โข ๐ Space data (ISS location, SpaceX, NASA) | |
| โข โฝ Sports scores & F1 standings | |
| โข ๐ต Song lyrics & music search | |
| โข ๐ฑ Currency conversion | |
| โข ๐ Dictionary & word definitions | |
| โข ๐จ Art from museums worldwide | |
| โข ๐ฌ TV shows & movie info | |
| โข ๐บ๏ธ Location & geocoding | |
| โข ๐ฌ Science facts & periodic table | |
| โข โ Much more! | |
| Just ask me anything! Try: | |
| โข "What's the weather in Tokyo?" | |
| โข "Get me a random joke" | |
| โข "Search anime One Piece" | |
| โข "What's the price of Bitcoin?" | |
| โข "Define serendipity" | |
| Type /help for more details. | |
| """ | |
| HELP_MESSAGE = """ | |
| ๐ *Hermes Agent Commands* | |
| */start* - Start the bot | |
| */help* - Show this help message | |
| */weather [city]* - Get weather forecast | |
| */crypto [coin]* - Get cryptocurrency price | |
| */joke* - Random joke | |
| */fact* - Random interesting fact | |
| */quote* - Inspirational quote | |
| */pokemon [name/id]* - Pokemon info | |
| */anime [title]* - Search anime | |
| */clear* - Clear your chat history | |
| */about* - About this bot | |
| *You can also ask me anything naturally!* | |
| Examples: | |
| โข "What's the weather in Paris?" | |
| โข "Tell me a Chuck Norris joke" | |
| โข "Get recipe for lasagna" | |
| โข "Who's in space right now?" | |
| โข "Latest news from Hacker News" | |
| โข "Convert 100 USD to EUR" | |
| โข "Search for Breaking Bad quotes" | |
| โข "Get F1 standings" | |
| โข "Draw 5 random cards" | |
| โข "What's Pokemon #25?" | |
| """ | |
| ABOUT_MESSAGE = """ | |
| ๐ค *About Hermes Agent* | |
| Built with: | |
| โข ๐ง *Nous Research Hermes* - Function calling LLM | |
| โข ๐ Python + FastAPI backend | |
| โข ๐ OpenRouter API for AI inference | |
| โข ๐ 100+ integrated APIs | |
| โข โ๏ธ Deployed on Hugging Face Spaces | |
| *Features:* | |
| โข Multi-turn conversations with memory | |
| โข 100+ real-world API integrations | |
| โข Automatic tool selection | |
| โข Weather, crypto, news, entertainment | |
| โข Games, anime, sports, science | |
| โข And much more! | |
| The agent uses the *Hermes Function Calling* standard to intelligently select and execute tools based on your questions. | |
| *Open Source:* Based on NousResearch/Hermes-Function-Calling | |
| """ | |
| async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /start command""" | |
| await update.message.reply_text( | |
| WELCOME_MESSAGE, | |
| parse_mode="Markdown", | |
| disable_web_page_preview=True | |
| ) | |
| async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /help command""" | |
| await update.message.reply_text( | |
| HELP_MESSAGE, | |
| parse_mode="Markdown" | |
| ) | |
| async def about_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /about command""" | |
| await update.message.reply_text( | |
| ABOUT_MESSAGE, | |
| parse_mode="Markdown" | |
| ) | |
| async def weather_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /weather command""" | |
| city = " ".join(context.args) if context.args else "London" | |
| processing_msg = await update.message.reply_text(f"๐ค๏ธ Getting weather for *{city}*...", parse_mode="Markdown") | |
| try: | |
| response = process_query(f"What's the weather in {city}?", str(update.effective_user.id)) | |
| await processing_msg.delete() | |
| await update.message.reply_text(response[:4000] if len(response) > 4000 else response) | |
| except Exception as e: | |
| await processing_msg.delete() | |
| await update.message.reply_text(f"โ Error getting weather: {str(e)[:200]}") | |
| async def crypto_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /crypto command""" | |
| coin = context.args[0] if context.args else "bitcoin" | |
| processing_msg = await update.message.reply_text(f"๐ฐ Fetching {coin} price...") | |
| try: | |
| response = process_query(f"What's the current price of {coin}?", str(update.effective_user.id)) | |
| await processing_msg.delete() | |
| await update.message.reply_text(response[:4000] if len(response) > 4000 else response) | |
| except Exception as e: | |
| await processing_msg.delete() | |
| await update.message.reply_text(f"โ Error: {str(e)[:200]}") | |
| async def joke_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /joke command""" | |
| try: | |
| response = process_query("Tell me a random joke", str(update.effective_user.id)) | |
| await update.message.reply_text(response) | |
| except Exception as e: | |
| await update.message.reply_text(f"โ Error: {str(e)[:200]}") | |
| async def fact_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /fact command""" | |
| try: | |
| response = process_query("Tell me an interesting random fact", str(update.effective_user.id)) | |
| await update.message.reply_text(response) | |
| except Exception as e: | |
| await update.message.reply_text(f"โ Error: {str(e)[:200]}") | |
| async def quote_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /quote command""" | |
| try: | |
| response = process_query("Give me an inspirational quote of the day", str(update.effective_user.id)) | |
| await update.message.reply_text(response) | |
| except Exception as e: | |
| await update.message.reply_text(f"โ Error: {str(e)[:200]}") | |
| async def pokemon_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /pokemon command""" | |
| name_or_id = context.args[0] if context.args else "25" | |
| processing_msg = await update.message.reply_text(f"๐ฎ Looking up Pokemon...") | |
| try: | |
| response = process_query(f"Get Pokemon info for {name_or_id}", str(update.effective_user.id)) | |
| await processing_msg.delete() | |
| await update.message.reply_text(response[:4000] if len(response) > 4000 else response) | |
| except Exception as e: | |
| await processing_msg.delete() | |
| await update.message.reply_text(f"โ Error: {str(e)[:200]}") | |
| async def anime_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /anime command""" | |
| title = " ".join(context.args) if context.args else "Attack on Titan" | |
| processing_msg = await update.message.reply_text(f"๐ Searching anime: *{title}*...", parse_mode="Markdown") | |
| try: | |
| response = process_query(f"Search anime {title}", str(update.effective_user.id)) | |
| await processing_msg.delete() | |
| await update.message.reply_text(response[:4000] if len(response) > 4000 else response) | |
| except Exception as e: | |
| await processing_msg.delete() | |
| await update.message.reply_text(f"โ Error: {str(e)[:200]}") | |
| async def clear_command(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle /clear command - clear conversation history""" | |
| user_id = str(update.effective_user.id) | |
| agent = get_agent() | |
| agent.clear_history(user_id) | |
| await update.message.reply_text("๐๏ธ *Conversation history cleared!*", parse_mode="Markdown") | |
| async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle regular text messages""" | |
| user_id = str(update.effective_user.id) | |
| user_message = update.message.text | |
| # Show typing indicator | |
| await context.bot.send_chat_action( | |
| chat_id=update.effective_chat.id, | |
| action="typing" | |
| ) | |
| try: | |
| # Process through Hermes agent | |
| response = process_query(user_message, user_id) | |
| # Send response (Telegram has 4096 char limit per message) | |
| if len(response) > 4000: | |
| # Split into chunks | |
| chunks = [response[i:i+4000] for i in range(0, len(response), 4000)] | |
| for chunk in chunks: | |
| await update.message.reply_text(chunk) | |
| else: | |
| await update.message.reply_text(response) | |
| except Exception as e: | |
| logger.error(f"Error processing message: {e}") | |
| await update.message.reply_text( | |
| f"โ Sorry, I encountered an error:\n```\n{str(e)[:300]}\n```\n\n" | |
| "Please try again or use /clear to reset the conversation.", | |
| parse_mode="Markdown" | |
| ) | |
| async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| """Handle errors""" | |
| logger.error(f"Update {update} caused error: {context.error}") | |
| if update and update.effective_message: | |
| await update.effective_message.reply_text( | |
| "โ An unexpected error occurred. Please try again later." | |
| ) | |
| class TelegramBot: | |
| """Telegram Bot wrapper for Hermes Agent""" | |
| def __init__(self, token: str = None): | |
| self.token = token or config.TELEGRAM_BOT_TOKEN | |
| self.application = None | |
| def setup(self): | |
| """Initialize the bot application""" | |
| self.application = Application.builder().token(self.token).build() | |
| # Register handlers | |
| self.application.add_handler(CommandHandler("start", start_command)) | |
| self.application.add_handler(CommandHandler("help", help_command)) | |
| self.application.add_handler(CommandHandler("about", about_command)) | |
| self.application.add_handler(CommandHandler("weather", weather_command)) | |
| self.application.add_handler(CommandHandler("crypto", crypto_command)) | |
| self.application.add_handler(CommandHandler("joke", joke_command)) | |
| self.application.add_handler(CommandHandler("fact", fact_command)) | |
| self.application.add_handler(CommandHandler("quote", quote_command)) | |
| self.application.add_handler(CommandHandler("pokemon", pokemon_command)) | |
| self.application.add_handler(CommandHandler("anime", anime_command)) | |
| self.application.add_handler(CommandHandler("clear", clear_command)) | |
| self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) | |
| self.application.add_error_handler(error_handler) | |
| logger.info("Telegram bot setup complete") | |
| def run_polling(self): | |
| """Run the bot in polling mode (for local development)""" | |
| if not self.application: | |
| self.setup() | |
| logger.info("Starting bot in polling mode...") | |
| self.application.run_polling( | |
| drop_pending_updates=True, | |
| allowed_updates=Update.ALL_TYPES | |
| ) | |
| async def setup_webhook(self, webhook_url: str): | |
| """Setup webhook for production""" | |
| if not self.application: | |
| self.setup() | |
| # Set webhook | |
| await self.application.bot.set_webhook( | |
| url=webhook_url, | |
| allowed_updates=Update.ALL_TYPES | |
| ) | |
| # Set bot commands | |
| await self.application.bot.set_my_commands(COMMANDS) | |
| logger.info(f"Webhook set to: {webhook_url}") | |
| def get_webhook_handler(self): | |
| """Get the webhook handler for FastAPI integration""" | |
| if not self.application: | |
| self.setup() | |
| return self.application | |
| def process_update(self, update_data: dict): | |
| """Process an update from webhook""" | |
| if not self.application: | |
| self.setup() | |
| update = Update.de_json(update_data, self.application.bot) | |
| self.application.process_update(update) | |
| # Global bot instance | |
| _bot = None | |
| def get_bot() -> TelegramBot: | |
| """Get or create the global bot instance""" | |
| global _bot | |
| if _bot is None: | |
| _bot = TelegramBot() | |
| return _bot | |