Créer une application d'IA agentique avec laquelle vos clients peuvent interagir via Telegram

1. Introduction

Créez des expériences d'IA agentique interactives et fluides avec lesquelles vos clients peuvent interagir directement depuis l'application de messagerie qu'ils utilisent déjà. Découvrez comment développer et déployer des applications intelligentes qui s'exécutent de manière fluide sur les interfaces Web et les canaux de messagerie modernes.

Objectifs de l'atelier

Intégration entre un "concierge de restaurant" complet, une application basée sur ADK optimisée par Gemini qui aide les clients à parcourir le menu d'un restaurant et à réserver, et l'application de chat Telegram. Vous pouvez interagir avec le bot Telegram et demander des descriptions en langage naturel, par exemple "Je veux quelque chose d'épicé et de végétarien". Le bot se connecte ensuite à l'agent ADK, qui lit et écrit dans une base de données Cloud SQL PostgreSQL entièrement via MCP Toolbox for Databases, qui gère tous les accès à la base de données, y compris la génération automatique d'embeddings pour la recherche vectorielle. Pendant ce temps, l'utilisateur peut voir que le bot accuse réception du message et saisit ... typing pour répondre en attendant le retour de l'agent ADK.

c1d28343ed68358a.png

Points abordés

  • Déployer un "concierge de restaurant" fonctionnel, une application basée sur ADK optimisée par Gemini
  • Configurer le bot de chat Telegram à l'aide de BotFather
  • Écrire des applications Python pour écouter le webhook du bot
  • Envoyer une action de chat pour fournir une notification ... typing dans Telegram sur le message de l'utilisateur et effectuer un polling pour envoyer ... typing périodiquement en attendant la réponse réelle
  • Appeler le point de terminaison Cloud Run Restaurant Concierge pour traiter la requête de l'utilisateur
  • Gérer le retour de l'agent ADK, envoyer un message à Telegram et fermer le tampon
  • Déployer l'application Python dans Cloud Run
  • Interagir avec votre bot Telegram

Prérequis

2. Configuration de l'environnement : continuer à partir de l'atelier de programmation précédent

Les récits que nous fournissons dans cet atelier de programmation sont en fait la suite de cet atelier de programmation prérequis : RAG agentique avec ADK, MCP Toolbox et Cloud SQL ou Agents à grande échelle : architecture multi-agents avec le protocole A2A sur Agent Runtime et intégration ADK. Vous pouvez continuer votre travail à partir de l'atelier de programmation précédent.

Nous pouvons commencer à créer dans le répertoire de travail de l'atelier de programmation précédent ( le répertoire de travail doit être build-agent-adk-toolbox-cloudsql ou adk-a2a-agent-runtime-starter). Pour éviter toute confusion, renommons le répertoire avec le même nom que celui que nous utilisons lorsque nous repartons à zéro.

Si vous continuez à partir de l'atelier RAG agentique avec ADK, MCP Toolbox et Cloud SQL :

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

Sinon, si vous continuez à partir de l'atelier Agents à grande échelle : architecture multi-agents avec le protocole A2A sur Agent Runtime et intégration ADK

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

Ensuite, remplacez notre répertoire de travail par celui-ci.

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

Ensuite, vérifiez que votre restaurant-agent est déjà déployé et dispose d'une URL publique pour y accéder.

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 vous pouvez accéder à l'URL, vous pouvez passer à la section suivante : Create Telegram Bot.

3. Configuration de l'environnement : repartir à zéro avec le dépôt de démarrage

Cette étape prépare votre environnement Cloud Shell, configure votre projet Google Cloud et clone le dépôt de démarrage.

Ouvrir Cloud Shell

Ouvrez Cloud Shell dans votre navigateur. Cloud Shell fournit un environnement préconfiguré avec tous les outils dont vous avez besoin pour cet atelier de programmation. Cliquez sur Autoriser lorsque vous y êtes invité.

