Crea una app con IA basada en agentes con la que tu cliente pueda interactuar a través de Telegram

1. Introducción

Crea experiencias de IA basadas en agentes interactivas y fluidas con las que tus clientes puedan interactuar directamente desde la aplicación de mensajería que ya usaron. Descubre cómo desarrollar e implementar aplicaciones inteligentes que se ejecuten sin problemas en interfaces web y canales de mensajería modernos.

Qué compilarás

Integración entre un "conserje de restaurante" completo, una aplicación basada en ADK potenciada por Gemini que ayuda a los comensales a explorar el menú de un restaurante y reservar mesas, y la app de chat de Telegram. Puedes interactuar con el bot de Telegram y solicitar descripciones en lenguaje natural, como "Quiero algo picante y vegetariano". Luego, el bot se conectará al agente de ADK, que leerá y escribirá en una base de datos de Cloud SQL PostgreSQL por completo a través de MCP Toolbox para bases de datos, que controla todo el acceso a la base de datos, incluida la generación automática de embeddings para la búsqueda de vectores. Mientras tanto, el usuario podrá ver que el bot reconoce el mensaje y escribe ... typing para responder mientras espera la respuesta del agente de ADK.

c1d28343ed68358a.png

Qué aprenderás

  • Implementa un "conserje de restaurante" funcional, una aplicación basada en ADK potenciada por Gemini.
  • Configura el bot de chat de Telegram con BotFather.
  • Escribe aplicaciones de Python para escuchar el webhook del bot.
  • Envía una acción de chat para proporcionar la notificación ... typing en Telegram en el mensaje del usuario y realiza un sondeo para enviar ... typing de forma periódica mientras esperas la respuesta real.
  • Llama al extremo de Cloud Run de Restaurant Concierge para procesar la consulta del usuario.
  • Controla la respuesta del agente de ADK, envía un mensaje a Telegram y cierra el búfer.
  • Implementa la aplicación de Python en Cloud Run.
  • Interactúa con tu bot de Telegram.

Requisitos previos

2. Configuración del entorno: Continuación del codelab anterior

Las narrativas que proporcionamos en este codelab son, en realidad, la continuación de este codelab de requisitos previos: RAG agente con ADK, MCP Toolbox y Cloud SQL o Agentes a gran escala: Arquitectura multiagente con protocolo A2A en Agent Runtime y la integración de ADK. Puedes continuar tu trabajo desde el codelab anterior.

Podemos comenzar a compilar en el directorio de trabajo del codelab anterior ( el directorio de trabajo debe ser build-agent-adk-toolbox-cloudsql o adk-a2a-agent-runtime-starter). Para evitar confusiones, cambiemos el nombre del directorio por el mismo nombre de directorio que usamos cuando comenzamos de cero.

Si continúas desde el lab RAG agente con ADK, MCP Toolbox y Cloud SQL :

mv ~/build-agent-adk-toolbox-cloudsql ~/build-agent-adk-telegram

De lo contrario, si continúas desde el lab Agentes a gran escala: Arquitectura multiagente con protocolo A2A en Agent Runtime y la integración de ADK

mv ~/adk-a2a-agent-runtime-starter ~/build-agent-adk-telegram

Luego, cambia nuestro directorio de trabajo a él.

cloudshell workspace ~/build-agent-adk-telegram && cd ~/build-agent-adk-telegram
source .env

Después de eso, verifica que tu restaurant-agent ya esté implementado y tenga una URL pública a la que se pueda acceder.

AGENT_URL=$(gcloud run services describe restaurant-agent \
    --region="$REGION" \
    --format='value(status.url)')

echo "      ✓ Agent service deployed"
echo "      Agent URL: $AGENT_URL"
echo ""

Si puedes acceder a la URL, puedes continuar con la siguiente sección: Create Telegram Bot.

3. Configuración del entorno: Comienza de cero con el repositorio de partida

En este paso, se prepara tu entorno de Cloud Shell, se configura tu proyecto de Google Cloud y se clona el repositorio de partida.

Abre Cloud Shell

