0Pricing
AI Agents · Lección

Escucha de eventos y comandos de barra

app_mention, comandos de barra y gestores de acciones en Slack Bolt.

Escucha de eventos y comandos de barra es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.

Descripción general de las suscripciones a eventos de Slack

Slack envía eventos a su aplicación cuando ocurren determinadas acciones: se publica un mensaje, alguien menciona su bot o un usuario se une a un canal. Suscríbase a tipos de eventos específicos en el panel de Slack App y, después, registre los controladores en Bolt mediante el decorador @app.event().

from slack_bolt import App
import os

app = App(
    token=os.environ['SLACK_BOT_TOKEN'],
    signing_secret=os.environ['SLACK_SIGNING_SECRET']
)

# Register event handlers with @app.event()
# The string argument must match the Slack event type exactly

@app.event('app_mention')
def handle_mention(event, say, logger):
    logger.info(f'Mention event: {event}')
    say(f'Hello <@{event["user"]}>!')

@app.event('message')
def handle_message(event, say):
    # Fires for every message in subscribed channels
    if event.get('subtype') is None:  # ignore bot messages
        print(f'Message: {event["text"]}')

Gestión de eventos app_mention

El evento app_mention se activa cuando alguien escribe @YourBot en un canal. event['text'] contiene el mensaje completo, incluida la mención. Elimine el prefijo de la mención para obtener la consulta real del usuario.

import re

@app.event('app_mention')
def handle_mention(event, say, client):
    # event['text'] example: '<@U0123BOT> summarize this'
    text = event.get('text', '')
    user_id = event['user']
    channel = event['channel']

    # Remove the @mention to get the clean query
    clean_text = re.sub(r'<@[A-Z0-9]+>', '', text).strip()
    print(f'User {user_id} asked: {clean_text}')

    if not clean_text:
        say(f'Hi <@{user_id}>! How can I help you today?')
        return

    # Process the query
    response = process_user_query(clean_text, user_id)
    say(response)

Acceso al payload del evento

Cada controlador de eventos recibe el diccionario event, que contiene el payload sin procesar del evento de Slack. Campos clave: event['user'] (ID de usuario), event['channel'] (ID de canal), event['text'] (contenido del mensaje) y event['ts'] (marca de tiempo o ID del mensaje).

@app.event('app_mention')
def handle_mention(event, say, client):
    print('Event type:', event.get('type'))
    print('User ID:', event.get('user'))     # e.g. 'U0123ABC'
    print('Channel:', event.get('channel'))   # e.g. 'C0456DEF'
    print('Text:', event.get('text'))          # full message text
    print('Timestamp:', event.get('ts'))       # '1234567890.123456'
    print('Thread TS:', event.get('thread_ts')) # if in a thread

    # Get full user info from the user ID
    user_info = client.users_info(user=event['user'])
    real_name = user_info['user']['real_name']
    email = user_info['user']['profile'].get('email', '')
    say(f'Hello {real_name}!')

Comandos slash: registro y respuesta

Los comandos slash permiten que los usuarios activen acciones del agente desde cualquier canal de Slack. Registre la URL del comando en el panel de Slack App, en Slash Commands, y después gestiónelo con @app.command('/command-name'). Llame siempre a ack() de inmediato: Slack agota el tiempo de espera en 3 segundos si no lo hace.

@app.command('/summarize')
def handle_summarize(ack, body, say, respond):
    # CRITICAL: ack() must be called within 3 seconds
    ack()  # acknowledge the command immediately

    # body contains the command payload
    user_id = body['user_id']
    channel_id = body['channel_id']
    text = body.get('text', '').strip()  # text after the command

    print(f'User {user_id} ran /summarize with: "{text}"')

    if not text:
        respond('Usage: /summarize <text or URL to summarize>')
        return

    # Process and respond
    summary = generate_summary(text)
    say(f'Summary by <@{user_id}>:\n{summary}')

ack(): la regla de los 3 segundos