Cliquez ensuite sur View (Afficher) -> Terminal pour ouvrir le terminal.Votre interface doit ressembler à ceci :

86307fac5da2f077.png

Il s'agit de notre interface principale, avec l'IDE en haut et le terminal en bas.

Configurer votre répertoire de travail

Clonez le dépôt de démarrage. Tout le code que vous écrivez dans cet atelier de programmation se trouve ici :

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

Créez le fichier .env à partir du modèle fourni :

cp .env.example .env

Pour simplifier la configuration du projet dans votre terminal, téléchargez ce script de configuration de projet dans votre répertoire de travail :

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

Exécutez le script. Il vérifie votre compte de facturation d'essai, crée un projet (ou en valide un existant), enregistre l'ID de votre projet dans un fichier .env du répertoire actuel et définit le projet actif dans gcloud.

bash setup_verify_trial_project.sh && source .env

Le script va :

  1. Vérifier que vous disposez d'un compte de facturation d'essai actif
  2. Rechercher un projet existant dans .env (le cas échéant)
  3. Créer un projet ou réutiliser celui existant
  4. Associer le compte de facturation d'essai à votre projet
  5. Enregistrer l'ID du projet dans .env
  6. Définir le projet comme projet gcloud actif

Vérifiez que le projet est correctement défini en examinant le texte jaune à côté de votre répertoire de travail dans l'invite du terminal Cloud Shell. Il doit afficher l'ID de votre projet.

5c515e235ee1179f.png

Configuration de l'infrastructure de démarrage

Tout d'abord, nous devons installer les dépendances Python à l'aide de uv. Il s'agit d'un gestionnaire de packages et de projets Python rapide écrit en Rust ( documentation uv). Cet atelier de programmation l'utilise pour sa rapidité et sa simplicité de maintenance du projet Python.

uv sync

Exécutez ensuite le script de configuration complet, qui crée l'instance Cloud SQL, amorce les données et déploie le service Toolbox qui servira d'état initial de notre agent de restaurant.

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

Cela va permettre de :

  • Créer une instance Cloud SQL et amorcer la base de données (phase 1)
  • Générer la configuration de l'environnement de l'agent et démarrer le service de boîte à outils local (phase 2)
  • Déployer les services de boîte à outils et d'agent dans Cloud Run (phase 3)

Une fois ce déploiement terminé, vous pouvez accéder à l'UI de développement ADK sur l'URL 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 ""

Ouvrez l'UI de développement ADK, sélectionnez restaurant_agent et testez-la avec des requêtes comme dans l'exemple suivant :

What Italian dishes do you have?

Ou

I want something spicy and creamy

L'étape suivante consiste à passer d'une interface de développement Web uniquement à un canal de messagerie Telegram.

4. Créer un bot Telegram

Telegram est une plate-forme de messagerie sans frais bien connue, largement utilisée pour l'engagement communautaire. L'une des raisons est qu'elle offre de nombreuses façons de s'intégrer facilement. Les utilisateurs peuvent ainsi créer facilement leur propre bot avec une grande variété de fonctions.

Dans notre cas, nous allons utiliser BotFather pour créer notre propre bot pour la première fois. N'oubliez pas que, bien que nous utilisions Telegram pour cette session, la même méthode peut être utilisée pour WhatsApp ou d'autres plates-formes de messagerie de votre choix.

Utiliser BotFather pour créer votre propre bot

Pointez votre navigateur Web et accédez à https://telegram.me/BotFather pour commencer à créer votre propre bot Telegram.

1b817e758c699a79.png

Commencer à interagir avec BotFather

ad3daa08e73502db.png

Envoyer la commande /start

Pour commencer à utiliser BotFather et créer votre premier bot, vous devez appeler le message /start à BotFather. Il partagera ensuite toutes les commandes dont il dispose pour que vous puissiez interagir avec lui.

/start

Lancer la création du bot avec la commande /newbot

