Crea un'app di AI agentica con cui il tuo cliente può interagire tramite Telegram

1. Introduzione

Crea esperienze di AI agentica interattive e senza interruzioni con cui i tuoi clienti possono interagire direttamente dall'applicazione di messaggistica che hanno già utilizzato. Scopri come sviluppare ed eseguire il deployment di applicazioni intelligenti che funzionano senza problemi su interfacce web e canali di messaggistica moderni.

Cosa creerai

Integrazione tra un'applicazione completa di "Concierge del ristorante" basata su ADK e alimentata da Gemini che aiuta i clienti a sfogliare il menu di un ristorante e a prenotare, e l'app di chat Telegram. Puoi interagire con il bot Telegram e chiedere descrizioni in linguaggio naturale come "Voglio qualcosa di piccante e vegetariano". Il bot si connetterà quindi all'agente ADK che legge e scrive in un database Cloud SQL PostgreSQL interamente tramite MCP Toolbox for Databases, che gestisce tutto l'accesso al database, inclusa la generazione automatica di incorporamenti per la ricerca vettoriale. Nel frattempo, l'utente potrà vedere che il bot sta riconoscendo il messaggio e digitando ... typing per la risposta mentre attende la risposta dell'agente ADK.

c1d28343ed68358a.png

Cosa imparerai a fare

  • Esegui il deployment di un'applicazione "Restaurant Concierge" basata su ADK e Gemini
  • Configurare il bot di chat Telegram utilizzando BotFather
  • Scrivere applicazioni Python per ascoltare il webhook del bot
  • Invia l'azione di chat per fornire la notifica ... typing in Telegram sul messaggio dell'utente ed esegui il polling per inviare ... typing periodicamente durante l'attesa della risposta effettiva
  • Chiama l'endpoint Cloud Run Restaurant Concierge per elaborare la richiesta dell'utente
  • Gestisci il ritorno dall'agente ADK, invia il messaggio a Telegram e chiudi il buffer
  • Esegui il deployment dell'applicazione Python in Cloud Run
  • Interagire con il bot Telegram

Prerequisiti

2. Configurazione dell'ambiente - Proseguimento del codelab precedente

Le narrazioni che forniamo in questo codelab sono in realtà la continuazione di questo codelab prerequisito: RAG agentica con ADK, MCP Toolbox e Cloud SQL o Agenti su larga scala: architettura multi-agente con protocollo A2A su runtime dell'agente e integrazione ADK. Puoi continuare il lavoro del codelab precedente

Possiamo iniziare a creare nella directory di lavoro del codelab precedente ( la directory di lavoro deve essere build-agent-adk-toolbox-cloudsql o adk-a2a-agent-runtime-starter). Per evitare confusione, rinominiamo la directory con lo stesso nome che utilizziamo quando iniziamo da zero.

Se continui dal lab RAG agentica con ADK, MCP Toolbox e Cloud SQL :

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

Altrimenti, se continui dal lab Agenti su larga scala: architettura multi-agente con protocollo A2A su Agent Runtime e integrazione ADK

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

Quindi, cambia la directory di lavoro impostandola su questa directory.

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

Dopodiché, verifica che il tuo restaurant-agent sia già stato implementato e abbia un URL pubblico a cui accedere.

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

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

Se riesci ad accedere all'URL, puoi passare alla sezione successiva: Create Telegram Bot

3. Configurazione dell'ambiente: un nuovo inizio con il repository iniziale

Questo passaggio prepara l'ambiente Cloud Shell, configura il progetto Google Cloud e clona il repository iniziale.

Apri Cloud Shell

Apri Cloud Shell nel browser. Cloud Shell fornisce un ambiente preconfigurato con tutti gli strumenti necessari per questo codelab. Quando richiesto, fai clic su Autorizza.

Poi fai clic su "Visualizza" -> "Terminale" per aprire il terminale.L'interfaccia dovrebbe avere un aspetto simile a questo

