Обработка событий и команд со слешем
app_mention, команды со слешем и обработчики действий в Slack Bolt.
«Обработка событий и команд со слешем» — бесплатный урок AI Agents на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
Обзор подписок на события Slack
Slack отправляет вашему приложению события, когда что-либо происходит: опубликовано сообщение, кто-то упомянул вашего бота или пользователь присоединился к каналу. Вы подписываетесь на определённые типы событий на панели управления Slack App, а затем регистрируете обработчики в Bolt с помощью декоратора @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"]}')Обработка событий app_mention
Событие app_mention срабатывает, когда кто-то вводит @YourBot в канале. В event['text'] содержится всё сообщение, включая упоминание. Удалите префикс упоминания, чтобы получить фактический запрос пользователя.
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)Доступ к данным события
Каждый обработчик событий получает словарь event, содержащий исходную полезную нагрузку события Slack. Основные поля: event['user'] (идентификатор пользователя), event['channel'] (идентификатор канала), event['text'] (содержимое сообщения), event['ts'] (временная метка или идентификатор сообщения).
@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}!')Команды с косой чертой — регистрация и обработка
Команды с косой чертой позволяют пользователям запускать действия агента из любого канала Slack. Зарегистрируйте URL команды на панели управления Slack App (в разделе Команды с косой чертой), а затем обработайте её с помощью @app.command('/command-name'). Всегда сразу вызывайте ack() — через 3 секунды Slack прекращает ожидание, если этого не сделать.
@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() — правило 3 секунд
Slack требует, чтобы ваше приложение в течение 3 секунд вызывало ack() (подтверждало получение) каждой входящей команды с косой чертой и каждой интерактивной полезной нагрузки. Если этого не сделать, Slack покажет пользователю ошибку. Для длительных операций сразу вызовите ack, запустите обработку в фоновом потоке, а затем отправьте результат с помощью respond().
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 outsay() и respond() — когда использовать каждую функцию
Две функции отправляют сообщения обратно в Slack:
say()— отправляет сообщение в канал, где произошло собыие; его видят всеrespond()— доступна только в обработчиках команд с косой чертой; может отправлять эфемерные сообщения, видимые только пользователю, вызвавшему команду
Используйте respond(response_type='in_channel') для общедоступных ответов и respond(response_type='ephemeral') для личных.
@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')События сообщений и подтипы
Общее событие message срабатывает для всех сообщений, включая сообщения ботов, изменения и удаления. Используйте поле subtype для фильтрации. Распространённые подтипы: bot_message, message_changed, message_deleted. Если subtype отсутствует, это обычное сообщение пользователя.
@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}')Отслеживание реакций
Событие reaction_added срабатывает, когда кто-то добавляет к сообщению реакцию с эмодзи. Вы можете использовать это для запуска действий агента — например, добавлять реакцию 📌, чтобы сохранить сообщение, или ✅, чтобы отметить задачу как выполненную.
@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]}')Данные действий интерактивных компонентов
Когда пользователь нажимает кнопку или выбирает пункт меню, Slack отправляет данные действия. Обработайте их с помощью @app.action('action_id'). Идентификатор действия — это строка, которую вы указали при создании компонента Block Kit. Всегда сразу вызывайте 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')Фильтрация событий по каналу или пользователю
В больших рабочих пространствах ваш бот может получать события из множества каналов. Фильтруйте их в начале обработчика, чтобы обрабатывать только относящиеся к делу события. Сравнивайте event['channel'] со списком разрешённых каналов или проверяйте event['user'], чтобы игнорировать определённых пользователей, например других ботов.
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]}')Поддержка ответов в ветках
Чтобы ответить в ветке, а не в основном канале, передайте thread_ts в say(). Используйте event.get('thread_ts', event['ts']), чтобы получить временную метку ветки: thread_ts существует только для сообщения, которое уже находится в ветке; в противном случае используйте собственную ts сообщения, чтобы начать новую ветку.
@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
)Быстрая проверка: время вызова ack()
Проверьте, насколько хорошо вы поняли обработку событий Slack.
Итоги: события и команды с косой чертой
Теперь ваш агент Slack может отвечать на любое взаимодействие пользователя:
- @app.event('app_mention') — обработка упоминаний @bot; удаление префикса упоминания с помощью регулярного выражения
- @app.command('/cmd') — обработка команд с косой чертой; всегда вызывайте
ack()в течение 3 секунд - say() — публичная отправка сообщения в канал; respond() — ответ на команду с косой чертой, который может быть эфемерным
- @app.action('id') — обработка нажатий кнопок и взаимодействий с интерактивными компонентами
- @app.event('reaction_added') — запуск обработки при реакциях с эмодзи
- Используйте
thread_tsв say(), чтобы сохранять ответы в ветках - Рано фильтруйте события по каналу и пользователю, чтобы не обрабатывать не относящиеся к делу события
Часто задаваемые вопросы
Урок «Обработка событий и команд со слешем» бесплатный?
Да — полный текст урока «Обработка событий и команд со слешем» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «Обработка событий и команд со слешем»?
app_mention, команды со слешем и обработчики действий в Slack Bolt. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Обработка событий и команд со слешем»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы Slack Bolt SDK
- Обработка событий и команд со слешем
- Отправка сообщений и содержательных блоков
- Создание бота для уведомлений команды