Créons notre nouveau bot en envoyant la commande /newbot à BotFather. Il vous demandera de nommer votre bot, puis de lui attribuer un username qui doit toujours se terminer par bot . Par exemple, TetrisBot ou tetris_bot. Ce nom doit être unique.

1f6a74f494d48986.png

Une fois le bot créé, vous recevrez le message suivant 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

Notez YOUR_TELEGRAM_API_KEY. Nous l'utiliserons dans la section suivante.

5. Développer l'application de webhook Telegram

Préparons le répertoire de travail pour commencer à développer votre application de webhook Telegram.

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

Ajouter les dépendances requises

Créez un script requirements.txt avec le contenu suivant pour fournir des dépendances adéquates au script d'écoute du webhook Telegram.

cloudshell edit ./telegram-integration/requirements.txt

Ajoutez ensuite les dépendances suivantes :

python-telegram-bot[webhooks]
httpx

Créer un script pour l'écouteur de webhook Telegram

Une fois la dépendance installée, nous pouvons créer un script Python main.py pour l'application d'intégration.

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

Copiez ensuite le code suivant :

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

Comprendre le code d'intégration du bot Telegram

23b346f5ceb4712a.png

Lorsqu'un utilisateur envoie un message, le pipeline suivant s'exécute sous handle_message() :

Étape 1 : Dérivation de l'identité et de la session

Le bot mappe l'ID utilisateur Telegram à des identifiants ADK uniques pour que les sessions utilisateur restent distinctes :

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

Étape 2 : État "Saisie en cours" asynchrone (lignes 53 à 58)

Pour garantir une expérience utilisateur très réactive pendant que l'agent ADK traite l'invite (ce qui peut prendre plusieurs secondes), le bot démarre une boucle d'arrière-plan asynchrone :

  • asyncio.Event est instancié en tant que stop_event.
  • asyncio.create_task génère send_typing_loop(...) en arrière-plan.
  • La boucle envoie une action ChatAction.TYPING à Telegram toutes les quatre secondes jusqu'à ce que stop_event soit défini.

Étape 3 : Vérification et création de la session ADK (lignes 61 à 72)

Avant d'exécuter l'agent, le bot vérifie si une session existe déjà :

  1. Envoie une requête GET à /apps/{appName}/users/{userId}/sessions/{sessionId}.
  2. Si la réponse est 404 Not Found, elle crée la session via une requête POST à la même URL avec un corps JSON vide.
  3. Si un état autre que 200 ou 404 est renvoyé, une exception est générée.

Étape 4 : Envoi de la requête à l'agent (lignes 74 à 85)

La charge utile du message est transmise au point de terminaison ADK /run :

  • Point de terminaison : POST /run
  • Le délai avant expiration de la requête est défini sur 60.0 secondes pour tenir compte du raisonnement complexe ou de la latence en amont.
  • Structure de la charge utile :
{
  "appName": "restaurant_agent",
  "userId": "tg_<user_id>",
  "sessionId": "tg_sess_<user_id>",
  "newMessage": {
    "role": "user",
    "parts": [{"text": "<user_message>"}]
  }
}

Étape 5 : Analyse de la réponse (lignes 87 à 101)

Le serveur ADK renvoie une liste d'événements de message. Le bot inspecte le tableau renvoyé :

  • Il récupère l'événement final dans la liste (events[-1]).
  • Il accède au contenu textuel via event["content"]["parts"][0]["text"].
  • Si aucun événement n'est renvoyé ou si la structure du texte est manquante, un texte d'espace réservé descriptif est défini.

Étape 6 : Démontage et distribution de la réponse (lignes 103 à 111)

  • Dans le bloc finally, stop_event est défini, ce qui arrête la boucle d'action de saisie.
  • Le bot attend la fin de la typing_task pour s'assurer que les ressources sont propres.
  • Enfin, le bot répond au chat Telegram avec le texte de réponse analysé.

6. Déployer l'application de webhook Telegram dans Cloud Run

Nous allons ensuite déployer l'écouteur de webhook Telegram dans Cloud Run afin que notre bot puisse communiquer avec lui.