86307fac5da2f077.png

Questa sarà la nostra interfaccia principale, con l'IDE in alto e il terminale in basso.

Configurare la directory di lavoro

Clona il repository iniziale. Tutto il codice che scrivi in questo codelab si trova qui:

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 il file .env dal modello fornito:

cp .env.example .env

Per semplificare la configurazione del progetto nel terminale, scarica questo script di configurazione del progetto nella tua directory di lavoro:

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

Esegui lo script. Verifica il tuo account di fatturazione di prova, crea un nuovo progetto (o ne convalida uno esistente), salva l'ID progetto in un file .env nella directory corrente e imposta il progetto attivo in gcloud.

bash setup_verify_trial_project.sh && source .env

Lo script:

  1. Verificare di avere un account di fatturazione di prova attivo
  2. Controlla se esiste un progetto in .env (se presente)
  3. Crea un nuovo progetto o riutilizza quello esistente
  4. Collega l'account di fatturazione di prova al tuo progetto
  5. Salva l'ID progetto in .env
  6. Imposta il progetto come progetto gcloud attivo

Verifica che il progetto sia impostato correttamente controllando il testo giallo accanto alla directory di lavoro nel prompt del terminale Cloud Shell. Dovrebbe essere visualizzato l'ID progetto.

5c515e235ee1179f.png

Starter Infrastructure Setup

Innanzitutto, dovremo installare le dipendenze Python utilizzando uv, un gestore di pacchetti e progetti Python veloce scritto in Rust ( documentazione di uv). Questo codelab lo utilizza per la velocità e la semplicità di manutenzione del progetto Python

uv sync

Poi esegui lo script di configurazione completo, che crea l'istanza Cloud SQL, inserisce i dati e implementa il servizio Toolbox che fungerà da stato iniziale del nostro agente ristorante

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

Questo ti permetterà di:

  • Crea un'istanza Cloud SQL e inizializza il database (fase 1)
  • Genera la configurazione dell'ambiente dell'agente e avvia il servizio Toolbox locale (fase 2)
  • Esegui il deployment dei servizi Toolbox e Agent su Cloud Run (fase 3)

Al termine del deployment, puoi accedere all'interfaccia utente per sviluppatori dell'ADK nell'URL di 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 ""

Apri l'interfaccia utente per sviluppatori dell'ADK, seleziona restaurant_agent e testa con query come l'esempio seguente:

What Italian dishes do you have?

Oppure,

I want something spicy and creamy

Ora, la prossima azione è come possiamo passare dall'interfaccia di sviluppo web al canale di messaggistica Telegram

4. Crea bot Telegram

Telegram è una piattaforma di messaggistica senza costi molto nota che viene ampiamente utilizzata per il coinvolgimento basato sulla community. Uno dei motivi è che offre molti modi per integrarsi facilmente, quindi le persone possono creare facilmente il proprio bot con una grande varietà di funzioni.

Nel nostro caso, utilizzeremo BotFather per creare il nostro bot per la prima volta. Tieni presente che, sebbene in questa sessione utilizzeremo Telegram, lo stesso metodo può essere utilizzato per WhatsApp o altre piattaforme di messaggistica a tua scelta.

Utilizzare BotFather per creare il tuo bot

Apri il browser web e visita https://telegram.me/BotFather per iniziare a creare il tuo bot Telegram.

1b817e758c699a79.png

Iniziare a interagire con BotFather

ad3daa08e73502db.png

Inviare il comando /start

Per iniziare a utilizzare BotFather e creare il tuo primo bot, devi richiamare il messaggio /start per BotFather, che condividerà tutti i comandi che ha a disposizione per interagire ulteriormente.

/start

Avviare la creazione di bot con il comando /newbot

Creiamo il nostro nuovo bot inviando il comando /newbot a BotFather. Ti verrà chiesto di dare un nome al bot e poi di assegnargli username, che deve sempre terminare con bot . Ad esempio, TetrisBot o tetris_bot. Deve essere univoco.