Slack requiere que su aplicación llame a ack() (acuse de recibo) para cada comando slash y payload interactivo entrante en un plazo de 3 segundos. Si no lo hace, Slack muestra un error al usuario. Para operaciones de larga duración, llame a ack() de inmediato, inicie el procesamiento en un hilo en segundo plano y, después, responda mediante respond() con el resultado.

import threading

@app.command('/analyze')
def handle_analyze(ack, body, respond):
    ack()  # Must be within 3 seconds!

    text = body.get('text', '').strip()
    if not text:
        respond('Please provide text to analyze.')
        return

    # For slow operations: run in background thread
    def process_in_background():
        result = slow_ai_analysis(text)  # may take 10+ seconds
        respond(f'Analysis complete:\n{result}')

    thread = threading.Thread(target=process_in_background)
    thread.daemon = True
    thread.start()

    # respond() is safe to call from a different thread
    # ack() already sent, Slack won't time out

say() frente a respond(): cuándo usar cada uno

Dos funciones publican mensajes de vuelta en Slack:

  • say(): publica en el canal donde ocurrió el evento; todos pueden verlo
  • respond(): solo está disponible en los controladores de comandos slash; puede publicar mensajes efímeros visibles únicamente para el usuario que ejecutó el comando

Use respond(response_type='in_channel') para las respuestas públicas y respond(response_type='ephemeral') para las privadas.

@app.command('/status')
def handle_status(ack, respond, body):
    ack()

    # Ephemeral: only visible to the user who ran the command
    respond(
        text='Agent status: Running | Queue: 3 tasks | Uptime: 4h 22m',
        response_type='ephemeral'  # private to command user
    )

@app.command('/broadcast')
def handle_broadcast(ack, say, respond, body):
    ack()
    text = body.get('text', '')

    # Public: visible to everyone in the channel
    say(
        text=f'<@{body["user_id"]}> broadcast: {text}',
        channel=body['channel_id']
    )

    # Confirm privately to the sender
    respond('Broadcast sent!', response_type='ephemeral')

Eventos de mensajes y subtipos

El evento genérico message se activa para todos los mensajes, incluidos los mensajes de bots, las ediciones y las eliminaciones. Use el campo subtype para filtrarlos. Subtipos comunes: bot_message, message_changed y message_deleted. Si subtype está ausente, se trata de un mensaje normal de un usuario.

@app.event('message')
def handle_message(event, say, client):
    subtype = event.get('subtype')

    # Ignore bot messages to prevent loops
    if subtype == 'bot_message':
        return

    # Ignore message edits and deletes
    if subtype in ('message_changed', 'message_deleted'):
        return

    # Only process direct messages (DMs) to the bot
    channel_type = event.get('channel_type', '')
    if channel_type == 'im':
        text = event.get('text', '').strip()
        user = event['user']
        print(f'DM from {user}: {text}')
        say(f'You said: {text}')

Escucha de reacciones

El evento reaction_added se activa cuando alguien añade una reacción con emoji a un mensaje. Puede usarlo para activar acciones del agente; por ejemplo, añadir una reacción 📌 para guardar un mensaje o una ✅ para marcar una tarea como completada.

@app.event('reaction_added')
def handle_reaction(event, client, say):
    reaction = event['reaction']  # e.g. 'thumbsup', 'white_check_mark'
    user_id = event['user']  # who reacted
    item = event['item']  # what was reacted to

    print(f'User {user_id} reacted :{reaction}: to {item["type"]}')

    if reaction == 'white_check_mark' and item['type'] == 'message':
        # Fetch the original message
        result = client.conversations_history(
            channel=item['channel'],
            oldest=item['ts'],
            latest=item['ts'],
            inclusive=True,
            limit=1
        )
        messages = result.get('messages', [])
        if messages:
            text = messages[0].get('text', '')
            print(f'Task completed: {text[:100]}')

Payloads de acciones de componentes interactivos

Cuando un usuario hace clic en un botón o selecciona un elemento de menú, Slack envía un payload de acción. Gestiónelo con @app.action('action_id'). El ID de acción es la cadena que estableció al crear el componente de Block Kit. Llame siempre a ack() de inmediato.

