0Pricing
AI Agents · Lekcja

Nasłuchiwanie zdarzeń i poleceń slash

app_mention, polecenia slash i handlery akcji w Slack Bolt.

Nasłuchiwanie zdarzeń i poleceń slash to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.

Przegląd subskrypcji zdarzeń Slack

Slack wysyła do Państwa aplikacji zdarzenia, gdy coś się dzieje — ktoś opublikuje wiadomość, wspomni o Państwa bocie albo dołączy do kanału. W panelu aplikacji Slack należy zasubskrybować konkretne typy zdarzeń, a następnie zarejestrować moduły obsługi w Bolt za pomocą dekoratora @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"]}')

Obsługa zdarzeń app_mention

Zdarzenie app_mention jest wywoływane, gdy ktoś wpisze na kanale @YourBot. Element event['text'] zawiera całą wiadomość wraz ze wzmianką. Należy usunąć prefiks wzmianki, aby uzyskać właściwe zapytanie użytkownika.

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)

Dostęp do payloadu zdarzenia

Każdy moduł obsługi zdarzeń otrzymuje słownik event, który zawiera surowy payload zdarzenia Slack. Najważniejsze pola: event['user'] (identyfikator użytkownika), event['channel'] (identyfikator kanału), event['text'] (treść wiadomości), event['ts'] (znacznik czasu/identyfikator wiadomości).

@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}!')

Polecenia ukośnikowe — rejestrowanie i odpowiadanie

Polecenia ukośnikowe pozwalają użytkownikom uruchamiać działania agenta z dowolnego kanału Slack. Należy zarejestrować adres URL polecenia w panelu aplikacji Slack (w sekcji Slash Commands), a następnie obsłużyć je za pomocą @app.command('/command-name'). Zawsze należy natychmiast wywołać ack() — Slack przerywa oczekiwanie po 3 sekundach, jeśli nie zostanie ono wywołane.

@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() — reguła 3 sekund

Slack wymaga, aby Państwa aplikacja wywołała ack() (potwierdziła odbiór) każdego przychodzącego polecenia ukośnikowego i payloadu interaktywnego w ciągu 3 sekund. W przeciwnym razie Slack wyświetli użytkownikowi błąd. W przypadku długotrwałych operacji należy natychmiast wywołać ack, rozpocząć przetwarzanie w wątku działającym w tle, a następnie odpowiedzieć za pomocą respond(), przekazując wynik.

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() a respond() — kiedy używać poszczególnych funkcji

Dwie funkcje publikują wiadomości w Slacku:

  • say() — publikuje wiadomość na kanale, na którym wystąpiło zdarzenie; jest ona widoczna dla wszystkich
  • respond() — dostępna tylko w modułach obsługi poleceń ukośnikowych; może publikować wiadomości ulotne, widoczne tylko dla użytkownika, który wydał polecenie

Należy użyć respond(response_type='in_channel') w przypadku odpowiedzi publicznych oraz respond(response_type='ephemeral') w przypadku odpowiedzi prywatnych.

@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')

Zdarzenia wiadomości i podtypy

Ogólne zdarzenie message jest wywoływane dla wszystkich wiadomości, w tym wiadomości botów, edycji i usunięć. Do filtrowania należy użyć pola subtype. Typowe podtypy to: bot_message, message_changed, message_deleted. Jeśli element subtype nie występuje, jest to zwykła wiadomość użytkownika.

@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}')

Nasłuchiwanie reakcji

Zdarzenie reaction_added jest wywoływane, gdy ktoś doda reakcję emoji do wiadomości. Można użyć go do uruchamiania działań agenta — na przykład dodanie reakcji 📌 może zapisać wiadomość, a reakcji ✅ oznaczyć zadanie jako ukończone.

@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]}')

Payloady działań z komponentów interaktywnych

Gdy użytkownik kliknie przycisk lub wybierze element menu, Slack wysyła payload działania. Należy obsłużyć go za pomocą @app.action('action_id'). Identyfikator działania to ciąg znaków ustawiany podczas tworzenia komponentu Block Kit. Zawsze należy natychmiast wywołać ack().

@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')

Filtrowanie zdarzeń według kanału lub użytkownika

W dużych obszarach roboczych bot może otrzymywać zdarzenia z wielu kanałów. Należy filtrować je na początku modułu obsługi, aby przetwarzać tylko istotne zdarzenia. Proszę sprawdzić wartość event['channel'] względem dozwolonej listy lub użyć event['user'], aby ignorować określonych użytkowników, takich jak inne boty.

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]}')

Obsługa odpowiedzi w wątkach

Aby odpowiedzieć w wątku zamiast na głównym kanale, należy przekazać thread_ts do say(). Użyj event.get('thread_ts', event['ts']), aby pobrać znacznik czasu wątku — thread_ts istnieje tylko wtedy, gdy wiadomość już znajduje się w wątku; w przeciwnym razie należy użyć własnego ts wiadomości, aby rozpocząć nowy wątek.

@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
    )

Szybki test: czas wywołania ack()

Sprawdź swoją wiedzę na temat obsługi zdarzeń Slack.

Podsumowanie zdarzeń i poleceń ukośnikowych

Państwa agent Slack potrafi teraz odpowiadać na dowolne interakcje użytkowników:

  • @app.event('app_mention') — obsługa wzmianek @bot; usuwanie prefiksu wzmianki za pomocą wyrażenia regularnego
  • @app.command('/cmd') — obsługa poleceń ukośnikowych; zawsze należy wywołać ack() w ciągu 3 sekund
  • say() — publiczne publikowanie na kanale; respond() — odpowiadanie na polecenie ukośnikowe (może wysyłać wiadomości ulotne)
  • @app.action('id') — obsługa kliknięć przycisków i interakcji z komponentami interaktywnymi
  • @app.event('reaction_added') — uruchamianie działania po dodaniu reakcji emoji
  • Używanie thread_ts w say() pozwala zachować odpowiedzi w wątkach
  • Wczesne filtrowanie według kanału/użytkownika pozwala uniknąć przetwarzania nieistotnych zdarzeń

Często zadawane pytania

Czy lekcja „Nasłuchiwanie zdarzeń i poleceń slash” jest bezpłatna?

Tak — pełny tekst „Nasłuchiwanie zdarzeń i poleceń slash” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.

Co nauczysz się w „Nasłuchiwanie zdarzeń i poleceń slash”?

app_mention, polecenia slash i handlery akcji w Slack Bolt. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Agents?

Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Nasłuchiwanie zdarzeń i poleceń slash”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?

Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Podstawy Slack Bolt SDK
  2. Nasłuchiwanie zdarzeń i poleceń slash
  3. Wysyłanie wiadomości i rozbudowanych bloków
  4. Tworzenie bota do powiadomień zespołowych
← Powrót do AI Agents