Créer le fichier Dockerfile

Tout d'abord, nous devons créer le fichier Dockerfile.

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

Copiez ensuite le code suivant :

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

Le service est conteneurisé à l'aide de python:3.11-slim pour réduire l'encombrement de l'image :

  • Installe les dépendances à partir de requirements.txt (python-telegram-bot[webhooks] et httpx).
  • Expose le port standard 8080.
  • Lance python main.py.

Préparer les variables d'environnement

Ensuite, vérifions si notre agent a bien été déployé.

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

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

Ajoutons ensuite le TELEGRAM_BOT_TOKEN que nous avons obtenu précédemment au .env.

echo "TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_API_KEY" >> .env

Ensuite, remplissons les données .env avec d'autres valeurs dont nous avons besoin.

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

Créer un script de déploiement

Créons un script de déploiement qui effectuera des vérifications complètes et déploiera l'application dans Cloud Run.

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

Copiez le code suivant dans le fichier :

#!/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 de double déploiement (deploy.sh)

Lors du déploiement dans Google Cloud Run, le bot doit spécifier sa propre URL (SERVICE_URL) dans son environnement afin de pouvoir l'enregistrer en tant que cible de webhook auprès de Telegram. Pour résoudre cette dépendance circulaire (l'URL est inconnue jusqu'au déploiement, mais le service nécessite l'URL pour démarrer sans échec de l'état de santé), deploy.sh effectue un déploiement en deux étapes :

  1. Étape 1 : Déploiement initial : démarre le conteneur avec un espace réservé DNS (https://google.com) afin que le service démarre correctement, se lie au port local et réussisse les vérifications de l'état de santé initiales de Cloud Run.
  2. Étape 2 : Récupérer l'URL : extrait par programmation le point de terminaison Cloud Run nouvellement créé à l'aide de gcloud run services describe.
  3. Étape 3 : Mettre à jour la configuration : met à jour les variables d'environnement avec l'URL du service en direct. Cela déclenche une mise à jour progressive propre dans Cloud Run et enregistre en toute sécurité la cible de webhook correcte auprès de l'API Telegram.

Déployer une application sur Cloud Run

Le script de déploiement affiche l'URL de l'agent. Ouvrez-la dans votre navigateur pour accéder à la même UI de développement ADK exécutée sur Cloud Run.

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

Si tout se passe bien, vous pouvez commencer à discuter avec votre bot directement depuis l'application de chat Telegram. Recherchez le bot que vous venez de créer et commencez à interagir avec lui :

What Italian dishes do you have?

Ou

I want something spicy and creamy

Regardez le bot envoyer l'état "...is typing" (Saisie en cours), puis il renverra bientôt le message de l'ADK que vous avez créé précédemment.

c62fd4016ddd3c9b.png

7. Félicitations !

Vous avez créé, déployé et entièrement intégré notre assistant de menu de restaurant intelligent basé sur ADK avec Telegram, via la communication entre le client et le serveur HTTP, et vous permettez aux utilisateurs d'interroger leur menu préféré et de réserver le restaurant.

Connaissances acquises

  • Déployer et configurer Restaurant Concierge, un agent basé sur ADK et MCP Toolbox dans Cloud Run
  • Configurer un bot Telegram à l'aide de BotFather
  • Écrire des scripts Python pour écouter le webhook Telegram et interagir avec l'agent ADK afin de transmettre la requête de l'utilisateur et la réponse en conséquence
  • Implémenter "... typing" dans Telegram pour signaler que les messages sont en cours de traitement en tant que feedback en temps réel aux utilisateurs en attendant que l'agent ADK réponde.
  • Déployer le script Python dans Cloud Run et pouvoir interagir avec lui

Nettoyage

Pour éviter que votre compte Google Cloud ne soit facturé, supprimez les ressources créées dans cet atelier de programmation.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Option 2 : Supprimer des ressources individuelles

# 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