@app.action('approve_task')
def handle_approve(ack, body, respond, client):
    ack()  # acknowledge within 3 seconds

    action = body['actions'][0]  # the button that was clicked
    action_id = action['action_id']  # 'approve_task'
    value = action.get('value', '')  # data attached to the button
    user_id = body['user']['id']

    print(f'User {user_id} clicked {action_id} with value: {value}')

    # Update the original message to show it was approved
    client.chat_update(
        channel=body['container']['channel_id'],
        ts=body['container']['message_ts'],
        text=f'Task approved by <@{user_id}>',
        blocks=[]  # remove buttons after action
    )
    respond('Task approved!', response_type='ephemeral')

Filtrado de eventos por canal o usuario

En los espacios de trabajo grandes, su bot puede recibir eventos de muchos canales. Filtre al principio del controlador para procesar únicamente los eventos relevantes. Compruebe event['channel'] con una lista de elementos permitidos o use event['user'] para ignorar determinados usuarios, como otros bots.

import os

# Only respond in designated channels
ALLOWED_CHANNELS = set(
    os.environ.get('ALLOWED_CHANNELS', '').split(',')
)

BOT_USER_IDS = set()  # will be populated at startup

@app.event('app_mention')
def handle_mention(event, say, client):
    channel = event.get('channel', '')
    user = event.get('user', '')

    # Skip if channel not in allowed list (if list is configured)
    if ALLOWED_CHANNELS and channel not in ALLOWED_CHANNELS:
        return

    # Skip if the 'user' is actually a bot
    if user in BOT_USER_IDS:
        return

    text = event.get('text', '').strip()
    say(f'Processing: {text[:50]}')

Compatibilidad con respuestas en hilos

Para responder en un hilo, en lugar de hacerlo en el canal principal, pase thread_ts a say(). Use event.get('thread_ts', event['ts']) para obtener la marca de tiempo del hilo: thread_ts solo existe si el mensaje ya está en un hilo; de lo contrario, use el ts del propio mensaje para iniciar un hilo nuevo.

@app.event('app_mention')
def handle_mention_in_thread(event, say):
    user = event['user']
    text = event.get('text', '').strip()

    # Reply in the same thread (or start a new one)
    thread_ts = event.get('thread_ts') or event.get('ts')

    response_text = f'<@{user}>, processing your request...'

    say(
        text=response_text,
        thread_ts=thread_ts  # keeps the reply in the thread
    )

    # Do the actual work
    result = do_agent_work(text)

    say(
        text=f'Done! Result:\n{result}',
        thread_ts=thread_ts
    )

Comprobación rápida: tiempo de respuesta de ack()

Compruebe sus conocimientos sobre la gestión de eventos de Slack.

Repaso de eventos y comandos slash

Su agente de Slack ahora puede responder a cualquier interacción del usuario:

  • @app.event('app_mention'): gestione las menciones a @bot; elimine el prefijo de la mención con una expresión regular
  • @app.command('/cmd'): gestione los comandos slash; llame siempre a ack() en un plazo de 3 segundos
  • say(): publique en el canal para que todos lo vean; respond(): responda al comando slash, con la posibilidad de hacerlo de forma efímera
  • @app.action('id'): gestione los clics en botones y las interacciones con componentes interactivos
  • @app.event('reaction_added'): active acciones cuando se añadan reacciones con emoji
  • Use thread_ts en say() para mantener las respuestas en hilos
  • Filtre pronto por canal o usuario para evitar procesar eventos irrelevantes

Preguntas frecuentes

¿La lección «Escucha de eventos y comandos de barra» es gratis?

Sí — el texto completo de «Escucha de eventos y comandos de barra» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.

¿Qué aprenderé en «Escucha de eventos y comandos de barra»?

app_mention, comandos de barra y gestores de acciones en Slack Bolt. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Agents?

No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Escucha de eventos y comandos de barra»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Agents?

Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Fundamentos del SDK Slack Bolt
  2. Escucha de eventos y comandos de barra
  3. Envío de mensajes y bloques enriquecidos
  4. Creación de un bot de notificaciones para equipos
← Volver a AI Agents