Abre Cloud Shell en tu navegador. Cloud Shell proporciona un entorno preconfigurado con todas las herramientas que necesitas para este codelab. Haz clic en Autorizar cuando se te solicite.

Luego, haz clic en "Ver" -> "Terminal" para abrir la terminal. Tu interfaz debería verse similar a esta.

86307fac5da2f077.png

Esta será nuestra interfaz principal, IDE en la parte superior y terminal en la parte inferior.

Configura el directorio de trabajo

Clona el repositorio de partida. Todo el código que escribas en este codelab se encuentra aquí:

rm -rf ~/build-agent-adk-telegram
git clone https://github.com/alphinside/adk-a2a-agent-runtime-starter.git build-agent-adk-telegram
cloudshell workspace ~/build-agent-adk-telegram && cd ~/build-agent-adk-telegram

Crea el archivo .env a partir de la plantilla proporcionada:

cp .env.example .env

Para simplificar la configuración del proyecto en tu terminal, descarga esta secuencia de comandos de configuración del proyecto en tu directorio de trabajo:

curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh

Ejecuta la secuencia de comandos. Verifica tu cuenta de facturación de prueba, crea un proyecto nuevo (o valida uno existente), guarda el ID del proyecto en un archivo .env en el directorio actual y establece el proyecto activo en gcloud.

bash setup_verify_trial_project.sh && source .env

La secuencia de comandos hará lo siguiente:

  1. Verifica que tengas una cuenta de facturación de prueba activa.
  2. Busca un proyecto existente en .env (si existe).
  3. Crea un proyecto nuevo o vuelve a usar el existente.
  4. Vincula la cuenta de facturación de prueba a tu proyecto.
  5. Guarda el ID del proyecto en .env.
  6. Establece el proyecto como el proyecto gcloud activo.

Para verificar que el proyecto esté configurado correctamente, revisa el texto amarillo junto a tu directorio de trabajo en el mensaje de la terminal de Cloud Shell. Debería mostrar el ID de tu proyecto.

5c515e235ee1179f.png

Configuración de la infraestructura de partida

Primero, deberemos instalar las dependencias de Python con uv. Es un administrador de proyectos y paquetes de Python rápido escrito en Rust ( documentación de uv). Este codelab lo usa por su velocidad y simplicidad para mantener el proyecto de Python.

uv sync

Luego, ejecuta la secuencia de comandos de configuración completa, que crea la instancia de Cloud SQL, inicializa los datos y, luego, implementa el servicio de Toolbox que actuará como el estado inicial de nuestro agente de restaurante.

bash scripts/full_setup.sh > logs/full_setup.log 2>&1 &

Este comando realizará las siguientes acciones:

  • Crea una instancia de Cloud SQL y propaga la base de datos (fase 1).
  • Genera la configuración del entorno del agente y, luego, inicia el servicio de Toolbox local (fase 2).
  • Implementa los servicios de Toolbox y Agent en Cloud Run (fase 3).

Una vez que finalice esta implementación, podrás acceder a la IU de desarrollo de ADK en la URL de Cloud Run.

source .env
AGENT_URL=$(gcloud run services describe restaurant-agent \
    --region="$REGION" \
    --format='value(status.url)')

echo "      ✓ Agent service deployed"
echo "      Agent URL: $AGENT_URL"
echo ""

Abre la IU de desarrollo de ADK, selecciona restaurant_agent y prueba con consultas como el siguiente ejemplo:

What Italian dishes do you have?

O

I want something spicy and creamy

Ahora, la siguiente acción es cómo podemos pasar de solo la interfaz de desarrollo web al canal de mensajería de Telegram.

4. Crea un bot de Telegram

Telegram es una plataforma de mensajería gratuita muy conocida que se usa ampliamente para la participación basada en la comunidad. Una de las razones es que ofrece muchas formas de integrarse con facilidad, por lo que las personas pueden crear fácilmente su propio bot con una gran variedad de funciones diferentes.

En nuestro caso, usaremos BotFather para crear nuestro propio bot por primera vez. Ten en cuenta que, aunque usamos Telegram para esta sesión, se puede usar el mismo método para WhatsApp o cualquier otra plataforma de mensajería que elijas.