1f6a74f494d48986.png

Una volta creato correttamente il bot, riceverai il seguente messaggio da 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

Prendi nota di YOUR_TELEGRAM_API_KEY, lo utilizzeremo nella sezione successiva.

5. Sviluppare l'applicazione webhook di Telegram

Prepariamo la directory di lavoro per iniziare a sviluppare l'applicazione webhook di Telegram

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

Aggiungere le dipendenze richieste

Crea lo script requirements.txt con i seguenti contenuti per fornire le dipendenze adeguate per lo script del listener webhook di Telegram.

cloudshell edit ./telegram-integration/requirements.txt

Quindi aggiungi le seguenti dipendenze

python-telegram-bot[webhooks]
httpx

Crea lo script per il listener webhook di Telegram

Una volta installata la dipendenza. Ora possiamo creare uno script Python main.py per l'applicazione di integrazione

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

Quindi, copia il seguente codice

# ./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()

Informazioni sul codice di integrazione del bot Telegram

23b346f5ceb4712a.png

Quando un utente invia un messaggio, la seguente pipeline viene eseguita in handle_message()

Passaggio 1: derivazione di identità e sessione

Il bot mappa l'ID utente Telegram a identificatori ADK unici per mantenere distinte le sessioni utente:

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

Passaggio 2: stato "Digitazione" asincrono (righe 53-58)

Per garantire un'esperienza utente altamente reattiva mentre l'agente ADK elabora il prompt (operazione che può richiedere diversi secondi), il bot avvia un ciclo asincrono in background:

  • asyncio.Event viene istanziato come stop_event.
  • asyncio.create_task genera send_typing_loop(...) in background.
  • Il ciclo invia un'azione ChatAction.TYPING a Telegram ogni 4 secondi finché non viene impostato stop_event.

Passaggio 3: verifica e creazione della sessione ADK (righe 61-72)

Prima di eseguire l'agente, il bot controlla se esiste già una sessione:

  1. Invia una richiesta GET a /apps/{appName}/users/{userId}/sessions/{sessionId}.
  2. Se la risposta è 404 Not Found, la sessione viene creata tramite una richiesta POST allo stesso URL con un corpo JSON vuoto.
  3. Se viene restituito uno stato diverso da 200 o 404, viene generata un'eccezione.

Passaggio 4: invio della richiesta all'agente (righe 74-85)

Il payload del messaggio viene inoltrato all'endpoint /run dell'ADK:

  • Endpoint: POST /run
  • Il timeout della richiesta è impostato su 60.0 secondi per consentire ragionamenti complessi o latenza upstream.
  • Struttura del payload:
{
  "appName": "restaurant_agent",
  "userId": "tg_<user_id>",
  "sessionId": "tg_sess_<user_id>",
  "newMessage": {
    "role": "user",
    "parts": [{"text": "<user_message>"}]
  }
}

Passaggio 5: analisi della risposta (righe 87-101)

Il server ADK restituisce un elenco di eventi di messaggi. Il bot esamina l'array restituito:

  • Recupera l'ultimo evento nell'elenco (events[-1]).
  • Va ai contenuti di testo tramite event["content"]["parts"][0]["text"].
  • Se non vengono restituiti eventi o se manca la struttura del testo, viene impostato un testo segnaposto descrittivo.

Passaggio 6: smontaggio e invio della risposta (righe 103-111)

  • Nel blocco finally, stop_event è impostato, interrompendo il ciclo dell'azione di digitazione.
  • Il bot attende il completamento di typing_task per garantire risorse pulite.
  • Infine, il bot risponde alla chat di Telegram con il testo della risposta analizzata.

6. Esegui il deployment dell'applicazione webhook di Telegram in Cloud Run

