Guides / Telegram Bot

Telegram Bot

Build a Telegram bot with /search and /artist commands that return art metadata in formatted messages. Then add inline query support so users can search from any chat. Uses python-telegram-bot v20+ and Python 3.9+.

Prerequisites

Set up the project

mkdir lagoon-bot && cd lagoon-bot pip install python-telegram-bot requests

Set your bot token as an environment variable.

export TELEGRAM_TOKEN=your_bot_token

Build the bot

Create bot.py with the command handlers.

# bot.py import os import requests from telegram import Update from telegram.ext import Application, CommandHandler, ContextTypes LAGOON = "https://lagoon.io/api/v1" def lagoon(endpoint, params): resp = requests.get(f"{LAGOON}/{endpoint}", params=params) return resp.json() async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text( "Search art metadata from Lagoon.\n\n" "/search <query> - search in plain English\n" "/artist <handle> - look up an artist" ) async def search(update: Update, context: ContextTypes.DEFAULT_TYPE): if not context.args: await update.message.reply_text("Usage: /search <query>") return query = " ".join(context.args) data = lagoon("search", {"nl": query, "limit": 5}) if not data["ok"]: await update.message.reply_text(f"Error: {data['error']}") return if not data["results"]: await update.message.reply_text("No results found.") return lines = [] for post in data["results"]: title = post.get("title") or f"Post #{post['id']}" tags = [t for t in post["tags"] if not t.startswith("rating:")][:5] line = f"<b>{title}</b>\n" line += f"{post['artist_handle']} on {post['platform_name']}\n" line += ", ".join(tags) if post.get("source_url"): line += f'\n<a href="{post["source_url"]}">Source</a>' lines.append(line) await update.message.reply_html("\n\n".join(lines)) async def artist(update: Update, context: ContextTypes.DEFAULT_TYPE): if not context.args: await update.message.reply_text("Usage: /artist <handle>") return handle = context.args[0] data = lagoon("artist", {"handle": handle}) if not data["ok"]: await update.message.reply_text(f"Artist not found: {handle}") return a = data["artist"] lines = [f"<b>{a.get('display_name') or a['handle']}</b> ({a['post_count']} posts)\n"] for p in a["platforms"]: if p.get("profile_url"): lines.append(f'{p["platform_name"]}: <a href="{p["profile_url"]}">{p["handle"]}</a>') else: lines.append(f'{p["platform_name"]}: {p["handle"]}') await update.message.reply_html("\n".join(lines)) app = Application.builder().token(os.environ["TELEGRAM_TOKEN"]).build() app.add_handler(CommandHandler("start", start)) app.add_handler(CommandHandler("search", search)) app.add_handler(CommandHandler("artist", artist)) app.run_polling()

Register commands

Register a command menu so users see available commands when they type / in the chat. Send /setcommands to @BotFather, select your bot, and enter:

search - Search art metadata in plain English artist - Look up an artist across platforms

Run the bot

python bot.py

Try it in a Telegram chat with your bot:

Add inline queries

Inline queries work from any Telegram chat. The user types @your_bot_name followed by a query, selects a result from the dropdown, and the formatted result is sent into the conversation.

Enable inline mode through @BotFather: send /setinline, select your bot, and enter a placeholder such as Search art metadata.... The placeholder appears in the input field when a user types your bot's name.

Add these imports to the top of bot.py:

from telegram import InlineQueryResultArticle, InputTextMessageContent from telegram.ext import InlineQueryHandler

Add this handler above the app = Application... line:

async def inline_search(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.inline_query.query if len(query) < 3: return data = lagoon("search", {"nl": query, "limit": 10}) if not data.get("ok"): return results = [] for post in data["results"]: title = post.get("title") or f"Post #{post['id']}" tags = [t for t in post["tags"] if not t.startswith("rating:")][:5] text = f"<b>{title}</b>\n" text += f"By {post['artist_handle']} on {post['platform_name']}\n" text += ", ".join(tags) if post.get("source_url"): text += f"\n{post['source_url']}" results.append(InlineQueryResultArticle( id=str(post["id"]), title=title, description=f"{post['artist_handle']} on {post['platform_name']}", input_message_content=InputTextMessageContent( text, parse_mode="HTML" ), )) await update.inline_query.answer(results, cache_time=60)

Register the handler alongside the others:

app.add_handler(InlineQueryHandler(inline_search))

Restart the bot and test by typing @your_bot_name sword art in any Telegram chat. Results appear after three characters.

Authenticate with an API key

Unauthenticated requests are rate-limited to 30 per minute. To use a higher-limit tier, set your API key in the environment and update the lagoon() helper to pass it in the request header.

# export LAGOON_KEY=lg_your_key_here def lagoon(endpoint, params): headers = {} api_key = os.environ.get("LAGOON_KEY") if api_key: headers["X-API-Key"] = api_key resp = requests.get(f"{LAGOON}/{endpoint}", params=params, headers=headers) return resp.json()

Next steps