Usa BotFather para crear tu propio bot

Abre tu navegador web y visita https://telegram.me/BotFather para comenzar a crear tu propio bot de Telegram.

1b817e758c699a79.png

Comienza a interactuar con BotFather

ad3daa08e73502db.png

Envía el comando /start

Para comenzar a usar BotFather y crear tu primer bot, debes invocar el mensaje /start a BotFather. Luego, se compartirán todos los comandos que tiene para que interactúes con él.

/start

Inicia la creación del bot con el comando /newbot

Creemos nuestro nuevo bot enviando el comando /newbot a BotFather. Se te pedirá que le pongas un nombre al bot y, luego, que le asignes un username, que siempre debe terminar en bot . Por ejemplo, TetrisBot o tetris_bot. Debe ser único.

1f6a74f494d48986.png

Una vez que se cree el bot correctamente, recibirás el siguiente mensaje de BotFather.

Done! Congratulations on your new bot. You will find it at t.me/AdkTelegramTest_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.

Use this token to access the HTTP API:
<YOUR_TELEGRAM_API_KEY>
Keep your token secure and store it safely, it can be used by anyone to control your bot.

For a description of the Bot API, see this page: https://core.telegram.org/bots/api

Toma nota de YOUR_TELEGRAM_API_KEY. La usaremos en la siguiente sección.

5. Desarrolla la aplicación de webhook de Telegram

Preparemos el directorio de trabajo para comenzar a desarrollar tu aplicación de webhook de Telegram.

mkdir ~/build-agent-adk-telegram/telegram-integration
cd ~/build-agent-adk-telegram

Agrega las dependencias necesarias

Crea la secuencia de comandos requirements.txt con el siguiente contenido para proporcionar dependencias adecuadas para la secuencia de comandos del objeto de escucha de webhook de Telegram.

cloudshell edit ./telegram-integration/requirements.txt

Luego, agrega las siguientes dependencias.

python-telegram-bot[webhooks]
httpx

Crea una secuencia de comandos para el objeto de escucha de webhook de Telegram

Una vez que se instale la dependencia, podremos crear una secuencia de comandos de Python main.py para la aplicación de integración.

cloudshell edit ~/build-agent-adk-telegram/telegram-integration/main.py

Luego, copia el siguiente código en él.

# ./telegram-integration/main.py

import asyncio
import os
import sys
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
from telegram.constants import ChatAction
import httpx

# Read token from environment variable
TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN")
ADK_SERVER_URL = os.environ.get("ADK_SERVER_URL", "http://localhost:8000")
ADK_APP_NAME = os.environ.get("ADK_APP_NAME", "restaurant_agent")

# Parse base URL out of ADK_SERVER_URL
BASE_URL = ADK_SERVER_URL.rstrip('/')
if BASE_URL.endswith('/run'):
    BASE_URL = BASE_URL[:-4]
elif BASE_URL.endswith('/query'):
    BASE_URL = BASE_URL[:-6]

if not TOKEN:
    print("Error: TELEGRAM_BOT_TOKEN environment variable not set.")
    print("Please set it before running the application.")
    sys.exit(1)

async def start(update: Update, context: CallbackContext) -> None:
    """Send a message when the command /start is issued."""
    await update.message.reply_text('Hi! I am your ADK Integration Bot. Send me a message and I will forward it to the ADK server.')

async def send_typing_loop(chat_id: int, bot, stop_event: asyncio.Event):
    """Send typing action periodically until the stop event is set."""
    while not stop_event.is_set():
        try:
            await bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
            # The research suggested repeating every 4 seconds
            await asyncio.sleep(4)
        except Exception as e:
            print(f"Error sending chat action: {e}")
            await asyncio.sleep(1) # Wait a bit before retrying if error