Successivamente, eseguiremo il deployment del listener webhook di Telegram in Cloud Run, in modo che il nostro bot possa comunicare con esso

Crea il Dockerfile

Innanzitutto, dobbiamo creare il Dockerfile.

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

Quindi, copia il seguente codice

# 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"]

Il servizio è containerizzato utilizzando python:3.11-slim per mantenere ridotto l'ingombro dell'immagine:

  • Installa le dipendenze da requirements.txt (python-telegram-bot[webhooks] e httpx).
  • Espone la porta standard 8080.
  • Lanciato il giorno python main.py.

Prepara le variabili di ambiente

Dopodiché, controlliamo di nuovo se l'agente è stato implementato correttamente.

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

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

Successivamente, inseriamo il TELEGRAM_BOT_TOKEN che abbiamo ottenuto in precedenza in .env

echo "TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_API_KEY" >> .env

Poi, compiliamo i dati .env con altri valori di cui abbiamo bisogno.

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

Crea script di deployment

Creiamo uno script di deployment che fornisca controlli completi ed esegui il deployment dell'app su Cloud Run

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

e copia il seguente codice nel file.

#!/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."

Script di doppio deployment (deploy.sh)

Quando esegue il deployment su Google Cloud Run, il bot deve specificare il proprio URL (SERVICE_URL) nel suo ambiente in modo da poterlo registrare come target webhook con Telegram. Per risolvere questa dipendenza ciclica (l'URL è sconosciuto fino al deployment, ma il servizio richiede l'URL per l'avvio senza errori di controllo di integrità), deploy.sh esegue un deployment in due fasi:

  1. Passaggio 1: deployment iniziale: avvia il container con un DNS segnaposto (https://google.com) in modo che il servizio si avvii correttamente, si associ alla porta locale e superi i controlli di integrità iniziali di Cloud Run.
  2. Passaggio 2: recupera l'URL: estrae a livello di programmazione l'endpoint Cloud Run appena creato utilizzando gcloud run services describe.
  3. Passaggio 3: aggiorna la configurazione: aggiorna le variabili di ambiente con l'URL del servizio live effettivo. In questo modo viene attivato un aggiornamento in sequenza pulito in Cloud Run e viene registrato in modo sicuro il target webhook corretto con l'API Telegram.

Esegui il deployment in Cloud Run

Lo script di deployment stampa l'URL dell'agente. Apri il link nel browser per accedere alla stessa UI di sviluppo dell'ADK in esecuzione su Cloud Run.

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

Se tutto va bene, puoi iniziare a chattare con il bot direttamente dall'applicazione di chat Telegram. Trova il bot che hai appena creato e inizia a interagire con lui:

What Italian dishes do you have?

Oppure,

I want something spicy and creamy

Guarda lo stato di invio del bot "...sta scrivendo" e poi, a breve, restituirà il messaggio dell'ADK che hai creato in precedenza.

c62fd4016ddd3c9b.png

7. Complimenti!

Hai creato, fatto il deployment e integrato completamente il nostro agente AI ADK dell'assistente per il menu del ristorante intelligente con Telegram, tramite la comunicazione client-server HTTP, e consenti alle persone di fare query sul loro menu preferito e prenotare il ristorante.

Che cosa hai imparato

  • Esegui il deployment e configura Restaurant Concierge, l'agente basato su ADK e MCP Toolbox su Cloud Run
  • Come configurare il bot Telegram utilizzando BotFather
  • Come scrivere script Python per ascoltare il webhook di Telegram e interagire con l'agente ADK per trasmettere la query e la risposta degli utenti di conseguenza
  • Come implementare "... typing" in Telegram per segnalare che i messaggi vengono elaborati come feedback in tempo reale agli utenti in attesa della risposta dell'agente ADK.
  • Come eseguire il deployment dello script Python in Cloud Run e poter interagire con esso

Pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi, elimina le risorse create in questo codelab.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Opzione 2: elimina singole risorse

# 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