{"id":79879,"date":"2025-05-16T08:43:11","date_gmt":"2025-05-16T07:43:11","guid":{"rendered":"https:\/\/kinsta.com\/fr\/?p=79879&#038;preview=true&#038;preview_id=79879"},"modified":"2025-05-19T09:06:25","modified_gmt":"2025-05-19T08:06:25","slug":"ajout-interactivite-planification-surveillance-slackbots","status":"publish","type":"post","link":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/","title":{"rendered":"Mettre en \u0153uvre l&rsquo;interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress"},"content":{"rendered":"<p>Les Slackbots n&rsquo;ont pas besoin d&rsquo;attendre que vous saisissiez des commandes. Avec la bonne configuration, votre robot peut vous aider \u00e0 g\u00e9rer vos sites <a href=\"https:\/\/kinsta.com\/fr\/blog\/qu-est-ce-que-wordpress\/\">WordPress<\/a> en proposant des boutons interactifs, des menus d\u00e9roulants, des t\u00e2ches planifi\u00e9es et des alertes intelligentes, le tout au sein m\u00eame de <a href=\"https:\/\/kinsta.com\/fr\/blog\/comment-utiliser-slack\/\">Slack<\/a>.<\/p>\n<p>Dans cet article, nous allons vous montrer comment ajouter de l&rsquo;interactivit\u00e9, de l&rsquo;automatisation et de la surveillance \u00e0 votre robot Slack.<\/p>\n<div><\/div><kinsta-auto-toc heading=\"Table of Contents\" exclude=\"last\" list-style=\"arrow\" selector=\"h2\" count-number=\"-1\"><\/kinsta-auto-toc>\n<h2>Conditions pr\u00e9alables<\/h2>\n<p>Avant de commencer, assurez-vous d&rsquo;avoir :<\/p>\n<ul>\n<li>Une application Slack avec des permissions de robot et une commande slash.<\/li>\n<li>Un <a href=\"https:\/\/kinsta.com\/fr\/\">compte Kinsta<\/a> avec un acc\u00e8s \u00e0 l&rsquo;API et un site \u00e0 tester.<\/li>\n<li>Node.js et NPM install\u00e9s localement.<\/li>\n<li>Une familiarit\u00e9 de base avec JavaScript (ou au moins \u00eatre \u00e0 l&rsquo;aise pour copier et modifier du code).<\/li>\n<li>Des cl\u00e9s API pour Slack et Kinsta.<\/li>\n<\/ul>\n<h2>Pour commencer<\/h2>\n<p>Pour construire ce Slackbot, <a href=\"https:\/\/kinsta.com\/fr\/blog\/qu-est-ce-que-node-js\/\">Node.js<\/a> et le <a href=\"https:\/\/api.slack.com\/bolt\" target=\"_blank\" rel=\"noopener noreferrer\">framework Bolt<\/a> de Slack sont utilis\u00e9s pour c\u00e2bler des commandes slash qui d\u00e9clenchent des actions via l&rsquo;API de Kinsta.<\/p>\n<p>Nous ne reviendrons pas sur toutes les \u00e9tapes de la cr\u00e9ation d&rsquo;une application Slack ou de l&rsquo;obtention d&rsquo;un acc\u00e8s \u00e0 l&rsquo;API Kinsta dans ce guide, car elles ont d\u00e9j\u00e0 \u00e9t\u00e9 abord\u00e9es dans notre guide pr\u00e9c\u00e9dent, <a href=\"https:\/\/kinsta.com\/fr\/blog\/construire-slackbot-nodejs-api-kinsta-gesion-site\/\">Comment cr\u00e9er un Slackbot avec Node.js et l&rsquo;API Kinsta pour la gestion de site<\/a>.<\/p>\n<p>Si vous n&rsquo;avez pas encore vu ce dernier, lisez-le d&rsquo;abord. Il vous explique comment cr\u00e9er votre application Slack, obtenir votre jeton de robot et votre secret de signature, et obtenir votre cl\u00e9 API Kinsta.<\/p>\n<h2>Ajouter de l&rsquo;interactivit\u00e9 \u00e0 votre Slackbot<\/h2>\n<p>Les Slackbots n&rsquo;ont pas besoin de s&rsquo;appuyer uniquement sur les commandes slash. Avec des composants interactifs tels que des boutons, des menus et des fen\u00eatres modales, vous pouvez transformer votre robot en un outil beaucoup plus intuitif et convivial.<\/p>\n<p>Au lieu de saisir <code>\/clear_cache environment_id<\/code>, imaginez que vous cliquiez sur un bouton intitul\u00e9 <strong>Vider le cache<\/strong> juste apr\u00e8s avoir v\u00e9rifi\u00e9 l&rsquo;\u00e9tat d&rsquo;un site. Pour cela, vous avez besoin du <a href=\"https:\/\/www.npmjs.com\/package\/@slack\/web-api\" target=\"_blank\" rel=\"noopener noreferrer\">client API Web de Slack<\/a>. Installez-le dans votre projet \u00e0 l&rsquo;aide de la commande ci-dessous :<\/p>\n<pre><code class=\"language-bash\">npm install @slack\/web-api<\/code><\/pre>\n<p>Puis initialisez-le dans votre <code>app.js<\/code>:<\/p>\n<pre><code class=\"language-js\">const { WebClient } = require('@slack\/web-api');\nconst web = new WebClient(process.env.SLACK_BOT_TOKEN);<\/code><\/pre>\n<p>Assurez-vous que <code>SLACK_BOT_TOKEN<\/code> est d\u00e9fini dans votre fichier <code>.env<\/code>. Maintenant, am\u00e9liorons la commande <code>\/site_status<\/code> de l&rsquo;article pr\u00e9c\u00e9dent. Au lieu d&rsquo;envoyer simplement du texte, nous attachons des boutons pour des actions rapides comme <strong>Vider le cache<\/strong>, <strong>Cr\u00e9er une sauvegarde<\/strong> ou <strong>V\u00e9rifier l&rsquo;\u00e9tat d\u00e9taill\u00e9<\/strong>.<\/p>\n<p>Voici \u00e0 quoi ressemble le gestionnaire mis \u00e0 jour :<\/p>\n<pre><code class=\"language-js\">app.command('\/site_status', async ({ command, ack, say }) =&gt; {\n  await ack();\n  \n  const environmentId = command.text.trim();\n  \n  if (!environmentId) {\n    await say('Please provide an environment ID. Usage: `\/site_status [environment-id]`');\n    return;\n  }\n  \n  try {\n    \/\/ Get environment status\n    const response = await kinstaRequest(`\/sites\/environments\/${environmentId}`);\n    \n    if (response && response.site && response.site.environments && response.site.environments.length &gt; 0) {\n      const env = response.site.environments[0];\n      \n      \/\/ Format the status message\n      let statusMessage = formatSiteStatus(env);\n      \n      \/\/ Send message with interactive buttons\n      await web.chat.postMessage({\n        channel: command.channel_id,\n        text: statusMessage,\n        blocks: [\n          {\n            type: 'section',\n            text: {\n              type: 'mrkdwn',\n              text: statusMessage\n            }\n          },\n          {\n            type: 'actions',\n            elements: [\n              {\n                type: 'button',\n                text: {\n                  type: 'plain_text',\n                  text: '\ud83e\uddf9 Clear Cache',\n                  emoji: true\n                },\n                value: environmentId,\n                action_id: 'clear_cache_button'\n              },\n              {\n                type: 'button',\n                text: {\n                  type: 'plain_text',\n                  text: '\ud83d\udcca Detailed Status',\n                  emoji: true\n                },\n                value: environmentId,\n                action_id: 'detailed_status_button'\n              },\n              {\n                type: 'button',\n                text: {\n                  type: 'plain_text',\n                  text: '\ud83d\udcbe Create Backup',\n                  emoji: true\n                },\n                value: environmentId,\n                action_id: 'create_backup_button'\n              }\n            ]\n          }\n        ]\n      });\n    } else {\n      await say(`\u26a0\ufe0f No environment found with ID: `${environmentId}``);\n    }\n  } catch (error) {\n    console.error('Error checking site status:', error);\n    await say(`\u274c Error checking site status: ${error.message}`);\n  }\n});<\/code><\/pre>\n<p>Chaque clic sur un bouton d\u00e9clenche une action. Voici comment nous g\u00e9rons le bouton <strong>Vider le cache <\/strong>:<\/p>\n<pre><code class=\"language-js\">\/\/ Add action handlers for the buttons\napp.action('clear_cache_button', async ({ body, ack, respond }) =&gt; {\n  await ack();\n  \n  const environmentId = body.actions[0].value;\n  \n  await respond(`\ud83d\udd04 Clearing cache for environment `${environmentId}`...`);\n  \n  try {\n    \/\/ Call Kinsta API to clear cache\n    const response = await kinstaRequest(\n      `\/sites\/environments\/${environmentId}\/clear-cache`,\n      'POST'\n    );\n    \n    if (response && response.operation_id) {\n      await respond(`\u2705 Cache clearing operation started! Operation ID: `${response.operation_id}``);\n    } else {\n      await respond('\u26a0\ufe0f Cache clearing request was sent, but no operation ID was returned.');\n    }\n  } catch (error) {\n    console.error('Cache clearing error:', error);\n    await respond(`\u274c Error clearing cache: ${error.message}`);\n  }\n});<\/code><\/pre>\n<p>Vous pouvez suivre le m\u00eame sch\u00e9ma pour les boutons de sauvegarde et d&rsquo;\u00e9tat, en reliant simplement chacun d&rsquo;eux au point de terminaison API ou \u00e0 la logique de commande appropri\u00e9s.<\/p>\n<pre><code class=\"language-js\">\/\/ Handlers for other buttons\napp.action('detailed_status_button', async ({ body, ack, respond }) =&gt; {\n  await ack();\n  const environmentId = body.actions[0].value;\n  \/\/ Implement detailed status check similar to the \/detailed_status command\n  \/\/ ...\n});\n\napp.action('create_backup_button', async ({ body, ack, respond }) =&gt; {\n  await ack();\n  const environmentId = body.actions[0].value;\n  \/\/ Implement backup creation similar to the \/create_backup command\n  \/\/ ...\n});<\/code><\/pre>\n<h3>Utiliser une liste d\u00e9roulante pour s\u00e9lectionner un site<\/h3>\n<p>Saisir les identifiants d&rsquo;environnement n&rsquo;est pas tr\u00e8s amusant. Et attendre de chaque membre de l&rsquo;\u00e9quipe qu&rsquo;il se souvienne de quel identifiant appartient \u00e0 quel environnement ? Ce n&rsquo;est pas r\u00e9aliste.<\/p>\n<p>Rendons cela plus intuitif. Au lieu de demander aux utilisateurs de saisir <code>\/site_status [environment-id]<\/code>, nous leur donnerons un menu d\u00e9roulant Slack o\u00f9 ils pourront choisir un site dans une liste. Une fois qu&rsquo;ils en auront s\u00e9lectionn\u00e9 un, le robot affichera le statut et attachera les m\u00eames boutons d&rsquo;action rapide que nous avons mis en \u0153uvre plus t\u00f4t.<\/p>\n<p>Pour cela, nous :<\/p>\n<ul>\n<li>R\u00e9cup\u00e9rons tous les sites \u00e0 partir de l&rsquo;API Kinsta.<\/li>\n<li>R\u00e9cup\u00e9rons les environnements pour chaque site<\/li>\n<li>Construisons un menu d\u00e9roulant avec ces options<\/li>\n<li>G\u00e9rons la s\u00e9lection de l&rsquo;utilisateur et afficher le statut du site.<\/li>\n<\/ul>\n<p>Voici la commande qui affiche le menu d\u00e9roulant :<\/p>\n<pre><code class=\"language-js\">app.command('\/select_site', async ({ command, ack, say }) =&gt; {\n  await ack();\n  \n  try {\n    \/\/ Get all sites\n    const response = await kinstaRequest('\/sites');\n    \n    if (response && response.company && response.company.sites) {\n      const sites = response.company.sites;\n      \n      \/\/ Create options for each site\n      const options = [];\n      \n      for (const site of sites) {\n        \/\/ Get environments for this site\n        const envResponse = await kinstaRequest(`\/sites\/${site.id}\/environments`);\n        \n        if (envResponse && envResponse.site && envResponse.site.environments) {\n          for (const env of envResponse.site.environments) {\n            options.push({\n              text: {\n                type: 'plain_text',\n                text: `${site.name} (${env.name})`\n              },\n              value: env.id\n            });\n          }\n        }\n      }\n      \n      \/\/ Send message with dropdown\n      await web.chat.postMessage({\n        channel: command.channel_id,\n        text: 'Select a site to manage:',\n        blocks: [\n          {\n            type: 'section',\n            text: {\n              type: 'mrkdwn',\n              text: '*Select a site to manage:*'\n            },\n            accessory: {\n              type: 'static_select',\n              placeholder: {\n                type: 'plain_text',\n                text: 'Select a site'\n              },\n              options: options.slice(0, 100), \/\/ Slack has a limit of 100 options\n              action_id: 'site_selected'\n            }\n          }\n        ]\n      });\n    } else {\n      await say('\u274c Error retrieving sites. Please check your API credentials.');\n    }\n  } catch (error) {\n    console.error('Error:', error);\n    await say(`\u274c Error retrieving sites: ${error.message}`);\n  }\n});<\/code><\/pre>\n<p>Lorsqu&rsquo;un utilisateur choisit un site, nous le g\u00e9rons avec ce gestionnaire d&rsquo;action :<\/p>\n<pre><code class=\"language-js\">\/\/ Handle the site selection\napp.action('site_selected', async ({ body, ack, respond }) =&gt; {\n  await ack();\n  \n  const environmentId = body.actions[0].selected_option.value;\n  const siteName = body.actions[0].selected_option.text.text;\n  \n  \/\/ Get environment status\n  try {\n    const response = await kinstaRequest(`\/sites\/environments\/${environmentId}`);\n    \n    if (response && response.site && response.site.environments && response.site.environments.length &gt; 0) {\n      const env = response.site.environments[0];\n      \n      \/\/ Format the status message\n      let statusMessage = `*${siteName}* (ID: `${environmentId}`)nn${formatSiteStatus(env)}`;\n      \n      \/\/ Send message with interactive buttons (similar to the site_status command)\n      \/\/ ...\n    } else {\n      await respond(`\u26a0\ufe0f No environment found with ID: `${environmentId}``);\n    }\n  } catch (error) {\n    console.error('Error:', error);\n    await respond(`\u274c Error retrieving environment: ${error.message}`);\n  }\n});<\/code><\/pre>\n<p>Maintenant que notre robot peut d\u00e9clencher des actions avec un bouton et s\u00e9lectionner des sites dans une liste, assurons-nous de ne pas ex\u00e9cuter accidentellement des op\u00e9rations risqu\u00e9es.<\/p>\n<h3>Dialogues de confirmation<\/h3>\n<p>Certaines op\u00e9rations ne devraient jamais \u00eatre ex\u00e9cut\u00e9es accidentellement. Vider un cache peut sembler inoffensif, mais si vous travaillez sur un site de production, vous n&rsquo;avez probablement pas envie de le faire d&rsquo;un simple clic &#8211; surtout si vous ne faisiez que v\u00e9rifier l&rsquo;\u00e9tat du site. C&rsquo;est l\u00e0 que les modales (dialogues) de Slack entrent en jeu.<\/p>\n<p>Au lieu d&rsquo;effacer imm\u00e9diatement le cache lorsque l&rsquo;on clique sur <code>clear_cache_button<\/code>, nous affichons une fen\u00eatre modale de confirmation. Voici comment proc\u00e9der :<\/p>\n<pre><code class=\"language-js\">app.action('clear_cache_button', async ({ body, ack, context }) =&gt; {\n  await ack();\n  \n  const environmentId = body.actions[0].value;\n  \n  \/\/ Open a confirmation dialog\n  try {\n    await web.views.open({\n      trigger_id: body.trigger_id,\n      view: {\n        type: 'modal',\n        callback_id: 'clear_cache_confirmation',\n        private_metadata: environmentId,\n        title: {\n          type: 'plain_text',\n          text: 'Confirm Cache Clearing'\n        },\n        blocks: [\n          {\n            type: 'section',\n            text: {\n              type: 'mrkdwn',\n              text: `Are you sure you want to clear the cache for environment `${environmentId}`?`\n            }\n          }\n        ],\n        submit: {\n          type: 'plain_text',\n          text: 'Clear Cache'\n        },\n        close: {\n          type: 'plain_text',\n          text: 'Cancel'\n        }\n      }\n    });\n  } catch (error) {\n    console.error('Error opening confirmation dialog:', error);\n  }\n});<\/code><\/pre>\n<p>Dans le code ci-dessus, nous utilisons <code>web.views.open()<\/code> pour lancer une fen\u00eatre modale avec un titre clair, un message d&rsquo;avertissement et deux boutons &#8211; <strong>Vider le cache<\/strong> et <strong>Annuler<\/strong> &#8211; et nous stockons <code>environmentId<\/code> dans <code>private_metadata<\/code> pour l&rsquo;avoir lorsque l&rsquo;utilisateur clique sur <strong>Vider le cache<\/strong>.<\/p>\n<p>Lorsque l&rsquo;utilisateur clique sur le bouton <strong>Vider le cache<\/strong> dans la fen\u00eatre modale, Slack envoie un \u00e9v\u00e9nement <code>view_submission<\/code>. Voici comment le g\u00e9rer et proc\u00e9der \u00e0 l&rsquo;op\u00e9ration proprement dite :<\/p>\n<pre><code class=\"language-js\">\/\/ Handle the confirmation dialog submission\napp.view('clear_cache_confirmation', async ({ ack, body, view }) =&gt; {\n  await ack();\n  \n  const environmentId = view.private_metadata;\n  const userId = body.user.id;\n  \n  \/\/ Find a DM channel with the user to respond to\n  const result = await web.conversations.open({\n    users: userId\n  });\n  \n  const channel = result.channel.id;\n  \n  await web.chat.postMessage({\n    channel,\n    text: `\ud83d\udd04 Clearing cache for environment `${environmentId}`...`\n  });\n  \n  try {\n    \/\/ Call Kinsta API to clear cache\n    const response = await kinstaRequest(\n      `\/sites\/environments\/${environmentId}\/clear-cache`,\n      'POST'\n    );\n    \n    if (response && response.operation_id) {\n      await web.chat.postMessage({\n        channel,\n        text: `\u2705 Cache clearing operation started! Operation ID: `${response.operation_id}``\n      });\n    } else {\n      await web.chat.postMessage({\n        channel,\n        text: '\u26a0\ufe0f Cache clearing request was sent, but no operation ID was returned.'\n      });\n    }\n  } catch (error) {\n    console.error('Cache clearing error:', error);\n    await web.chat.postMessage({\n      channel,\n      text: `\u274c Error clearing cache: ${error.message}`\n    });\n  }\n});<\/code><\/pre>\n<p>Dans ce code, une fois que l&rsquo;utilisateur a confirm\u00e9, nous saisissons le <code>environmentId<\/code> \u00e0 partir de <code>private_metadata<\/code>, ouvrons un DM priv\u00e9 \u00e0 l&rsquo;aide de <code>web.conversations.open()<\/code> pour \u00e9viter d&rsquo;encombrer les canaux publics, ex\u00e9cutons la requ\u00eate API pour vider le cache, et suivons avec un message de r\u00e9ussite ou d&rsquo;erreur en fonction du r\u00e9sultat.<\/p>\n<h3>Dialogues de confirmation<\/h3>\n<p>Certaines commandes Slack sont instantan\u00e9es, comme vider un cache ou v\u00e9rifier un statut. Mais d&rsquo;autres ? Pas tant que \u00e7a.<\/p>\n<p>La cr\u00e9ation d&rsquo;une sauvegarde ou le d\u00e9ploiement de fichiers peut prendre plusieurs secondes, voire plusieurs minutes. Et si votre robot reste silencieux pendant tout ce temps, les utilisateurs pourraient supposer que quelque chose s&rsquo;est cass\u00e9.<\/p>\n<p>Slack ne vous donne pas de barre de progression native, mais nous pouvons en simuler une avec un peu de cr\u00e9ativit\u00e9. Voici une fonction d&rsquo;aide qui met \u00e0 jour un message avec une barre de progression visuelle \u00e0 l&rsquo;aide d&rsquo;un kit de blocs :<\/p>\n<pre><code class=\"language-js\">async function updateProgress(channel, messageTs, text, percentage) {\n  \/\/ Create a progress bar\n  const barLength = 20;\n  const filledLength = Math.round(barLength * (percentage \/ 100));\n  const bar = '\u2588'.repeat(filledLength) + '\u2591'.repeat(barLength - filledLength);\n  \n  await web.chat.update({\n    channel,\n    ts: messageTs,\n    text: `${text} [${percentage}%]`,\n    blocks: [\n      {\n        type: 'section',\n        text: {\n          type: 'mrkdwn',\n          text: `${text} [${percentage}%]n`${bar}``\n        }\n      }\n    ]\n  });\n}<\/code><\/pre>\n<p>Int\u00e9grons ceci dans une commande <code>\/create_backup<\/code>. Au lieu d&rsquo;attendre que toute l&rsquo;op\u00e9ration soit termin\u00e9e avant de r\u00e9pondre, nous prendrons des nouvelles de l&rsquo;utilisateur \u00e0 chaque \u00e9tape.<\/p>\n<pre><code class=\"language-js\">app.command('\/create_backup', async ({ command, ack, say }) =&gt; {\n  await ack();\n  \n  const args = command.text.split(' ');\n  const environmentId = args[0];\n  const tag = args.length &gt; 1 ? args.slice(1).join(' ') : `Manual backup ${new Date().toISOString()}`;\n  \n  if (!environmentId) {\n    await say('Please provide an environment ID. Usage: `\/create_backup [environment-id] [optional-tag]`');\n    return;\n  }\n  \n  \/\/ Post initial message and get its timestamp for updates\n  const initial = await say('\ud83d\udd04 Initiating backup...');\n  const messageTs = initial.ts;\n  \n  try {\n    \/\/ Update progress to 10%\n    await updateProgress(command.channel_id, messageTs, '\ud83d\udd04 Creating backup...', 10);\n    \n    \/\/ Call Kinsta API to create a backup\n    const response = await kinstaRequest(\n      `\/sites\/environments\/${environmentId}\/manual-backups`,\n      'POST',\n      { tag }\n    );\n    \n    if (response && response.operation_id) {\n      await updateProgress(command.channel_id, messageTs, '\ud83d\udd04 Backup in progress...', 30);\n      \n      \/\/ Poll the operation status\n      let completed = false;\n      let percentage = 30;\n      \n      while (!completed && percentage  setTimeout(resolve, 3000));\n        \n        \/\/ Check operation status\n        const statusResponse = await kinstaRequest(`\/operations\/${response.operation_id}`);\n        \n        if (statusResponse && statusResponse.operation) {\n          const operation = statusResponse.operation;\n          \n          if (operation.status === 'completed') {\n            completed = true;\n            percentage = 100;\n          } else if (operation.status === 'failed') {\n            await web.chat.update({\n              channel: command.channel_id,\n              ts: messageTs,\n              text: `\u274c Backup failed! Error: ${operation.error || 'Unknown error'}`\n            });\n            return;\n          } else {\n            \/\/ Increment progress\n            percentage += 10;\n            if (percentage &gt; 95) percentage = 95;\n            \n            await updateProgress(\n              command.channel_id, \n              messageTs, \n              '\ud83d\udd04 Backup in progress...', \n              percentage\n            );\n          }\n        }\n      }\n      \n      \/\/ Final update\n      await web.chat.update({\n        channel: command.channel_id,\n        ts: messageTs,\n        text: `\u2705 Backup completed successfully!`,\n        blocks: [\n          {\n            type: 'section',\n            text: {\n              type: 'mrkdwn',\n              text: `\u2705 Backup completed successfully!n*Tag:* ${tag}n*Operation ID:* `${response.operation_id}``\n            }\n          }\n        ]\n      });\n    } else {\n      await web.chat.update({\n        channel: command.channel_id,\n        ts: messageTs,\n        text: '\u26a0\ufe0f Backup request was sent, but no operation ID was returned.'\n      });\n    }\n  } catch (error) {\n    console.error('Backup creation error:', error);\n    \n    await web.chat.update({\n      channel: command.channel_id,\n      ts: messageTs,\n      text: `\u274c Error creating backup: ${error.message}`\n    });\n  }\n});<\/code><\/pre>\n<h3>Notifications de succ\u00e8s\/\u00e9chec<\/h3>\n<p>\u00c0 l&rsquo;heure actuelle, votre robot renvoie probablement du texte brut comme <strong>\u2705 Succ\u00e8s<\/strong> ou <strong>\u274c \u00c9chec<\/strong>. Cela fonctionne, mais c&rsquo;est fade, et cela n&rsquo;aide pas les utilisateurs \u00e0 comprendre <em>pourquoi<\/em> quelque chose a r\u00e9ussi ou ce qu&rsquo;ils doivent faire en cas d&rsquo;\u00e9chec.<\/p>\n<p>R\u00e9parons cela avec une mise en forme appropri\u00e9e pour les messages de r\u00e9ussite et d&rsquo;erreur aux c\u00f4t\u00e9s d&rsquo;un contexte utile, de suggestions et d&rsquo;une mise en forme propre.<\/p>\n<p>Ajoutez ces utilitaires \u00e0 ton site <code>utils.js<\/code> pour pouvoir les r\u00e9utiliser dans toutes les commandes :<\/p>\n<pre><code class=\"language-js\">function formatSuccessMessage(title, details = []) {\n  let message = `\u2705 *${title}*nn`;\n  \n  if (details.length &gt; 0) {\n    details.forEach(detail =&gt; {\n      message += `\u2022 ${detail.label}: ${detail.value}n`;\n    });\n  }\n  \n  return message;\n}\n\nfunction formatErrorMessage(title, error, suggestions = []) {\n  let message = `\u274c *${title}*nn`;\n  message += `*Error:* ${error}nn`;\n  \n  if (suggestions.length &gt; 0) {\n    message += '*Suggestions:*n';\n    suggestions.forEach(suggestion =&gt; {\n      message += `\u2022 ${suggestion}n`;\n    });\n  }\n  \n  return message;\n}\n\nmodule.exports = {\n  connectToSite,\n  logCommand,\n  formatSuccessMessage,\n  formatErrorMessage\n};<\/code><\/pre>\n<p>Ces fonctions prennent les entr\u00e9es structur\u00e9es et les transforment en markdown adapt\u00e9 \u00e0 Slack avec des emoji, des \u00e9tiquettes et des sauts de ligne. C&rsquo;est beaucoup plus facile \u00e0 scanner au milieu d&rsquo;un fil de discussion Slack tr\u00e8s anim\u00e9. Voici \u00e0 quoi cela ressemble \u00e0 l&rsquo;int\u00e9rieur d&rsquo;un v\u00e9ritable gestionnaire de commandes. Prenons l&rsquo;exemple de <code>\/clear_cache<\/code>:<\/p>\n<pre><code class=\"language-js\">app.command('\/clear_cache', async ({ command, ack, say }) =&gt; {\n  await ack();\n  \n  const environmentId = command.text.trim();\n  \n  if (!environmentId) {\n    await say('Please provide an environment ID. Usage: `\/clear_cache [environment-id]`');\n    return;\n  }\n  \n  try {\n    await say('\ud83d\udd04 Processing...');\n    \n    \/\/ Call Kinsta API to clear cache\n    const response = await kinstaRequest(\n      `\/sites\/environments\/${environmentId}\/clear-cache`,\n      'POST'\n    );\n    \n    if (response && response.operation_id) {\n      const { formatSuccessMessage } = require('.\/utils');\n      \n      await say(formatSuccessMessage('Cache Clearing Started', [\n        { label: 'Environment ID', value: ``${environmentId}`` },\n        { label: 'Operation ID', value: ``${response.operation_id}`` },\n        { label: 'Status', value: 'In Progress' }\n      ]));\n    } else {\n      const { formatErrorMessage } = require('.\/utils');\n      \n      await say(formatErrorMessage(\n        'Cache Clearing Error',\n        'No operation ID returned',\n        [\n          'Check your environment ID',\n          'Verify your API credentials',\n          'Try again later'\n        ]\n      ));\n    }\n  } catch (error) {\n    console.error('Cache clearing error:', error);\n    \n    const { formatErrorMessage } = require('.\/utils');\n    \n    await say(formatErrorMessage(\n      'Cache Clearing Error',\n      error.message,\n      [\n        'Check your environment ID',\n        'Verify your API credentials',\n        'Try again later'\n      ]\n    ));\n  }\n});<\/code><\/pre>\n<h2>Automatiser les t\u00e2ches WordPress avec des t\u00e2ches planifi\u00e9es<\/h2>\n<p>Jusqu&rsquo;\u00e0 pr\u00e9sent, tout ce que fait votre Slackbot se produit lorsque quelqu&rsquo;un d\u00e9clenche explicitement une commande. Mais tout ne doit pas d\u00e9pendre du fait que quelqu&rsquo;un se souvienne de l&rsquo;ex\u00e9cuter.<\/p>\n<p>Et si votre Slackbot pouvait automatiquement sauvegarder vos sites tous les soirs ? Ou v\u00e9rifier si un site est en panne tous les matins avant que l&rsquo;\u00e9quipe ne se r\u00e9veille.<\/p>\n<p>Nous allons utiliser la biblioth\u00e8que <a href=\"https:\/\/www.npmjs.com\/package\/node-schedule\" target=\"_blank\" rel=\"noopener noreferrer\">node-schedule<\/a> pour ex\u00e9cuter des t\u00e2ches bas\u00e9es sur des expressions cron. Tout d&rsquo;abord, installez-la :<\/p>\n<pre><code class=\"language-bash\">npm install node-schedule<\/code><\/pre>\n<p>Maintenant, installez-la en haut de votre <code>app.js<\/code>:<\/p>\n<pre><code class=\"language-js\">const schedule = require('node-schedule');<\/code><\/pre>\n<p>Nous aurons \u00e9galement besoin d&rsquo;un moyen de suivre les travaux planifi\u00e9s actifs afin que les utilisateurs puissent les lister ou les annuler plus tard :<\/p>\n<pre><code class=\"language-js\">const scheduledJobs = {};<\/code><\/pre>\n<h3>Cr\u00e9ation de la commande schedule task<\/h3>\n<p>Nous commencerons par une commande de base <code>\/schedule_task<\/code> qui accepte un type de t\u00e2che (<code>backup<\/code>, <code>clear_cache<\/code>, ou <code>status_check<\/code>), l&rsquo;identifiant de l&rsquo;environnement et une expression cron.<\/p>\n<pre><code class=\"language-bash\">\/schedule_task backup 12345 0 0 * * *<\/code><\/pre>\n<p>Cette commande planifierait une sauvegarde quotidienne \u00e0 minuit. Voici le gestionnaire de commande complet :<\/p>\n<pre><code class=\"language-js\">app.command('\/schedule_task', async ({ command, ack, say }) =&gt; {\n  await ack();\n\n  const args = command.text.split(' ');\n  if (args.length  {\n      console.log(`Running scheduled ${taskType} for environment ${environmentId}`);\n\n      try {\n        switch (taskType) {\n          case 'backup':\n            await kinstaRequest(`\/sites\/environments\/${environmentId}\/manual-backups`, 'POST', {\n              tag: `Scheduled backup ${new Date().toISOString()}`\n            });\n            break;\n          case 'clear_cache':\n            await kinstaRequest(`\/sites\/environments\/${environmentId}\/clear-cache`, 'POST');\n            break;\n          case 'status_check':\n            const response = await kinstaRequest(`\/sites\/environments\/${environmentId}`);\n            const env = response?.site?.environments?.[0];\n            if (env) {\n              console.log(`Status: ${env.display_name} is ${env.is_blocked ? 'blocked' : 'running'}`);\n            }\n            break;\n        }\n      } catch (err) {\n        console.error(`Scheduled ${taskType} failed for ${environmentId}:`, err.message);\n      }\n    });\n\n    scheduledJobs[jobId] = {\n      job,\n      taskType,\n      environmentId,\n      cronSchedule,\n      userId: command.user_id,\n      createdAt: new Date().toISOString()\n    };\n\n    await say(`\u2705 Scheduled task created!\n*Task:* ${taskType}\n*Environment:* `${environmentId}`\n*Cron:* `${cronSchedule}`\n*Job ID:* `${jobId}`\n\nTo cancel this task, run `\/cancel_task ${jobId}``);\n  } catch (err) {\n    console.error('Error creating scheduled job:', err);\n    await say(`\u274c Failed to create scheduled task: ${err.message}`);\n  }\n});<\/code><\/pre>\n<h3>Annulation des t\u00e2ches planifi\u00e9es<\/h3>\n<p>Si quelque chose change ou si la t\u00e2che n&rsquo;est plus n\u00e9cessaire, les utilisateurs peuvent l&rsquo;annuler avec :<\/p>\n<pre><code class=\"language-bash\">\/cancel_task<\/code><\/pre>\n<p>Voici la mise en \u0153uvre :<\/p>\n<pre><code class=\"language-js\">app.command('\/cancel_task', async ({ command, ack, say }) =&gt; {\n  await ack();\n\n  const jobId = command.text.trim();\n\n  if (!scheduledJobs[jobId]) {\n    await say(`\u26a0\ufe0f No task found with ID: `${jobId}``);\n    return;\n  }\n\n  scheduledJobs[jobId].job.cancel();\n  delete scheduledJobs[jobId];\n\n  await say(`\u2705 Task `${jobId}` has been cancelled.`);\n});<\/code><\/pre>\n<h3>Lister toutes les t\u00e2ches planifi\u00e9es<\/h3>\n<p>Permettons \u00e9galement aux utilisateurs d&rsquo;afficher toutes les t\u00e2ches qui ont \u00e9t\u00e9 planifi\u00e9es :<\/p>\n<pre><code class=\"language-js\">app.command('\/list_tasks', async ({ command, ack, say }) =&gt; {\n  await ack();\n\n  const tasks = Object.entries(scheduledJobs);\n  if (tasks.length === 0) {\n    await say('No scheduled tasks found.');\n    return;\n  }\n\n  let message = '*Scheduled Tasks:*nn';\n\n  for (const [jobId, job] of tasks) {\n    message += `\u2022 *Job ID:* `${jobId}`n`;\n    message += `  - Task: ${job.taskType}n`;\n    message += `  - Environment: `${job.environmentId}`n`;\n    message += `  - Cron: `${job.cronSchedule}`n`;\n    message += `  - Created by: nn`;\n  }\n\n  message += '_Use `\/cancel_task [job_id]` to cancel a task._';\n  await say(message);\n});<\/code><\/pre>\n<p>Cela donne \u00e0 votre Slackbot un tout nouveau niveau d&rsquo;autonomie. Les sauvegardes, les vidages de cache et les v\u00e9rifications d&rsquo;\u00e9tat n&rsquo;ont plus besoin d&rsquo;\u00eatre le travail de quelqu&rsquo;un. Elles se font tranquillement, de mani\u00e8re fiable et \u00e0 l&rsquo;heure pr\u00e9vue.<\/p>\n<h2>Maintenance r\u00e9currente<\/h2>\n<p>Parfois, vous voulez ex\u00e9cuter un groupe de t\u00e2ches de maintenance \u00e0 intervalles r\u00e9guliers, comme les sauvegardes hebdomadaires et les vidages de cache le dimanche soir. C&rsquo;est l\u00e0 qu&rsquo;interviennent les fen\u00eatres de maintenance.<\/p>\n<p>Une fen\u00eatre de maintenance est un bloc de temps programm\u00e9 pendant lequel le robot ex\u00e9cute automatiquement des t\u00e2ches pr\u00e9d\u00e9finies telles que :<\/p>\n<ul>\n<li>La cr\u00e9ation d&rsquo;une sauvegarde<\/li>\n<li>Vider le cache<\/li>\n<li>L&rsquo;envoi de notifications de d\u00e9marrage et d&rsquo;ach\u00e8vement<\/li>\n<\/ul>\n<p>Le format est simple :<\/p>\n<pre><code class=\"language-bash\">\/maintenance_window [environment_id] [day_of_week] [hour] [duration_hours]<\/code><\/pre>\n<p>Par exemple :<\/p>\n<pre><code class=\"language-bash\">\/maintenance_window 12345 Sunday 2 3<\/code><\/pre>\n<p>Cela signifie que tous les dimanches \u00e0 2 heures du matin, les t\u00e2ches de maintenance sont ex\u00e9cut\u00e9es pendant 3 heures. Voici la mise en \u0153uvre compl\u00e8te :<\/p>\n<pre><code class=\"language-js\">\/\/ Add a command to create a maintenance window\napp.command('\/maintenance_window', async ({ command, ack, say }) =&gt; {\n  await ack();\n  \n  \/\/ Expected format: environment_id day_of_week hour duration\n  \/\/ Example: \/maintenance_window 12345 Sunday 2 3\n  const args = command.text.split(' ');\n  \n  if (args.length &lt; 4) {\n    await say('Please provide all required parameters. Usage: `\/maintenance_window [environment_id] [day_of_week] [hour] [duration_hours]`');\n    return;\n  }\n  \n  const [environmentId, dayOfWeek, hour, duration] = args;\n  \n  \/\/ Validate inputs\n  const validDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\n  if (!validDays.includes(dayOfWeek)) {\n    await say(`Invalid day of week. Please choose from: ${validDays.join(', ')}`);\n    return;\n  }\n  \n  const hourInt = parseInt(hour, 10);\n  if (isNaN(hourInt) || hourInt  23) {\n    await say('Hour must be a number between 0 and 23.');\n    return;\n  }\n  \n  const durationInt = parseInt(duration, 10);\n  if (isNaN(durationInt) || durationInt  12) {\n    await say('Duration must be a number between 1 and 12 hours.');\n    return;\n  }\n  \n  \/\/ Convert day of week to cron format\n  const dayMap = {\n    'Sunday': 0,\n    'Monday': 1,\n    'Tuesday': 2,\n    'Wednesday': 3,\n    'Thursday': 4,\n    'Friday': 5,\n    'Saturday': 6\n  };\n  \n  const cronDay = dayMap[dayOfWeek];\n  \n  \/\/ Create cron schedule for the start of the maintenance window\n  const cronSchedule = `0 ${hourInt} * * ${cronDay}`;\n  \n  \/\/ Generate a unique job ID\n  const jobId = `maintenance_${environmentId}_${Date.now()}`;\n  \n  \/\/ Schedule the job\n  try {\n    const job = schedule.scheduleJob(cronSchedule, async function() {\n      \/\/ Start of maintenance window\n      await web.chat.postMessage({\n        channel: command.channel_id,\n        text: `\ud83d\udd27 *Maintenance Window Started*n*Environment:* `${environmentId}`n*Duration:* ${durationInt} hoursnnAutomatic maintenance tasks are now running.`\n      });\n      \n      \/\/ Perform maintenance tasks\n      try {\n        \/\/ 1. Create a backup\n        const backupResponse = await kinstaRequest(\n          `\/sites\/environments\/${environmentId}\/manual-backups`,\n          'POST',\n          { tag: `Maintenance backup ${new Date().toISOString()}` }\n        );\n        \n        if (backupResponse && backupResponse.operation_id) {\n          await web.chat.postMessage({\n            channel: command.channel_id,\n            text: `\u2705 Maintenance backup created. Operation ID: `${backupResponse.operation_id}``\n          });\n        }\n        \n        \/\/ 2. Clear cache\n        const cacheResponse = await kinstaRequest(\n          `\/sites\/environments\/${environmentId}\/clear-cache`,\n          'POST'\n        );\n        \n        if (cacheResponse && cacheResponse.operation_id) {\n          await web.chat.postMessage({\n            channel: command.channel_id,\n            text: `\u2705 Cache cleared. Operation ID: `${cacheResponse.operation_id}``\n          });\n        }\n        \n        \/\/ 3. Schedule end of maintenance window notification\n        setTimeout(async () =&gt; {\n          await web.chat.postMessage({\n            channel: command.channel_id,\n            text: `\u2705 *Maintenance Window Completed*n*Environment:* `${environmentId}`nnAll maintenance tasks have been completed.`\n          });\n        }, durationInt * 60 * 60 * 1000); \/\/ Convert hours to milliseconds\n      } catch (error) {\n        console.error('Maintenance tasks error:', error);\n        await web.chat.postMessage({\n          channel: command.channel_id,\n          text: `\u274c Error during maintenance: ${error.message}`\n        });\n      }\n    });\n    \n    \/\/ Store the job for later cancellation\n    scheduledJobs[jobId] = {\n      job,\n      taskType: 'maintenance',\n      environmentId,\n      cronSchedule,\n      dayOfWeek,\n      hour: hourInt,\n      duration: durationInt,\n      userId: command.user_id,\n      createdAt: new Date().toISOString()\n    };\n    \n    await say(`\u2705 Maintenance window scheduled!\n*Environment:* `${environmentId}`\n*Schedule:* Every ${dayOfWeek} at ${hourInt}:00 for ${durationInt} hours\n*Job ID:* `${jobId}`\n\nTo cancel this maintenance window, use `\/cancel_task ${jobId}``);\n  } catch (error) {\n    console.error('Error scheduling maintenance window:', error);\n    await say(`\u274c Error scheduling maintenance window: ${error.message}`);\n  }\n});<\/code><\/pre>\n<h3>Rapports automatis\u00e9s<\/h3>\n<p>Vous ne voulez pas vous r\u00e9veiller tous les lundis en vous demandant si votre site WordPress a \u00e9t\u00e9 sauvegard\u00e9 ou s&rsquo;il est en panne depuis des heures. Gr\u00e2ce aux rapports automatis\u00e9s, votre robot Slack peut vous donner, ainsi qu&rsquo;\u00e0 votre \u00e9quipe, un r\u00e9sum\u00e9 rapide des performances selon un calendrier \u00e9tabli.<\/p>\n<p>Ce type de rapport est id\u00e9al pour garder un \u0153il sur des choses comme :<\/p>\n<ul>\n<li>L&rsquo;\u00e9tat actuel du site<\/li>\n<li>L&rsquo;activit\u00e9 de sauvegarde au cours des 7 derniers jours<\/li>\n<li>La version de PHP et le domaine principal<\/li>\n<li>Les signaux d&rsquo;alarme, comme les environnements bloqu\u00e9s ou les sauvegardes manquantes.<\/li>\n<\/ul>\n<p>Construisons une commande <code>\/schedule_report<\/code> qui automatise tout cela.<\/p>\n<pre><code class=\"language-js\">\/\/ Add a command to schedule weekly reporting\napp.command('\/schedule_report', async ({ command, ack, say }) =&gt; {\n  await ack();\n  \n  \/\/ Expected format: environment_id day_of_week hour\n  \/\/ Example: \/schedule_report 12345 Monday 9\n  const args = command.text.split(' ');\n  \n  if (args.length &lt; 3) {\n    await say('Please provide all required parameters. Usage: `\/schedule_report [environment_id] [day_of_week] [hour]`');\n    return;\n  }\n  \n  const [environmentId, dayOfWeek, hour] = args;\n  \n  \/\/ Validate inputs\n  const validDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\n  if (!validDays.includes(dayOfWeek)) {\n    await say(`Invalid day of week. Please choose from: ${validDays.join(', ')}`);\n    return;\n  }\n  \n  const hourInt = parseInt(hour, 10);\n  if (isNaN(hourInt) || hourInt  23) {\n    await say('Hour must be a number between 0 and 23.');\n    return;\n  }\n  \n  \/\/ Convert day of week to cron format\n  const dayMap = {\n    'Sunday': 0,\n    'Monday': 1,\n    'Tuesday': 2,\n    'Wednesday': 3,\n    'Thursday': 4,\n    'Friday': 5,\n    'Saturday': 6\n  };\n  \n  const cronDay = dayMap[dayOfWeek];\n  \n  \/\/ Create cron schedule for the report\n  const cronSchedule = `0 ${hourInt} * * ${cronDay}`;\n  \n  \/\/ Generate a unique job ID\n  const jobId = `report_${environmentId}_${Date.now()}`;\n  \n  \/\/ Schedule the job\n  try {\n    const job = schedule.scheduleJob(cronSchedule, async function() {\n      \/\/ Generate and send the report\n      await generateWeeklyReport(environmentId, command.channel_id);\n    });\n    \n    \/\/ Store the job for later cancellation\n    scheduledJobs[jobId] = {\n      job,\n      taskType: 'report',\n      environmentId,\n      cronSchedule,\n      dayOfWeek,\n      hour: hourInt,\n      userId: command.user_id,\n      createdAt: new Date().toISOString()\n    };\n    \n    await say(`\u2705 Weekly report scheduled!\n*Environment:* `${environmentId}`\n*Schedule:* Every ${dayOfWeek} at ${hourInt}:00\n*Job ID:* `${jobId}`\n\nTo cancel this report, use `\/cancel_task ${jobId}``);\n  } catch (error) {\n    console.error('Error scheduling report:', error);\n    await say(`\u274c Error scheduling report: ${error.message}`);\n  }\n});\n\n\/\/ Function to generate weekly report\nasync function generateWeeklyReport(environmentId, channelId) {\n  try {\n    \/\/ Get environment details\n    const response = await kinstaRequest(`\/sites\/environments\/${environmentId}`);\n    \n    if (!response || !response.site || !response.site.environments || !response.site.environments.length) {\n      await web.chat.postMessage({\n        channel: channelId,\n        text: `\u26a0\ufe0f Weekly Report Error: No environment found with ID: `${environmentId}``\n      });\n      return;\n    }\n    \n    const env = response.site.environments[0];\n    \n    \/\/ Get backups for the past week\n    const backupsResponse = await kinstaRequest(`\/sites\/environments\/${environmentId}\/backups`);\n    \n    let backupsCount = 0;\n    let latestBackup = null;\n    \n    if (backupsResponse && backupsResponse.environment && backupsResponse.environment.backups) {\n      const oneWeekAgo = new Date();\n      oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);\n      \n      const recentBackups = backupsResponse.environment.backups.filter(backup =&gt; {\n        const backupDate = new Date(backup.created_at);\n        return backupDate &gt;= oneWeekAgo;\n      });\n      \n      backupsCount = recentBackups.length;\n      \n      if (recentBackups.length &gt; 0) {\n        latestBackup = recentBackups.sort((a, b) =&gt; b.created_at - a.created_at)[0];\n      }\n    }\n    \n    \/\/ Get environment status\n    const statusEmoji = env.is_blocked ? '\ud83d\udd34' : '\ud83d\udfe2';\n    const statusText = env.is_blocked ? 'Blocked' : 'Running';\n    \n    \/\/ Create report message\n    const reportDate = new Date().toLocaleDateString('en-US', {\n      weekday: 'long',\n      year: 'numeric',\n      month: 'long',\n      day: 'numeric'\n    });\n    \n    const reportMessage = `\ud83d\udcca *Weekly Report - ${reportDate}*\n*Site:* ${env.display_name}\n*Environment ID:* `${environmentId}`\n\n*Status Summary:*\n\u2022 Current Status: ${statusEmoji} ${statusText}\n\u2022 PHP Version: ${env.container_info?.php_engine_version || 'Unknown'}\n\u2022 Primary Domain: ${env.primaryDomain?.name || env.domains?.[0]?.name || 'N\/A'}\n\n*Backup Summary:*\n\u2022 Total Backups (Last 7 Days): ${backupsCount}\n\u2022 Latest Backup: ${latestBackup ? new Date(latestBackup.created_at).toLocaleString() : 'N\/A'}\n\u2022 Latest Backup Type: ${latestBackup ? latestBackup.type : 'N\/A'}\n\n*Recommendations:*\n\u2022 ${backupsCount === 0 ? '\u26a0\ufe0f No recent backups found. Consider creating a manual backup.' : '\u2705 Regular backups are being created.'}\n\u2022 ${env.is_blocked ? '\u26a0\ufe0f Site is currently blocked. Check for issues.' : '\u2705 Site is running normally.'}\n\n_This is an automated report. For detailed information, use the `\/site_status ${environmentId}` command._`;\n    \n    await web.chat.postMessage({\n      channel: channelId,\n      text: reportMessage\n    });\n  } catch (error) {\n    console.error('Report generation error:', error);\n    await web.chat.postMessage({\n      channel: channelId,\n      text: `\u274c Error generating weekly report: ${error.message}`\n    });\n  }\n}<\/code><\/pre>\n<h2>Gestion des erreurs et surveillance<\/h2>\n<p>Une fois que votre robot commence \u00e0 effectuer de vraies op\u00e9rations comme la modification d&rsquo;environnements ou le d\u00e9clenchement de t\u00e2ches programm\u00e9es, vous avez besoin de plus que <code>console.log()<\/code> pour suivre ce qui se passe en coulisses.<\/p>\n<p>D\u00e9composons cela en couches propres et faciles \u00e0 maintenir :<\/p>\n<h3>Journalisation structur\u00e9e avec Winston<\/h3>\n<p>Au lieu d&rsquo;imprimer les journaux sur la console, utilise <code><a href=\"https:\/\/www.npmjs.com\/package\/winston\" target=\"_blank\" rel=\"noopener noreferrer\">winston<\/a><\/code> pour envoyer des journaux structur\u00e9s vers des fichiers, et \u00e9ventuellement vers des services comme <a href=\"https:\/\/www.loggly.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Loggly<\/a> ou <a href=\"https:\/\/www.datadoghq.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Datadog<\/a>. Installez-le avec la commande ci-dessous :<\/p>\n<pre><code class=\"language-bash\">npm install winston<\/code><\/pre>\n<p>Ensuite, installez <code>logger.js<\/code>:<\/p>\n<pre><code class=\"language-js\">const winston = require('winston');\nconst fs = require('fs');\nconst path = require('path');\n\nconst logsDir = path.join(__dirname, '..\/logs');\nif (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir);\n\nconst logger = winston.createLogger({\n  level: 'info',\n  format: winston.format.combine(\n    winston.format.timestamp(),\n    winston.format.json()\n  ),\n  defaultMeta: { service: 'wordpress-slack-bot' },\n  transports: [\n    new winston.transports.Console({ format: winston.format.simple() }),\n    new winston.transports.File({ filename: path.join(logsDir, 'error.log'), level: 'error' }),\n    new winston.transports.File({ filename: path.join(logsDir, 'combined.log') })\n  ]\n});\n\nmodule.exports = logger;<\/code><\/pre>\n<p>Ensuite, dans votre <code>app.js<\/code>, remplacez tous les appels <code>console.log<\/code> ou <code>console.error<\/code> par :<\/p>\n<pre><code class=\"language-js\">const logger = require('.\/logger');\n\nlogger.info('Cache clear initiated', { userId: command.user_id });\nlogger.error('API failure', { error: err.message });<\/code><\/pre>\n<h3>Envoyer des alertes aux administrateurs via Slack<\/h3>\n<p>Vous avez d\u00e9j\u00e0 la variable env <code>ADMIN_USERS<\/code>, utilisez-la pour avertir votre \u00e9quipe directement dans Slack lorsque quelque chose de critique \u00e9choue :<\/p>\n<pre><code class=\"language-js\">async function alertAdmins(message, metadata = {}) {\n  for (const userId of ADMIN_USERS) {\n    const dm = await web.conversations.open({ users: userId });\n    const channel = dm.channel.id;\n\n    let alert = `\ud83d\udea8 *${message}*n`;\n    for (const [key, value] of Object.entries(metadata)) {\n      alert += `\u2022 *${key}:* ${value}n`;\n    }\n\n    await web.chat.postMessage({ channel, text: alert });\n  }\n}<\/code><\/pre>\n<p>Utilisez-la comme ceci :<\/p>\n<pre><code class=\"language-js\">await alertAdmins('Backup Failed', {\n  environmentId,\n  error: error.message,\n  user: ``\n});<\/code><\/pre>\n<h3>Suivre les performances avec des mesures de base<\/h3>\n<p>Ne vous lancez pas dans un <a href=\"https:\/\/prometheus.io\/docs\/guides\/query-log\/\" target=\"_blank\" rel=\"noopener noreferrer\">Prometheus<\/a> complet si vous essayez juste de voir si votre robot est en bonne sant\u00e9. Gardez un objet de performance l\u00e9ger :<\/p>\n<pre><code class=\"language-js\">const metrics = {\n  apiCalls: 0,\n  errors: 0,\n  commands: 0,\n  totalTime: 0,\n  get avgResponseTime() {\n    return this.apiCalls === 0 ? 0 : this.totalTime \/ this.apiCalls;\n  }\n};<\/code><\/pre>\n<p>Mettez-le \u00e0 jour dans votre helper <code>kinstaRequest()<\/code>:<\/p>\n<pre><code class=\"language-js\">const start = Date.now();\ntry {\n  metrics.apiCalls++;\n  const res = await fetch(...);\n  return await res.json();\n} catch (err) {\n  metrics.errors++;\n  throw err;\n} finally {\n  metrics.totalTime += Date.now() - start;\n}<\/code><\/pre>\n<p>Exposez-le via une commande comme <code>\/bot_performance<\/code>:<\/p>\n<pre><code class=\"language-js\">app.command('\/bot_performance', async ({ command, ack, say }) =&gt; {\n  await ack();\n\n  if (!ADMIN_USERS.includes(command.user_id)) {\n    return await say('\u26d4 Not authorized.');\n  }\n\n  const msg = `\ud83d\udcca *Bot Metrics*\n\u2022 API Calls: ${metrics.apiCalls}\n\u2022 Errors: ${metrics.errors}\n\u2022 Avg Response Time: ${metrics.avgResponseTime.toFixed(2)}ms\n\u2022 Commands Run: ${metrics.commands}`;\n\n  await say(msg);\n});<\/code><\/pre>\n<h3>Facultatif : D\u00e9finir les \u00e9tapes de r\u00e9cup\u00e9ration<\/h3>\n<p>Si vous voulez mettre en place une logique de r\u00e9cup\u00e9ration (comme r\u00e9essayer de vider le cache via <a href=\"https:\/\/kinsta.com\/fr\/blog\/comment-utiliser-ssh\/\">SSH<\/a>), il vous suffit de cr\u00e9er un helper comme :<\/p>\n<pre><code class=\"language-js\">async function attemptRecovery(environmentId, issue) {\n  logger.warn('Attempting recovery', { environmentId, issue });\n\n  if (issue === 'cache_clear_failure') {\n    \/\/ fallback logic here\n  }\n\n  \/\/ Return a recovery status object\n  return { success: true, message: 'Fallback ran.' };\n}<\/code><\/pre>\n<p>Tenez-le \u00e0 l&rsquo;\u00e9cart de votre logique de commande principale \u00e0 moins qu&rsquo;il ne s&rsquo;agisse d&rsquo;un chemin critique. Dans de nombreux cas, il est pr\u00e9f\u00e9rable d&rsquo;enregistrer l&rsquo;erreur, d&rsquo;alerter les administrateurs et de laisser les humains d\u00e9cider de ce qu&rsquo;il faut faire.<\/p>\n<h2>D\u00e9ployer et g\u00e9rer votre Slackbot<\/h2>\n<p>Une fois que votre Slackbot est dot\u00e9 de toutes les fonctionnalit\u00e9s, vous devez le d\u00e9ployer dans un environnement de production o\u00f9 il peut fonctionner 24\/7.<\/p>\n<p><a href=\"https:\/\/sevalla.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Sevalla<\/a> de Kinsta est un excellent endroit pour h\u00e9berger des robots comme celui-ci. Il prend en charge les applications Node.js, les variables d&rsquo;environnement, la journalisation et les d\u00e9ploiements \u00e9volutifs d\u00e8s le d\u00e9part.<\/p>\n<p>Sinon, vous pouvez conteneuriser votre robot \u00e0 l&rsquo;aide de <a href=\"https:\/\/kinsta.com\/fr\/blog\/qu-est-ce-que-docker\/\">Docker<\/a> ou le d\u00e9ployer sur n&rsquo;importe quelle plateforme cloud qui prend en charge Node.js et les services d&rsquo;arri\u00e8re-plan.<\/p>\n<p>Voici quelques points \u00e0 garder \u00e0 l&rsquo;esprit avant de passer \u00e0 l&rsquo;action :<\/p>\n<ul>\n<li>Utilisez des variables d&rsquo;environnement pour tous les secrets (jetons Slack, cl\u00e9s API Kinsta, cl\u00e9s SSH).<\/li>\n<li>Configurez la journalisation et la surveillance du temps de fonctionnement pour que vous sachiez quand quelque chose se casse.<\/li>\n<li>Ex\u00e9cutez votre robot avec un gestionnaire de processus comme PM2 ou la politique <code>restart: always<\/code> de Docker pour le maintenir en vie apr\u00e8s les pannes ou les red\u00e9marrages.<\/li>\n<li>Gardez vos cl\u00e9s SSH en s\u00e9curit\u00e9, surtout si vous les utilisez pour l&rsquo;automatisation.<\/li>\n<\/ul>\n<h2>R\u00e9sum\u00e9<\/h2>\n<p>Vous avez maintenant fait passer votre Slackbot d&rsquo;un simple gestionnaire de commandes \u00e0 un outil puissant dot\u00e9 d&rsquo;une r\u00e9elle interactivit\u00e9, d&rsquo;une automatisation programm\u00e9e et d&rsquo;une surveillance solide. Ces caract\u00e9ristiques rendent votre robot plus utile, plus fiable et bien plus agr\u00e9able \u00e0 utiliser, en particulier pour les \u00e9quipes qui g\u00e8rent plusieurs sites WordPress.<\/p>\n<p>Et quand vous associez cela \u00e0 la puissance de l&rsquo;<a href=\"https:\/\/kinsta.com\/fr\/docs\/api-kinsta\/\">API Kinsta<\/a> et \u00e0 l&rsquo;<a href=\"https:\/\/kinsta.com\/fr\/hebergement-wordpress\/\">h\u00e9bergement sans stress de Kinsta<\/a>, vous obtenez une configuration \u00e0 la fois \u00e9volutive et fiable.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Les Slackbots n&rsquo;ont pas besoin d&rsquo;attendre que vous saisissiez des commandes. Avec la bonne configuration, votre robot peut vous aider \u00e0 g\u00e9rer vos sites WordPress en &#8230;<\/p>\n","protected":false},"author":287,"featured_media":79880,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[994],"class_list":["post-79879","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","topic-node-js"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.6 (Yoast SEO v24.6) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Ajoutez de l&#039;interactivit\u00e9 aux slackbots pour la gestion de WordPress.<\/title>\n<meta name=\"description\" content=\"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l&#039;interactivit\u00e9, planifiez et surveillez \u00e0 l&#039;aide de Node.js et de l&#039;API Kinsta.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Mettre en \u0153uvre l&#039;interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress\" \/>\n<meta property=\"og:description\" content=\"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l&#039;interactivit\u00e9, planifiez et surveillez \u00e0 l&#039;aide de Node.js et de l&#039;API Kinsta.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstafrance\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-05-16T07:43:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-05-19T08:06:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1470\" \/>\n\t<meta property=\"og:image:height\" content=\"735\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Joel Olawanle\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l&#039;interactivit\u00e9, planifiez et surveillez \u00e0 l&#039;aide de Node.js et de l&#039;API Kinsta.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\" \/>\n<meta name=\"twitter:creator\" content=\"@olawanle_joel\" \/>\n<meta name=\"twitter:site\" content=\"@kinsta_fr\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"Joel Olawanle\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"26 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\"},\"author\":{\"name\":\"Joel Olawanle\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07\"},\"headline\":\"Mettre en \u0153uvre l&rsquo;interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress\",\"datePublished\":\"2025-05-16T07:43:11+00:00\",\"dateModified\":\"2025-05-19T08:06:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\"},\"wordCount\":2298,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"inLanguage\":\"fr-FR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\",\"url\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\",\"name\":\"Ajoutez de l'interactivit\u00e9 aux slackbots pour la gestion de WordPress.\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"datePublished\":\"2025-05-16T07:43:11+00:00\",\"dateModified\":\"2025-05-19T08:06:25+00:00\",\"description\":\"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l'interactivit\u00e9, planifiez et surveillez \u00e0 l'aide de Node.js et de l'API Kinsta.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"contentUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"width\":1470,\"height\":735},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/fr\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Node.js\",\"item\":\"https:\/\/kinsta.com\/fr\/sujets\/node-js\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Mettre en \u0153uvre l&#8217;interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/fr\/#website\",\"url\":\"https:\/\/kinsta.com\/fr\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/fr\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/fr\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/fr\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstafrance\/\",\"https:\/\/x.com\/kinsta_fr\",\"https:\/\/www.instagram.com\/kinstahosting\/\",\"https:\/\/www.linkedin.com\/company\/kinsta\/\",\"https:\/\/www.pinterest.com\/kinstahosting\/\",\"https:\/\/www.youtube.com\/c\/Kinsta\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07\",\"name\":\"Joel Olawanle\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/kinsta.com\/fr\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/051bf577ce2c837846a1db9eef184758?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/051bf577ce2c837846a1db9eef184758?s=96&d=mm&r=g\",\"caption\":\"Joel Olawanle\"},\"description\":\"Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 300 technical articles majorly around JavaScript and it's frameworks.\",\"sameAs\":[\"https:\/\/joelolawanle.com\/\",\"https:\/\/www.linkedin.com\/in\/olawanlejoel\/\",\"https:\/\/x.com\/olawanle_joel\",\"https:\/\/www.youtube.com\/@joelolawanle\"],\"gender\":\"male\",\"knowsAbout\":[\"JavaScript\",\"React\",\"Next.js\"],\"knowsLanguage\":[\"English\"],\"jobTitle\":\"Technical Editor\",\"worksFor\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/fr\/blog\/author\/joelolawanle\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Ajoutez de l'interactivit\u00e9 aux slackbots pour la gestion de WordPress.","description":"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l'interactivit\u00e9, planifiez et surveillez \u00e0 l'aide de Node.js et de l'API Kinsta.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/","og_locale":"fr_FR","og_type":"article","og_title":"Mettre en \u0153uvre l'interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress","og_description":"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l'interactivit\u00e9, planifiez et surveillez \u00e0 l'aide de Node.js et de l'API Kinsta.","og_url":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstafrance\/","article_published_time":"2025-05-16T07:43:11+00:00","article_modified_time":"2025-05-19T08:06:25+00:00","og_image":[{"width":1470,"height":735,"url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","type":"image\/png"}],"author":"Joel Olawanle","twitter_card":"summary_large_image","twitter_description":"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l'interactivit\u00e9, planifiez et surveillez \u00e0 l'aide de Node.js et de l'API Kinsta.","twitter_image":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","twitter_creator":"@olawanle_joel","twitter_site":"@kinsta_fr","twitter_misc":{"\u00c9crit par":"Joel Olawanle","Dur\u00e9e de lecture estim\u00e9e":"26 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/"},"author":{"name":"Joel Olawanle","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07"},"headline":"Mettre en \u0153uvre l&rsquo;interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress","datePublished":"2025-05-16T07:43:11+00:00","dateModified":"2025-05-19T08:06:25+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/"},"wordCount":2298,"publisher":{"@id":"https:\/\/kinsta.com\/fr\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","inLanguage":"fr-FR"},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/","url":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/","name":"Ajoutez de l'interactivit\u00e9 aux slackbots pour la gestion de WordPress.","isPartOf":{"@id":"https:\/\/kinsta.com\/fr\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","datePublished":"2025-05-16T07:43:11+00:00","dateModified":"2025-05-19T08:06:25+00:00","description":"Automatisez la gestion de WordPress gr\u00e2ce \u00e0 un puissant Slackbot. Ajoutez de l'interactivit\u00e9, planifiez et surveillez \u00e0 l'aide de Node.js et de l'API Kinsta.","breadcrumb":{"@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#primaryimage","url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","contentUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","width":1470,"height":735},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/fr\/blog\/ajout-interactivite-planification-surveillance-slackbots\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/fr\/"},{"@type":"ListItem","position":2,"name":"Node.js","item":"https:\/\/kinsta.com\/fr\/sujets\/node-js\/"},{"@type":"ListItem","position":3,"name":"Mettre en \u0153uvre l&#8217;interactivit\u00e9, la programmation et la surveillance dans les Slackbots pour la gestion des sites WordPress"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/fr\/#website","url":"https:\/\/kinsta.com\/fr\/","name":"Kinsta\u00ae","description":"Solutions d&#039;h\u00e9bergement premium, rapides et s\u00e9curis\u00e9es","publisher":{"@id":"https:\/\/kinsta.com\/fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/fr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/fr\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/fr\/wp-content\/uploads\/sites\/4\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstafrance\/","https:\/\/x.com\/kinsta_fr","https:\/\/www.instagram.com\/kinstahosting\/","https:\/\/www.linkedin.com\/company\/kinsta\/","https:\/\/www.pinterest.com\/kinstahosting\/","https:\/\/www.youtube.com\/c\/Kinsta"]},{"@type":"Person","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07","name":"Joel Olawanle","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/kinsta.com\/fr\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/051bf577ce2c837846a1db9eef184758?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/051bf577ce2c837846a1db9eef184758?s=96&d=mm&r=g","caption":"Joel Olawanle"},"description":"Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 300 technical articles majorly around JavaScript and it's frameworks.","sameAs":["https:\/\/joelolawanle.com\/","https:\/\/www.linkedin.com\/in\/olawanlejoel\/","https:\/\/x.com\/olawanle_joel","https:\/\/www.youtube.com\/@joelolawanle"],"gender":"male","knowsAbout":["JavaScript","React","Next.js"],"knowsLanguage":["English"],"jobTitle":"Technical Editor","worksFor":"Kinsta","url":"https:\/\/kinsta.com\/fr\/blog\/author\/joelolawanle\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/79879","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/users\/287"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/comments?post=79879"}],"version-history":[{"count":5,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/79879\/revisions"}],"predecessor-version":[{"id":79889,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/posts\/79879\/revisions\/79889"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/jp"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/nl"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/translations\/es"},{"href":"https:\/\/kinsta.com\/fr\/wp-json\/kinsta\/v1\/posts\/79879\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/media\/79880"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/media?parent=79879"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/tags?post=79879"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/fr\/wp-json\/wp\/v2\/topic?post=79879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}