async def handle_message(update: Update, context: CallbackContext) -> None:
    """Handle incoming user messages."""
    user_message = update.message.text
    chat_id = update.message.chat_id
    raw_user_id = str(update.message.from_user.id)
    
    # Derive unique user_id and session_id for this user
    user_id = f"tg_{raw_user_id}"
    session_id = f"tg_sess_{raw_user_id}"

    print(f"Received message from {user_id}: {user_message}")

    # Create a stop event for the typing loop
    stop_event = asyncio.Event()
    
    # Start the typing loop as a background task
    typing_task = asyncio.create_task(send_typing_loop(chat_id, context.bot, stop_event))

    try:
        async with httpx.AsyncClient() as client:
            # 1. Check if the session exists
            session_url = f"{BASE_URL}/apps/{ADK_APP_NAME}/users/{user_id}/sessions/{session_id}"
            session_check = await client.get(session_url, timeout=10.0)
            
            if session_check.status_code == 404:
                # 2. If session doesn't exist, create it
                print(f"Session {session_id} not found. Creating session...")
                session_create = await client.post(session_url, json={}, timeout=10.0)
                if session_create.status_code != 200:
                    raise Exception(f"Failed to create session: {session_create.status_code} {session_create.text}")
            elif session_check.status_code != 200:
                raise Exception(f"Error checking session: {session_check.status_code} {session_check.text}")
            
            # 3. Run the ADK agent
            run_url = f"{BASE_URL}/run"
            payload = {
                "appName": ADK_APP_NAME,
                "userId": user_id,
                "sessionId": session_id,
                "newMessage": {
                    "role": "user",
                    "parts": [{"text": user_message}]
                }
            }
            response = await client.post(run_url, json=payload, timeout=60.0)
            
        if response.status_code == 200:
            events = response.json()
            if isinstance(events, list) and len(events) > 0:
                # The last event contains the final text response
                last_event = events[-1]
                content = last_event.get("content", {})
                parts = content.get("parts", [])
                if parts and "text" in parts[0]:
                    reply_text = parts[0]["text"]
                else:
                    reply_text = "ADK agent returned an empty or non-text response."
            else:
                reply_text = "No events returned from ADK agent."
        else:
            reply_text = f"Error communicating with ADK server (Status: {response.status_code})."
            
    except Exception as e:
        reply_text = f"Failed to connect to ADK server: {e}"
    finally:
        # Stop the typing loop
        stop_event.set()
        await typing_task

    # Send the final response back to the user
    await update.message.reply_text(reply_text)

def main() -> None:
    """Start the bot."""
    # Create the Application and pass it your bot's token.
    application = Application.builder().token(TOKEN).build()

    # on different commands - answer in Telegram
    application.add_handler(CommandHandler("start", start))

    # on non command i.e message - echo the message on Telegram
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))

    # Check if running in webhook mode (e.g., on Cloud Run)
    port = os.environ.get("PORT")
    service_url = os.environ.get("SERVICE_URL")

    if port and service_url:
        if not service_url.startswith("http"):
            service_url = f"https://{service_url}"
        
        print(f"Starting bot in WEBHOOK mode on port {port} with url {service_url}")
        
        application.run_webhook(
            listen="0.0.0.0",
            port=int(port),
            url_path=TOKEN,
            webhook_url=f"{service_url}/{TOKEN}",
            allowed_updates=Update.ALL_TYPES
        )
    else:
        print("Starting bot in POLLING mode")
        # Run the bot until the user presses Ctrl-C
        application.run_polling(allowed_updates=Update.ALL_TYPES)


if __name__ == "__main__":
    main()

Comprende el código de integración del bot de Telegram

23b346f5ceb4712a.png

Cuando un usuario envía un mensaje, se ejecuta la siguiente canalización en handle_message().

Paso 1: Derivación de identidad y sesión

El bot asigna el ID de usuario de Telegram a identificadores de ADK únicos para mantener las sesiones de usuario distintas:

user_id = f"tg_{raw_user_id}"
session_id = f"tg_sess_{raw_user_id}"

Paso 2: Estado "Escribiendo" asíncrono (líneas 53 a 58)

Para garantizar una experiencia del usuario altamente responsiva mientras el agente de ADK procesa el mensaje (lo que puede tardar varios segundos), el bot inicia un bucle asíncrono en segundo plano:

  • Se crea una instancia de asyncio.Event como stop_event.
  • asyncio.create_task genera send_typing_loop(...) en segundo plano.
  • El bucle envía una acción ChatAction.TYPING a Telegram cada 4 segundos hasta que se establece stop_event.

