Créer un bot de notifications d’équipe
Messages planifiés, résumés en DM et agents d’alerte pour les canaux.
Créer un bot de notifications d’équipe est une leçon AI Agents gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Agents, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Agents comprend 4 leçons au total.
Architecture d’un bot de notifications d’équipe
Un bot de notifications d’équipe surveille des systèmes externes — déploiements, chaînes CI/CD, alertes de surveillance et suivi des erreurs — puis publie des mises à jour mises en forme dans les canaux concernés de Slack. Le schéma central est le suivant : événement externe → webhook → agent → message Slack. L’agent s’occupe de l’acheminement, de la mise en forme et de la distribution.
# Team Notification Bot Flow:
#
# External System (GitHub, PagerDuty, Sentry, etc.)
# |
# | HTTP POST (webhook)
# v
# Flask/FastAPI webhook endpoint
# |
# | Parse event
# v
# Agent: classify, format, route
# |
# | Slack API
# v
# Team channel / DM / thread
print('Webhook -> Agent -> Slack is the core notification pattern')Recevoir des webhooks externes
Les services externes envoient des événements à votre bot via des webhooks HTTP. Configurez un point de terminaison Flask qui reçoit les requêtes POST, les valide — notamment en vérifiant leur signature si le service le permet — puis transmet la charge utile à votre gestionnaire de notifications.
from flask import Flask, request, jsonify
import hmac
import hashlib
import os
flask_app = Flask(__name__)
@flask_app.route('/webhook/github', methods=['POST'])
def github_webhook():
# Verify GitHub signature
signature = request.headers.get('X-Hub-Signature-256', '')
secret = os.environ['GITHUB_WEBHOOK_SECRET'].encode()
body = request.get_data()
expected = 'sha256=' + hmac.new(secret, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected):
return jsonify({'error': 'Invalid signature'}), 403
event_type = request.headers.get('X-GitHub-Event', '')
payload = request.json
handle_github_event(event_type, payload)
return jsonify({'status': 'ok'}), 200Acheminer les événements vers le bon canal
Les différents types d’événements doivent être envoyés vers des canaux différents. Définissez une table de routage : les événements CI/CD vont dans #deployments, les erreurs dans #alerts et les revues de PR dans #engineering. Stockez les identifiants des canaux dans des variables d’environnement afin de pouvoir les configurer sans modifier le code.
import os
# Channel routing configuration
CHANNEL_MAP = {
'deploy': os.environ.get('DEPLOY_CHANNEL', 'C0DEPLOY123'),
'error': os.environ.get('ERROR_CHANNEL', 'C0ERROR456'),
'pr_review': os.environ.get('PR_CHANNEL', 'C0PR789'),
'general': os.environ.get('GENERAL_CHANNEL', 'C0GENERAL'),
}
def route_github_event(event_type, payload):
if event_type == 'push':
branch = payload.get('ref', '').replace('refs/heads/', '')
if branch in ('main', 'master'):
return CHANNEL_MAP['deploy']
return CHANNEL_MAP['general']
elif event_type == 'pull_request':
return CHANNEL_MAP['pr_review']
elif event_type == 'workflow_run':
if payload.get('workflow_run', {}).get('conclusion') == 'failure':
return CHANNEL_MAP['error']
return CHANNEL_MAP['deploy']
return CHANNEL_MAP['general']
# --- demo ---
push_payload = {'ref': 'refs/heads/main'}
pr_payload = {}
wf_payload = {'workflow_run': {'conclusion': 'failure'}}
print('push to main ->', route_github_event('push', push_payload))
print('pull_request ->', route_github_event('pull_request', pr_payload))
print('failed workflow_run ->', route_github_event('workflow_run', wf_payload))
Mettre en forme les notifications de déploiement
Les notifications de déploiement doivent indiquer ce qui a été déployé, par qui, dans quel environnement et si l’opération a réussi ou échoué. Utilisez des sections Block Kit et des blocs de contexte pour obtenir un format clair et facile à parcourir.
def build_deploy_blocks(repo, branch, commit_sha, deployer, status, env):
status_emoji = ':white_check_mark:' if status == 'success' else ':x:'
status_text = 'Success' if status == 'success' else 'Failed'
blocks = [
{
'type': 'header',
'text': {
'type': 'plain_text',
'text': f'{status_emoji} Deploy {status_text}: {repo}'
}
},
{
'type': 'section',
'fields': [
{'type': 'mrkdwn', 'text': f'*Repo:*\n{repo}'},
{'type': 'mrkdwn', 'text': f'*Environment:*\n{env}'},
{'type': 'mrkdwn', 'text': f'*Branch:*\n{branch}'},
{'type': 'mrkdwn', 'text': f'*Deployed by:*\n{deployer}'},
{'type': 'mrkdwn', 'text': f'*Commit:*\n`{commit_sha[:8]}`'}
]
}
]
return blocks
# --- demo ---
blocks = build_deploy_blocks('coddy-agents', 'main', 'a1b2c3d4e5f6', 'alice', 'success', 'production')
for b in blocks:
print(b)
Envoyer des résumés par DM aux personnes concernées
Certaines notifications sont plus pertinentes sous forme de message direct à la personne concernée que sous forme de publication dans un canal. Utilisez client.conversations_open(users=[user_id]) pour ouvrir un canal de DM, puis publiez dans l’identifiant de canal renvoyé.
def send_dm(client, user_id, text, blocks=None):
# Open DM channel with the user
dm_result = client.conversations_open(users=[user_id])
dm_channel = dm_result['channel']['id']
# Post message to the DM channel
msg = {'channel': dm_channel, 'text': text}
if blocks:
msg['blocks'] = blocks
return client.chat_postMessage(**msg)
# Example: DM a developer when their PR build fails
def notify_pr_author_of_failure(client, pr_author_slack_id, pr_title, build_url):
blocks = [
{
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': f'Your PR build failed: *{pr_title}*\n<{build_url}|View build logs>'
}
}
]
send_dm(client, pr_author_slack_id, f'Build failed: {pr_title}', blocks)
# --- demo: minimal stand-in for the Slack client ---
class _FakeClient:
def conversations_open(self, users):
return {'channel': {'id': f'D_{users[0]}'}}
def chat_postMessage(self, **kwargs):
print(f"[slack DM] to {kwargs['channel']}: {kwargs['text']}")
return {'ts': '1700000000.000200'}
notify_pr_author_of_failure(_FakeClient(), 'U_ALICE', 'Add retry logic to fetcher', 'https://ci.example.com/build/42')
Envoi planifié de messages
Utilisez APScheduler pour envoyer des rapports planifiés — résumés quotidiens, récapitulatifs hebdomadaires ou briefings du lundi matin. Planifiez les tâches à des heures précises avec des expressions de type cron. Le planificateur s’exécute dans un fil d’exécution en arrière-plan, parallèlement à votre gestionnaire d’événements.
from apscheduler.schedulers.background import BackgroundScheduler
from slack_sdk import WebClient
import os
client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
def send_daily_summary():
stats = fetch_daily_stats() # query your data source
blocks = [
{
'type': 'header',
'text': {'type': 'plain_text', 'text': 'Daily Team Summary'}
},
{
'type': 'section',
'text': {'type': 'mrkdwn',
'text': f'*PRs merged:* {stats["prs"]}\n'
f'*Deploys:* {stats["deploys"]}\n'
f'*Incidents:* {stats["incidents"]}'}
}
]
client.chat_postMessage(
channel=os.environ['DAILY_CHANNEL'],
text='Daily Team Summary',
blocks=blocks
)
scheduler = BackgroundScheduler()
scheduler.add_job(send_daily_summary, 'cron', hour=9, minute=0)
scheduler.start()
print('Scheduler started: daily summary at 9:00 AM')Agrégation des alertes — éviter la saturation due aux notifications
Envoyer un message Slack pour chaque erreur entraîne rapidement une saturation due aux notifications. Agrégez les alertes : collectez les erreurs pendant une période donnée, par exemple 5 minutes, puis publiez un seul message récapitulatif. Utilisez un compteur et videz-le selon un calendrier.
import threading
import time
from collections import defaultdict
class AlertAggregator:
def __init__(self, client, channel, flush_interval=300):
self.client = client
self.channel = channel
self.flush_interval = flush_interval
self.buffer = defaultdict(int) # error_type -> count
self.lock = threading.Lock()
self._start_flusher()
def add_alert(self, error_type):
with self.lock:
self.buffer[error_type] += 1
def _flush(self):
with self.lock:
if not self.buffer:
return
lines = [f'• {err}: {count}x' for err, count in self.buffer.items()]
self.buffer.clear()
self.client.chat_postMessage(
channel=self.channel,
text=f'Alert summary ({len(lines)} error types):\n' + '\n'.join(lines)
)
def _start_flusher(self):
def loop():
while True:
time.sleep(self.flush_interval)
self._flush()
threading.Thread(target=loop, daemon=True).start()
# --- demo (flush immediately instead of waiting flush_interval seconds) ---
class _FakeClient:
def chat_postMessage(self, **kwargs):
print(f"[slack] postMessage to {kwargs['channel']}: {kwargs['text']}")
agg = AlertAggregator(_FakeClient(), '#alerts', flush_interval=9999)
agg.add_alert('TimeoutError')
agg.add_alert('TimeoutError')
agg.add_alert('ConnectionError')
agg._flush() # normally the background thread does this every flush_interval seconds
Mettre en forme des blocs d’alerte détaillés
Les alertes doivent permettre de comprendre rapidement la gravité de la situation. Utilisez un contexte à code couleur, des émojis et des champs structurés. Ajoutez un bouton d’action renvoyant vers le guide d’intervention ou le tableau de bord des alertes afin que les personnes d’astreinte puissent agir immédiatement depuis Slack.
def build_incident_alert_blocks(service, error_rate, threshold,
runbook_url, pagerduty_url):
blocks = [
{
'type': 'header',
'text': {'type': 'plain_text', 'text': ':rotating_light: Incident Alert'}
},
{
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': (
f'*Service:* `{service}`\n'
f'*Error Rate:* {error_rate:.1f}% (threshold: {threshold}%)\n'
f'*Status:* Investigating'
)
}
},
{
'type': 'actions',
'elements': [
{
'type': 'button',
'text': {'type': 'plain_text', 'text': 'View Runbook'},
'url': runbook_url,
'action_id': 'view_runbook'
},
{
'type': 'button',
'text': {'type': 'plain_text', 'text': 'PagerDuty'},
'url': pagerduty_url,
'style': 'danger',
'action_id': 'view_pagerduty'
}
]
}
]
return blocks
# --- demo ---
blocks = build_incident_alert_blocks('checkout-api', 12.4, 5.0,
'https://runbooks.example.com/checkout-api',
'https://pagerduty.example.com/incidents/1')
for b in blocks:
print(b)
Mention de la personne d’astreinte
Les alertes critiques doivent mentionner la personne d’astreinte par son nom. Utilisez les mentions de groupes d’utilisateurs Slack (<!subteam^SUBTEAM_ID>) pour les groupes d’astreinte, ou recherchez la personne d’astreinte dans PagerDuty/OpsGenie et mentionnez-la directement avec <@USER_ID>.
import requests
import os
def get_oncall_slack_user():
# Query PagerDuty for current on-call
headers = {'Authorization': f'Token token={os.environ["PAGERDUTY_TOKEN"]}'}
r = requests.get(
'https://api.pagerduty.com/oncalls?include[]=users&limit=1',
headers=headers
)
oncalls = r.json().get('oncalls', [])
if not oncalls:
return None
email = oncalls[0]['user']['email']
return email
def send_oncall_alert(client, channel, alert_text):
oncall_email = get_oncall_slack_user()
if oncall_email:
# Look up Slack user by email
user_result = client.users_lookupByEmail(email=oncall_email)
user_id = user_result['user']['id']
mention = f'<@{user_id}>'
else:
mention = '<!channel>'
client.chat_postMessage(
channel=channel,
text=f'{mention} - CRITICAL ALERT: {alert_text}'
)Regrouper les alertes associées dans un fil
Lorsque plusieurs alertes concernent le même incident, publiez-les sous forme de réponses dans un fil au message d’alerte initial. Le canal principal reste ainsi clair, tout en conservant l’historique complet des alertes dans le fil. Enregistrez l’horodatage du message initial pour rattacher les suivis au fil.
class IncidentThread:
def __init__(self, client, channel):
self.client = client
self.channel = channel
self.active_incidents = {} # service_name -> thread_ts
def open_incident(self, service, initial_text, blocks=None):
msg = self.client.chat_postMessage(
channel=self.channel,
text=initial_text,
blocks=blocks
)
self.active_incidents[service] = msg['ts']
return msg['ts']
def update_incident(self, service, update_text):
thread_ts = self.active_incidents.get(service)
if thread_ts:
self.client.chat_postMessage(
channel=self.channel,
thread_ts=thread_ts,
text=update_text
)
else:
self.open_incident(service, f'[New] {update_text}')
def close_incident(self, service, resolution_text):
thread_ts = self.active_incidents.pop(service, None)
if thread_ts:
self.client.chat_postMessage(
channel=self.channel,
thread_ts=thread_ts,
text=f':white_check_mark: RESOLVED: {resolution_text}'
)
# --- demo: minimal stand-in for the Slack client ---
class _FakeClient:
def __init__(self):
self._counter = 0
def chat_postMessage(self, **kwargs):
self._counter += 1
ts = f'ts_{self._counter}'
print(f"[slack] {kwargs.get('text')} (thread_ts={kwargs.get('thread_ts')})")
return {'ts': ts}
thread = IncidentThread(_FakeClient(), '#incidents')
thread.open_incident('checkout-api', 'Checkout API error rate spiking')
thread.update_incident('checkout-api', 'Rolled back the last deploy')
thread.close_incident('checkout-api', 'Error rate back to normal')
Tester le bot de notifications
Testez votre bot de notifications avant son déploiement en envoyant des webhooks de test avec requests.post() et en vérifiant que les messages apparaissent dans un canal #bot-testing. Écrivez un script de test qui simule chaque type d’événement et vérifie le format de sortie.
import requests
import json
def test_webhook(webhook_url, event_type, payload):
response = requests.post(
webhook_url,
json=payload,
headers={'X-GitHub-Event': event_type, 'Content-Type': 'application/json'}
)
print(f'Webhook test {event_type}: {response.status_code}')
return response
# Test a deploy notification
test_webhook(
webhook_url='http://localhost:3000/webhook/github',
event_type='push',
payload={
'ref': 'refs/heads/main',
'pusher': {'name': 'alice'},
'repository': {'full_name': 'myorg/myapp'},
'head_commit': {'id': 'abc12345', 'message': 'Fix: auth bug'}
}
)
print('Check #bot-testing channel for the notification')Vérification rapide : messages éphémères ou messages de canal
Testez votre compréhension du routage des notifications.
Récapitulatif du robot de notifications d’équipe
Vous pouvez désormais créer un robot complet de notifications d’équipe :
- Point de terminaison de notification Web : recevoir et valider les événements provenant de systèmes externes (vérifier les signatures)
- Routage par canal : associer les types d’événements aux canaux Slack appropriés via un dictionnaire de configuration
- Alertes Block Kit : en-tête structuré, champs et boutons d’action avec des liens vers les guides d’exploitation
- Distribution par DM :
conversations_open(users=[id]), puischat_postMessagevers le canal de DM - Rapports planifiés : APScheduler avec des expressions cron pour les synthèses quotidiennes ou hebdomadaires
- Agrégation des alertes : mettre les erreurs en mémoire tampon pendant une période donnée, puis les envoyer sous forme d’une synthèse unique
- Gestion des fils de discussion : publier les alertes de suivi dans un fil pour garder le canal principal propre
Questions Fréquemment Posées
La leçon « Créer un bot de notifications d’équipe » est-elle gratuite ?
Oui — le texte complet de « Créer un bot de notifications d’équipe » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Agents, passe à CoddyKit PRO. Le cours AI Agents comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Créer un bot de notifications d’équipe » ?
Messages planifiés, résumés en DM et agents d’alerte pour les canaux. Tu pratiques AI Agents avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Agents ?
Aucune expérience préalable n'est requise. AI Agents sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Créer un bot de notifications d’équipe » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Agents ?
Oui. Chaque leçon AI Agents inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Bases du SDK Slack Bolt
- Écouter les événements et les commandes slash
- Envoyer des messages et des blocs enrichis
- Créer un bot de notifications d’équipe