Paso 3: Verificación y creación de la sesión de ADK (líneas 61 a 72)

Antes de ejecutar el agente, el bot verifica si ya existe una sesión:

  1. Envía una solicitud GET a /apps/{appName}/users/{userId}/sessions/{sessionId}.
  2. Si la respuesta es 404 Not Found, crea la sesión a través de una solicitud POST a la misma URL con un cuerpo JSON vacío.
  3. Si se muestra un estado que no sea 200 o 404, se genera una excepción.

Paso 4: Envío de la solicitud al agente (líneas 74 a 85)

La carga útil del mensaje se reenvía al extremo /run de ADK:

  • Extremo: POST /run
  • El tiempo de espera de la solicitud se establece en 60.0 segundos para permitir un razonamiento complejo o una latencia ascendente.
  • Estructura de carga útil:
{
  "appName": "restaurant_agent",
  "userId": "tg_<user_id>",
  "sessionId": "tg_sess_<user_id>",
  "newMessage": {
    "role": "user",
    "parts": [{"text": "<user_message>"}]
  }
}

Paso 5: Análisis de la respuesta (líneas 87 a 101)

El servidor de ADK muestra una lista de eventos de mensajes. El bot inspecciona el array que se muestra:

  • Recupera el evento final de la lista (events[-1]).
  • Navega al contenido de texto a través de event["content"]["parts"][0]["text"].
  • Si no se muestran eventos o falta la estructura de texto, se establece un texto de marcador de posición descriptivo.

Paso 6: Desglose y envío de la respuesta (líneas 103 a 111)

  • En el bloque finally, se establece stop_event, lo que detiene el bucle de acción de escritura.
  • El bot espera a que se complete typing_task para garantizar recursos limpios.
  • Por último, el bot responde al chat de Telegram con el texto de respuesta analizado.

6. Implementa la aplicación de webhook de Telegram en Cloud Run

A continuación, implementaremos el objeto de escucha de webhook de Telegram en Cloud Run para que nuestro bot pueda comunicarse con él.

Crea el Dockerfile

Primero, debemos crear el Dockerfile.

cloudshell edit ~/build-agent-adk-telegram/telegram-integration/Dockerfile

Luego, copia el siguiente código en él.

# Use an official Python runtime as a parent image
FROM python:3.11-slim

# Prevent Python from writing pyc files to disc and buffering stdout/stderr
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set the working directory in the container
WORKDIR /app

# Install system dependencies if needed
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Copy the dependencies file to the working directory
COPY requirements.txt .

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code
COPY main.py .

# Expose the port that Cloud Run will provide via environment variable
EXPOSE 8080

# Run main.py when the container launches
CMD ["python", "main.py"]

El servicio se contiene en contenedores con python:3.11-slim para mantener pequeña la huella de la imagen:

  • Instala dependencias de requirements.txt (python-telegram-bot[webhooks] y httpx).
  • Expone el puerto estándar 8080.
  • Inicia python main.py.

Prepara las variables de entorno

Después de eso, volvamos a verificar si nuestro agente se implementó correctamente.

AGENT_URL=$(gcloud run services describe restaurant-agent \
    --region="$REGION" \
    --format='value(status.url)')

echo "      ✓ Agent service deployed"
echo "      Agent URL: $AGENT_URL"
echo ""

A continuación, coloquemos el TELEGRAM_BOT_TOKEN que obtuvimos anteriormente en el .env.

echo "TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_API_KEY" >> .env

Luego, propaguemos los datos .env con otros valores que necesitamos.

echo "ADK_SERVER_URL=$AGENT_URL" >> .env
echo "ADK_APP_NAME=restaurant_agent" >> .env
echo "SERVICE_NAME=telegram-integration" >> .env
source .env

Crea una secuencia de comandos de implementación

Creemos una secuencia de comandos de implementación que proporcione verificaciones completas y, luego, implemente la app en Cloud Run.

cloudshell edit ~/build-agent-adk-telegram/telegram-integration/deploy.sh

Copia el siguiente código en el archivo.

#!/usr/bin/env bash
# ./telegram-integration/deploy.sh

# Exit immediately if a command exits with a non-zero status
set -euo pipefail

# Color codes for neat terminal output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0;37m' # No Color
# Load environment variables from .env if it exists
if [ -f .env ]; then
    echo -e "${GREEN}✔ Loading environment variables from .env...${NC}"
    export $(grep -v '^#' .env | xargs)
fi

echo -e "${BLUE}====================================================${NC}"
echo -e "${BLUE}   Google Cloud Run Deployment: Telegram Bot        ${NC}"
echo -e "${BLUE}====================================================${NC}"

# 1. Check for gcloud CLI
if ! command -v gcloud &> /dev/null; then
    echo -e "${RED}Error: 'gcloud' CLI is not installed.${NC}"
    echo "Please install the Google Cloud SDK and try again."
    echo "See: https://cloud.google.com/sdk/docs/install"
    exit 1
fi

# 2. Check active gcloud account/auth
ACTIVE_ACCOUNT=$(gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null || true)
if [ -z "$ACTIVE_ACCOUNT" ]; then
    echo -e "${RED}Error: No active Google Cloud account found.${NC}"
    echo "Please run: gcloud auth login"
    exit 1
fi

# 3. Detect / Prompt for GCP Project
DEFAULT_PROJECT=${GCP_PROJECT_ID:-$(gcloud config get-value project 2>/dev/null || true)}
if [ -n "${DEFAULT_PROJECT}" ]; then
    echo -e "${GREEN}✔ Using GCP Project: $DEFAULT_PROJECT${NC}"
    GCP_PROJECT="$DEFAULT_PROJECT"
else
    echo -n "Enter GCP Project ID: "
    read -r GCP_PROJECT
fi

if [ -z "$GCP_PROJECT" ]; then
    echo -e "${RED}Error: GCP Project ID is required.${NC}"
    exit 1
fi

# Set active project
gcloud config set project "$GCP_PROJECT" &> /dev/null

# 4. Configure Service Parameters
DEFAULT_SERVICE=${SERVICE_NAME:-"telegram-integration"}
if [ -n "${SERVICE_NAME:-}" ]; then
    echo -e "${GREEN}✔ Using Cloud Run Service Name: $SERVICE_NAME${NC}"
else
    echo -n "Enter Cloud Run Service Name [Default: $DEFAULT_SERVICE]: "
    read -r SERVICE_NAME
    SERVICE_NAME=${SERVICE_NAME:-$DEFAULT_SERVICE}
fi

DEFAULT_REGION=${REGION:-"us-central1"}
if [ -n "${REGION:-}" ]; then
    echo -e "${GREEN}✔ Using Cloud Run Region: $REGION${NC}"
else
    echo -n "Enter Cloud Run Region [Default: $DEFAULT_REGION]: "
    read -r REGION
    REGION=${REGION:-$DEFAULT_REGION}
fi

DEFAULT_ADK_APP=${ADK_APP_NAME:-"restaurant_agent"}
if [ -n "${ADK_APP_NAME:-}" ]; then
    echo -e "${GREEN}✔ Using ADK App Name: $ADK_APP_NAME${NC}"
    ADK_APP="$ADK_APP_NAME"
else
    echo -n "Enter ADK App Name [Default: $DEFAULT_ADK_APP]: "
    read -r ADK_APP
    ADK_APP=${ADK_APP:-$DEFAULT_ADK_APP}
fi

# 5. Retrieve/Prompt for Telegram Bot Token
if [ -n "${TELEGRAM_BOT_TOKEN:-}" ]; then
    echo -e "${GREEN}✔ Found TELEGRAM_BOT_TOKEN in environment.${NC}"
    BOT_TOKEN="$TELEGRAM_BOT_TOKEN"
else
    echo -e "${YELLOW}TELEGRAM_BOT_TOKEN is not set in your environment.${NC}"
    echo -n "Enter your Telegram Bot Token (input will be hidden): "
    read -s -r BOT_TOKEN
    echo ""
fi

if [ -z "$BOT_TOKEN" ]; then
    echo -e "${RED}Error: Telegram Bot Token is required.${NC}"
    exit 1
fi

# 6. Retrieve/Prompt for ADK Server URL
DEFAULT_ADK_URL="http://localhost:8000"
if [ -n "${ADK_SERVER_URL:-}" ]; then
    echo -e "${GREEN}✔ Found ADK_SERVER_URL in environment: $ADK_SERVER_URL${NC}"
    ADK_URL="$ADK_SERVER_URL"
else
    echo -n "Enter your ADK Server URL [Default: $DEFAULT_ADK_URL]: "
    read -r ADK_URL
    ADK_URL=${ADK_URL:-$DEFAULT_ADK_URL}
fi

# Enable required GCP services
echo -e "\n${YELLOW}Checking and enabling required GCP services...${NC}"
gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com --project "$GCP_PROJECT"

# Determine source directory dynamically
SOURCE_DIR="."
if [ -d "telegram-integration" ]; then
    SOURCE_DIR="telegram-integration"
    echo -e "${GREEN}✔ Found source directory: telegram-integration${NC}"
elif [ -f "Dockerfile" ]; then
    SOURCE_DIR="."
    echo -e "${GREEN}✔ Dockerfile found in current directory. Using current directory as source.${NC}"
else
    echo -e "${RED}Error: Could not find source directory 'telegram-integration' or Dockerfile in current directory.${NC}"
    exit 1
fi

# 7. First-pass Deployment with placeholder SERVICE_URL
# This boots the container in Webhook mode (so health check binds to port)
# but uses a high-reliability placeholder URL (google.com) to pass DNS verification checks.
echo -e "\n${YELLOW}Deploying to Cloud Run (Step 1/2: Initial Deploy)...${NC}"
gcloud run deploy "$SERVICE_NAME" \
  --source "$SOURCE_DIR" \
  --region "$REGION" \
  --allow-unauthenticated \
  --set-env-vars "TELEGRAM_BOT_TOKEN=$BOT_TOKEN,ADK_SERVER_URL=$ADK_URL,ADK_APP_NAME=$ADK_APP,SERVICE_URL=https://google.com" \
  --project "$GCP_PROJECT"

# 8. Retrieve the actual service URL
echo -e "\n${YELLOW}Retrieving service URL...${NC}"
SERVICE_URL=$(gcloud run services describe "$SERVICE_NAME" --region "$REGION" --project "$GCP_PROJECT" --format 'value(status.url)')
echo -e "${GREEN}✔ Service URL is: $SERVICE_URL${NC}"

# 9. Update service environment variables with the real SERVICE_URL
# This triggers a rolling update and registers the correct webhook with Telegram automatically!
echo -e "\n${YELLOW}Updating configuration with final Webhook URL (Step 2/2)...${NC}"
gcloud run services update "$SERVICE_NAME" \
  --region "$REGION" \
  --set-env-vars "TELEGRAM_BOT_TOKEN=$BOT_TOKEN,ADK_SERVER_URL=$ADK_URL,ADK_APP_NAME=$ADK_APP,SERVICE_URL=$SERVICE_URL" \
  --project "$GCP_PROJECT"

echo -e "\n${GREEN}====================================================${NC}"
echo -e "${GREEN}   Deployment Completed Successfully! 🎉            ${NC}"
echo -e "${GREEN}====================================================${NC}"
echo -e "Service Name:   ${BLUE}$SERVICE_NAME${NC}"
echo -e "Region:         ${BLUE}$REGION${NC}"
echo -e "Active URL:     ${BLUE}$SERVICE_URL${NC}"
echo -e "Webhook Path:   ${BLUE}$SERVICE_URL/<bot-token>${NC}"
echo -e "ADK Backend:    ${BLUE}$ADK_URL${NC}"
echo -e "ADK App Name:   ${BLUE}$ADK_APP${NC}"
echo -e "${GREEN}====================================================${NC}"
echo "Your Telegram Bot has been configured to use webhooks."
echo "Any message sent to your bot will now trigger this Cloud Run instance."

Secuencia de comandos de doble implementación (deploy.sh)

Cuando se implementa en Google Cloud Run, el bot debe especificar su propia URL (SERVICE_URL) en su entorno para poder registrarla como el destino del webhook con Telegram. Para resolver esta dependencia circular (la URL es desconocida hasta la implementación, pero el servicio requiere la URL para iniciarse sin fallas en la verificación de estado), deploy.sh realiza una implementación de dos etapas:

  1. Paso 1: Implementación inicial: Inicia el contenedor con un DNS de marcador de posición (https://google.com) para que el servicio se inicie correctamente, se vincule al puerto local y pase las verificaciones de estado iniciales de Cloud Run.
  2. Paso 2: Recupera la URL: Extrae de forma programática el extremo de Cloud Run recién creado con gcloud run services describe.
  3. Paso 3: Actualiza la configuración: Actualiza las variables de entorno con la URL real del servicio en vivo. Esto activa una actualización progresiva limpia en Cloud Run y registra de forma segura el destino correcto del webhook con la API de Telegram.

Implementa en Cloud Run

La secuencia de comandos de implementación imprime la URL del agente. Ábrela en tu navegador para acceder a la misma IU de desarrollo de ADK que se ejecuta en Cloud Run.

cd ~/build-agent-adk-telegram
bash ./telegram-integration/deploy.sh

Si todo sale bien, este es el momento en el que puedes comenzar a chatear con tu bot directamente desde la aplicación de chat de Telegram. Busca el bot que acabas de crear y comienza a interactuar con él:

What Italian dishes do you have?

O

I want something spicy and creamy

Mira el bot que envía el estado "...is typing" y, luego, pronto, mostrará el mensaje del ADK que creaste antes.

c62fd4016ddd3c9b.png

7. ¡Felicitaciones!

Compilaste, implementaste y completaste la integración de nuestro agente de IA inteligente basado en ADK del asistente de menú de restaurante con Telegram, a través de la comunicación del servidor cliente HTTP, y permitiste que las personas consulten su menú favorito y reserven el restaurante.

Qué aprendiste

  • Implementa y configura el Conserje de restaurante, el agente basado en ADK y MCP Toolbox en Cloud Run.
  • Cómo configurar el bot de Telegram con BotFather
  • Cómo escribir secuencias de comandos de Python para escuchar el webhook de Telegram e interactuar con el agente de ADK para transmitir la consulta y la respuesta de los usuarios en consecuencia
  • Cómo implementar "... typing" en Telegram para indicar que los mensajes se están procesando como comentarios en tiempo real para los usuarios mientras esperan que responda el agente de ADK
  • Cómo implementar la secuencia de comandos de Python en Cloud Run y poder interactuar con ella

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud, borra los recursos que creaste en este codelab.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Opción 2: Borra los recursos individuales

# If you follow from previous A2A Agent Runtime codelab
# Delete the Agent Runtime deployment (skip if not found)
uv run python -c "
import vertexai
from google.genai import types
vertexai.init(project='$GOOGLE_CLOUD_PROJECT', location='$REGION')
client = vertexai.Client(
    project='$GOOGLE_CLOUD_PROJECT', location='$REGION',
    http_options=types.HttpOptions(api_version='v1beta1'),
)
try:
    agent = client.agent_engines.get(name='$RESERVATION_AGENT_RESOURCE_NAME')
    agent.delete(force=True)
    print('Agent Runtime deployment deleted.')
except Exception as e:
    print(f'No agent deployment found or already deleted, skipping. ({e})')
"

# Delete GCS staging bucket (skip if STAGING_BUCKET is not set)
if [ -n "$STAGING_BUCKET" ]; then
  gsutil rm -r gs://$STAGING_BUCKET
else
  echo "STAGING_BUCKET not set, skipping bucket deletion."
fi

# Delete Cloud Run services
gcloud run services delete restaurant-agent --region=$REGION --quiet
gcloud run services delete toolbox-service --region=$REGION --quiet
gcloud run services delete telegram-integration --region=$REGION --quiet

# Delete Cloud SQL instance
gcloud sql instances delete $DB_INSTANCE --quiet