{"id":71847,"date":"2025-05-16T04:48:52","date_gmt":"2025-05-16T07:48:52","guid":{"rendered":"https:\/\/kinsta.com\/pt\/?p=71847&#038;preview=true&#038;preview_id=71847"},"modified":"2025-05-21T06:35:57","modified_gmt":"2025-05-21T09:35:57","slug":"adicionar-interatividade-agendamento-monitoramento-slackbot","status":"publish","type":"post","link":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/","title":{"rendered":"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress"},"content":{"rendered":"<p>Os slackbots n\u00e3o precisam esperar que voc\u00ea digite comandos. Com a configura\u00e7\u00e3o correta, seu bot pode ajudar a gerenciar seus sites <a href=\"https:\/\/kinsta.com\/pt\/blog\/o-que-wordpress\/\">WordPress<\/a> oferecendo bot\u00f5es interativos, menus suspensos, tarefas agendadas e alertas inteligentes &#8211; tudo isso diretamente no <a href=\"https:\/\/kinsta.com\/pt\/blog\/como-usar-slack\/\">Slack<\/a>.<\/p>\n<p>Neste artigo, mostraremos a voc\u00ea como adicionar interatividade, automa\u00e7\u00e3o e monitoramento ao seu slackbot.<\/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>Pr\u00e9-requisitos<\/h2>\n<p>Antes de come\u00e7ar, certifique-se de ter:<\/p>\n<ul>\n<li>Um aplicativo Slack com permiss\u00f5es de bot e um comando de barra.<\/li>\n<li>Uma <a href=\"https:\/\/kinsta.com\/pt\/\">conta Kinsta<\/a> com acesso \u00e0 API e um site para voc\u00ea testar.<\/li>\n<li>Node.js e NPM instalados localmente.<\/li>\n<li>Familiaridade b\u00e1sica com JavaScript (ou, pelo menos, que voc\u00ea se sinta \u00e0 vontade para copiar e ajustar o c\u00f3digo).<\/li>\n<li>Chaves API do Slack e da Kinsta.<\/li>\n<\/ul>\n<h2>Primeiros passos<\/h2>\n<p>Para criar esse Slackbot, o <a href=\"https:\/\/kinsta.com\/pt\/blog\/o-que-e-node-js\/\">Node.js<\/a> e a <a href=\"https:\/\/api.slack.com\/bolt\" target=\"_blank\" rel=\"noopener noreferrer\">framework Bolt<\/a> do Slack s\u00e3o usados para conectar comandos de barra que acionam a\u00e7\u00f5es via a API da Kinsta.<\/p>\n<p>N\u00e3o vamos repetir todas as etapas de cria\u00e7\u00e3o de um aplicativo Slack ou de como obter acesso \u00e0 API da Kinsta neste guia, pois isso j\u00e1 foi abordado no nosso guia anterior, <a href=\"https:\/\/kinsta.com\/pt\/blog\/criar-slackbot-gerenciamento-sites\/\">Como criar um Slackbot com Node.js e API da Kinsta para Gerenciamento de Sites<\/a>.<\/p>\n<p>Se voc\u00ea ainda n\u00e3o leu esse guia, leia primeiro. Ele mostra como criar seu aplicativo Slack, obter o token do bot e o segredo de assinatura, al\u00e9m da chave API da Kinsta.<\/p>\n<h2>Adicione interatividade ao seu Slackbot<\/h2>\n<p>Slackbots n\u00e3o precisam se basear apenas em comandos slash. Com componentes interativos como bot\u00f5es, menus e modais, voc\u00ea pode transformar seu bot em uma ferramenta muito mais intuitiva e amig\u00e1vel ao usu\u00e1rio.<\/p>\n<p>Em vez de digitar <code>\/clear_cache environment_id<\/code>, imagine que voc\u00ea clique em um bot\u00e3o chamado <strong>Limpar cache<\/strong> logo ap\u00f3s verificar o status de um site. Para fazer isso, voc\u00ea precisa do cliente da <a href=\"https:\/\/www.npmjs.com\/package\/@slack\/web-api\" target=\"_blank\" rel=\"noopener noreferrer\">Web API do Slack<\/a>. Instale-o em seu projeto com o comando abaixo:<\/p>\n<pre><code class=\"language-bash\">npm install @slack\/web-api<\/code><\/pre>\n<p>Em seguida, inicialize-o em seu site <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>Certifique-se de que <code>SLACK_BOT_TOKEN<\/code> esteja definido em seu arquivo <code>.env<\/code>. Agora, vamos aprimorar o comando <code>\/site_status<\/code> do artigo anterior. Em vez de apenas enviar texto, anexamos bot\u00f5es para a\u00e7\u00f5es r\u00e1pidas, como <strong>Limpar cache<\/strong>, <strong>Criar backup<\/strong> ou <strong>Verificar status detalhado<\/strong>.<\/p>\n<p>Veja como fica o manipulador atualizado:<\/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>Cada clique em um bot\u00e3o aciona uma a\u00e7\u00e3o. Veja como lidamos com o bot\u00e3o <strong>Limpar 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>Voc\u00ea pode seguir o mesmo padr\u00e3o para os bot\u00f5es de backup e status, apenas vinculando cada um ao endpoint da API ou \u00e0 l\u00f3gica de comando apropriada.<\/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>Use um menu suspenso para selecionar um site<\/h3>\n<p>Digitar IDs de ambiente n\u00e3o \u00e9 divertido. E voc\u00ea espera que cada membro da equipe se lembre de qual ID pertence a qual ambiente? Isso n\u00e3o \u00e9 realista.<\/p>\n<p>Vamos tornar isso mais intuitivo. Em vez de pedir aos usu\u00e1rios que digitem <code>\/site_status [environment-id]<\/code>, daremos a eles um menu suspenso do Slack em que poder\u00e3o escolher um site em uma lista. Assim que o site for selecionado, o bot exibir\u00e1 o status e anexar\u00e1 os mesmos bot\u00f5es de a\u00e7\u00e3o r\u00e1pida que implementamos anteriormente.<\/p>\n<p>Para fazer isso, n\u00f3s:<\/p>\n<ul>\n<li>Buscamos todos os sites na API da Kinsta<\/li>\n<li>Buscamos os ambientes de cada site<\/li>\n<li>Criamos um menu suspenso com essas op\u00e7\u00f5es<\/li>\n<li>Tratamos a sele\u00e7\u00e3o do usu\u00e1rio e exibimos o status do site<\/li>\n<\/ul>\n<p>Aqui est\u00e1 o comando que mostra o menu suspenso:<\/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>Quando um usu\u00e1rio escolhe um site, tratamos disso com este manipulador de a\u00e7\u00e3o:<\/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>Agora que nosso bot pode acionar a\u00e7\u00f5es com um bot\u00e3o e selecionar sites em uma lista, vamos nos certificar de que n\u00e3o executamos opera\u00e7\u00f5es arriscadas acidentalmente.<\/p>\n<h3>Di\u00e1logos de confirma\u00e7\u00e3o<\/h3>\n<p>Algumas opera\u00e7\u00f5es nunca devem ser executadas acidentalmente. Limpar um cache pode parecer inofensivo, mas se voc\u00ea estiver trabalhando em um site de produ\u00e7\u00e3o, provavelmente n\u00e3o vai querer fazer isso com um \u00fanico clique, especialmente se estiver apenas verificando o status do site. \u00c9 a\u00ed que entram os modais (caixas de di\u00e1logo) do Slack.<\/p>\n<p>Em vez de limpar imediatamente o cache quando voc\u00ea clica em <code>clear_cache_button<\/code>, mostramos um modal de confirma\u00e7\u00e3o. Veja como:<\/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>No c\u00f3digo acima, usamos <code>web.views.open()<\/code> para iniciar um modal com um t\u00edtulo claro, uma mensagem de aviso e dois bot\u00f5es &#8211; <strong data-start=\"1644\" data-end=\"1660\">Limpar Cache<\/strong> e <strong data-start=\"1663\" data-end=\"1675\">Cancelar<\/strong>\u00a0&#8211; e armazenamos o <code>environmentId<\/code> em <code>private_metadata<\/code> para que voc\u00ea o tenha quando o usu\u00e1rio clicar em <strong>Clear Cache<\/strong>.<\/p>\n<p>Quando o usu\u00e1rio clica no bot\u00e3o <strong>Limpar Cache<\/strong>\u00a0no modal, o Slack envia um evento <code>view_submission<\/code>. Veja como lidar com ele e prosseguir com a opera\u00e7\u00e3o real:<\/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>Neste c\u00f3digo, depois que o usu\u00e1rio confirma, pegamos o <code>environmentId<\/code> de <code>private_metadata<\/code>, abrimos um DM privado usando <code>web.conversations.open()<\/code> para evitar a confus\u00e3o de canais p\u00fablicos, executamos a solicita\u00e7\u00e3o de API para limpar o cache e acompanhamos com uma mensagem de sucesso ou de erro, dependendo do resultado.<\/p>\n<h3>Indicadores de progresso<\/h3>\n<p>Alguns comandos do Slack s\u00e3o instant\u00e2neos, como limpar um cache ou verificar um status. Mas e outros? Nem tanto.<\/p>\n<p>Criar um backup ou implantar arquivos pode levar v\u00e1rios segundos, ou at\u00e9 minutos. E se o seu bot simplesmente ficar em sil\u00eancio durante esse tempo, o usu\u00e1rio pode pensar que algo deu errado.<\/p>\n<p>O Slack n\u00e3o oferece uma barra de progresso nativa, mas podemos simular uma com um pouco de criatividade. Aqui est\u00e1 uma fun\u00e7\u00e3o auxiliar que atualiza uma mensagem com uma barra de progresso visual usando o <strong data-start=\"866\" data-end=\"879\">Block Kit<\/strong>:<\/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>Vamos integrar isso em um comando <code>\/create_backup<\/code>. Em vez de esperar que toda a opera\u00e7\u00e3o seja conclu\u00edda antes de responder, faremos o check-in com o usu\u00e1rio em cada etapa.<\/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>Notifica\u00e7\u00f5es de sucesso\/falha<\/h3>\n<p>Atualmente, seu bot provavelmente envia mensagens simples como <strong>\u2705 Success<\/strong>\u00a0ou <strong>\u274c Failed<\/strong>. Funciona, mas \u00e9 sem gra\u00e7a \u2014 e n\u00e3o ajuda o usu\u00e1rio a entender por que algo deu certo ou o que fazer quando falha.<\/p>\n<p>Vamos corrigir isso com a formata\u00e7\u00e3o adequada para mensagens de sucesso e de erro, juntamente com contexto \u00fatil, sugest\u00f5es e formata\u00e7\u00e3o limpa.<\/p>\n<p>Adicione esses utilit\u00e1rios ao seu site <code>utils.js<\/code> para que voc\u00ea possa reutiliz\u00e1-los em todos os comandos:<\/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>Essas fun\u00e7\u00f5es recebem dados estruturados e os transformam em markdown compat\u00edvel com Slack, com emojis, etiquetas e quebras de linha. Muito mais f\u00e1cil de visualizar no meio de um thread cheio no Slack. Veja como isso fica em um manipulador real de comandos. Vamos usar o <code>\/clear_cache<\/code> como exemplo:<\/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>Automatize tarefas do WordPress com jobs agendados<\/h2>\n<p>At\u00e9 agora, tudo o que o Slackbot faz acontece quando algu\u00e9m aciona explicitamente um comando. Mas nem tudo deveria depender de algu\u00e9m lembrar de rodar uma tarefa.<\/p>\n<p>E se seu bot pudesse fazer backup dos seus sites automaticamente todas as noites? Ou verificar se algum site est\u00e1 fora do ar todas as manh\u00e3s antes da equipe acordar?<\/p>\n<p>Vamos usar a biblioteca <a href=\"https:\/\/www.npmjs.com\/package\/node-schedule\" target=\"_blank\" rel=\"noopener noreferrer\">node-schedule<\/a> para executar tarefas com base em express\u00f5es cron. Primeiro, instale-a:<\/p>\n<pre><code class=\"language-bash\">npm install node-schedule<\/code><\/pre>\n<p>Agora, configure-a na parte superior do seu site <code>app.js<\/code>:<\/p>\n<pre><code class=\"language-js\">const schedule = require('node-schedule');<\/code><\/pre>\n<p>Tamb\u00e9m precisaremos de uma forma de rastrear os jobs agendados ativos, para que os usu\u00e1rios possam list\u00e1-los ou cancel\u00e1-los posteriormente:<\/p>\n<pre><code class=\"language-js\">const scheduledJobs = {};<\/code><\/pre>\n<h3>Criando o comando de agendamento de tarefas<\/h3>\n<p>Come\u00e7aremos com um comando b\u00e1sico <code>\/schedule_task<\/code> que aceita um tipo de tarefa (<code>backup<\/code>, <code>clear_cache<\/code> ou <code>status_check<\/code>), o ID do ambiente e uma express\u00e3o cron.<\/p>\n<pre><code class=\"language-bash\">\/schedule_task backup 12345 0 0 * * *<\/code><\/pre>\n<p>Esse comando agendaria um backup di\u00e1rio \u00e0 meia-noite. Aqui est\u00e1 o manipulador de comando completo:<\/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>Cancelando tarefas agendadas<\/h3>\n<p>Se algo mudar ou a tarefa n\u00e3o for mais necess\u00e1ria, os usu\u00e1rios podem cancel\u00e1-la com:<\/p>\n<pre><code class=\"language-bash\">\/cancel_task<\/code><\/pre>\n<p>Aqui est\u00e1 a implementa\u00e7\u00e3o:<\/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>Listando todas as tarefas agendadas<\/h3>\n<p>Vamos permitir tamb\u00e9m que os usu\u00e1rios visualizem todos os jobs programados:<\/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>Isso d\u00e1 ao seu Slackbot um n\u00edvel totalmente novo de autonomia. Backups, limpezas de cache e verifica\u00e7\u00f5es de status n\u00e3o precisam mais ser tarefas de algu\u00e9m. Eles simplesmente acontecem de forma silenciosa, confi\u00e1vel e dentro do cronograma.<\/p>\n<h2>Manuten\u00e7\u00e3o recorrente<\/h2>\n<p>\u00c0s vezes, voc\u00ea quer executar um conjunto de tarefas de manuten\u00e7\u00e3o em intervalos regulares, como backups semanais e limpezas de cache nas noites de domingo. \u00c9 a\u00ed que entram as janelas de manuten\u00e7\u00e3o (maintenance windows).<\/p>\n<p>Uma janela de manuten\u00e7\u00e3o \u00e9 um bloco de tempo programado em que o bot executa automaticamente tarefas predefinidas, como:<\/p>\n<ul>\n<li>Criar um backup<\/li>\n<li>Limpar o cache<\/li>\n<li>Enviar notifica\u00e7\u00f5es de in\u00edcio e conclus\u00e3o<\/li>\n<\/ul>\n<p>O formato \u00e9 simples:<\/p>\n<pre><code class=\"language-bash\">\/maintenance_window [environment_id] [day_of_week] [hour] [duration_hours]<\/code><\/pre>\n<p>Por exemplo:<\/p>\n<pre><code class=\"language-bash\">\/maintenance_window 12345 Sunday 2 3<\/code><\/pre>\n<p>Isso significa que todo domingo, \u00e0s 2h da manh\u00e3, as tarefas de manuten\u00e7\u00e3o s\u00e3o executadas por 3 horas. Aqui est\u00e1 a implementa\u00e7\u00e3o completa:<\/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>Relat\u00f3rios automatizados<\/h3>\n<p>Voc\u00ea n\u00e3o quer acordar toda segunda-feira se perguntando se o backup do seu site WordPress foi feito ou se ele ficou fora do ar por horas. Com os relat\u00f3rios automatizados, seu bot do Slack pode fornecer a voc\u00ea e \u00e0 sua equipe um resumo r\u00e1pido do desempenho em uma programa\u00e7\u00e3o.<\/p>\n<p>Esse tipo de relat\u00f3rio \u00e9 \u00f3timo para que voc\u00ea mantenha o controle sobre coisas como:<\/p>\n<ul>\n<li>O status atual do site<\/li>\n<li>Atividade de backup nos \u00faltimos 7 dias<\/li>\n<li>Vers\u00e3o do PHP e dom\u00ednio principal<\/li>\n<li>Quaisquer sinais de alerta, como ambientes bloqueados ou backups ausentes<\/li>\n<\/ul>\n<p>Vamos criar um comando <code>\/schedule_report<\/code> que automatize isso.<\/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>Tratamento de erros e monitoramento<\/h2>\n<p>Quando seu bot come\u00e7ar a realizar opera\u00e7\u00f5es reais, como modificar ambientes ou acionar tarefas agendadas, voc\u00ea precisar\u00e1 de mais do que <code>console.log()<\/code> para acompanhar o que est\u00e1 acontecendo nos bastidores.<\/p>\n<p>Vamos dividir isso em camadas limpas e de f\u00e1cil manuten\u00e7\u00e3o:<\/p>\n<h3>Registros estruturados com Winston<\/h3>\n<p>Em vez de imprimir registros no console, use <code><a href=\"https:\/\/www.npmjs.com\/package\/winston\" target=\"_blank\" rel=\"noopener noreferrer\">winston<\/a><\/code> para enviar registros estruturados para arquivos e, opcionalmente, para servi\u00e7os como <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>. Instale-o com o comando abaixo:<\/p>\n<pre><code class=\"language-bash\">npm install winston<\/code><\/pre>\n<p>Em seguida, configure <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>No seu <code data-start=\"1103\" data-end=\"1111\">app.js<\/code>, substitua <code data-start=\"1123\" data-end=\"1138\">console.log()<\/code> ou <code data-start=\"1142\" data-end=\"1159\">console.error()<\/code> por:<\/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>Envie alertas para administradores via Slack<\/h3>\n<p>Voc\u00ea j\u00e1 tem a vari\u00e1vel de ambiente <code>ADMIN_USERS<\/code>, use-a para notificar sua equipe diretamente no Slack quando algo cr\u00edtico falhar:<\/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>Use-a desta forma:<\/p>\n<pre><code class=\"language-js\">await alertAdmins('Backup Failed', {\n  environmentId,\n  error: error.message,\n  user: ``\n});<\/code><\/pre>\n<h3>Monitore desempenho com m\u00e9tricas simples<\/h3>\n<p>N\u00e3o use o <a href=\"https:\/\/prometheus.io\/docs\/guides\/query-log\/\" target=\"_blank\" rel=\"noopener noreferrer\">Prometheus<\/a> completo se voc\u00ea estiver apenas tentando verificar a integridade do seu bot. Mantenha um objeto de desempenho leve:<\/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>Atualize isso dentro do seu helper <code data-start=\"2568\" data-end=\"2585\">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>Exponha as m\u00e9tricas com o comando <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>Opcional: defina etapas de recupera\u00e7\u00e3o<\/h3>\n<p>Se voc\u00ea quiser implementar a l\u00f3gica de recupera\u00e7\u00e3o (como tentar novamente limpar o cache via <a href=\"https:\/\/kinsta.com\/pt\/blog\/usar-ssh\/\">SSH<\/a>), basta criar um helper como:<\/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>Evite misturar esse tipo de l\u00f3gica diretamente nos comandos principais, exceto quando for um caminho cr\u00edtico. Em muitos casos, \u00e9 melhor apenas registrar o erro, alertar os administradores e deixar que humanos tomem a decis\u00e3o.<\/p>\n<h2>Implante e gerencie seu Slackbot<\/h2>\n<p>Quando o seu bot estiver completo, \u00e9 hora de coloc\u00e1-lo em produ\u00e7\u00e3o, em um ambiente que esteja sempre dispon\u00edvel.<\/p>\n<p>O <a href=\"https:\/\/sevalla.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Sevalla<\/a> da Kinsta \u00e9 um excelente lugar para voc\u00ea hospedar bots como esse. Ele oferece suporte a aplicativos Node.js, vari\u00e1veis de ambiente, registro e implementa\u00e7\u00f5es escalon\u00e1veis prontas para uso.<\/p>\n<p>Como alternativa, voc\u00ea pode colocar seu bot em cont\u00eaineres usando o <a href=\"https:\/\/kinsta.com\/pt\/blog\/que-e-docker\/\">Docker<\/a> ou implant\u00e1-lo em qualquer plataforma de nuvem que ofere\u00e7a suporte a Node.js e servi\u00e7os em segundo plano.<\/p>\n<p>Aqui est\u00e3o algumas coisas que voc\u00ea deve ter em mente antes de entrar em opera\u00e7\u00e3o:<\/p>\n<ul>\n<li>Use vari\u00e1veis de ambiente para todos os segredos (tokens do Slack, chaves API da Kinsta, chaves SSH).<\/li>\n<li>Configure o registro e o monitoramento do tempo de atividade para saber quando algo falhar.<\/li>\n<li>Execute seu bot com um gerenciador de processos como o PM2 ou a pol\u00edtica <code>restart: always<\/code> do Docker para mant\u00ea-lo ativo ap\u00f3s falhas ou reinicializa\u00e7\u00f5es.<\/li>\n<li>Mantenha suas chaves SSH seguras, especialmente se voc\u00ea as estiver usando para automa\u00e7\u00e3o.<\/li>\n<\/ul>\n<h2>Resumo<\/h2>\n<p>Agora voc\u00ea transformou seu Slackbot de um simples manipulador de comandos em uma ferramenta poderosa com interatividade real, automa\u00e7\u00e3o programada e monitoramento s\u00f3lido. Esses recursos tornam seu bot mais \u00fatil, mais confi\u00e1vel e muito mais agrad\u00e1vel de usar, especialmente para equipes que gerenciam v\u00e1rios sites WordPress.<\/p>\n<p>E ao combinar tudo isso com o poder da <a href=\"https:\/\/kinsta.com\/pt\/docs\/kinsta-api\/\">API da Kinsta<\/a> e a <a href=\"https:\/\/kinsta.com\/pt\/hospedagem-wordpress\/\">hospedagem sem estresse da Kinsta<\/a>, voc\u00ea tem uma configura\u00e7\u00e3o que \u00e9 escal\u00e1vel e confi\u00e1vel.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Os slackbots n\u00e3o precisam esperar que voc\u00ea digite comandos. Com a configura\u00e7\u00e3o correta, seu bot pode ajudar a gerenciar seus sites WordPress oferecendo bot\u00f5es interativos, menus &#8230;<\/p>\n","protected":false},"author":287,"featured_media":71848,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kinsta_gated_content":false,"_kinsta_gated_content_redirect":"","footnotes":""},"tags":[],"topic":[992],"class_list":["post-71847","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>Adicionar Interatividade em Slackbots para Gerenciar Sites WordPress<\/title>\n<meta name=\"description\" content=\"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da 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\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\" \/>\n<meta property=\"og:locale\" content=\"pt_PT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress\" \/>\n<meta property=\"og:description\" content=\"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da Kinsta.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\" \/>\n<meta property=\"og:site_name\" content=\"Kinsta\u00ae\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/kinstapt\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-05-16T07:48:52+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-05-21T09:35:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/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=\"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da Kinsta.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/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_pt\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Joel Olawanle\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo estimado de leitura\" \/>\n\t<meta name=\"twitter:data2\" content=\"26 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\"},\"author\":{\"name\":\"Joel Olawanle\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07\"},\"headline\":\"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress\",\"datePublished\":\"2025-05-16T07:48:52+00:00\",\"dateModified\":\"2025-05-21T09:35:57+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\"},\"wordCount\":2038,\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"inLanguage\":\"pt-PT\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\",\"url\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\",\"name\":\"Adicionar Interatividade em Slackbots para Gerenciar Sites WordPress\",\"isPartOf\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"datePublished\":\"2025-05-16T07:48:52+00:00\",\"dateModified\":\"2025-05-21T09:35:57+00:00\",\"description\":\"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da Kinsta.\",\"breadcrumb\":{\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#breadcrumb\"},\"inLanguage\":\"pt-PT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage\",\"url\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"contentUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png\",\"width\":1470,\"height\":735},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/kinsta.com\/pt\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Node.js\",\"item\":\"https:\/\/kinsta.com\/pt\/topicos\/node-js\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/kinsta.com\/pt\/#website\",\"url\":\"https:\/\/kinsta.com\/pt\/\",\"name\":\"Kinsta\u00ae\",\"description\":\"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura\",\"publisher\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/kinsta.com\/pt\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-PT\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/kinsta.com\/pt\/#organization\",\"name\":\"Kinsta\",\"url\":\"https:\/\/kinsta.com\/pt\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"contentUrl\":\"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg\",\"width\":500,\"height\":500,\"caption\":\"Kinsta\"},\"image\":{\"@id\":\"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/kinstapt\/\",\"https:\/\/x.com\/kinsta_pt\",\"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\/pt\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07\",\"name\":\"Joel Olawanle\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-PT\",\"@id\":\"https:\/\/kinsta.com\/pt\/#\/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\/pt\/blog\/author\/joelolawanle\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Adicionar Interatividade em Slackbots para Gerenciar Sites WordPress","description":"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da 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\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/","og_locale":"pt_PT","og_type":"article","og_title":"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress","og_description":"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da Kinsta.","og_url":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/","og_site_name":"Kinsta\u00ae","article_publisher":"https:\/\/www.facebook.com\/kinstapt\/","article_published_time":"2025-05-16T07:48:52+00:00","article_modified_time":"2025-05-21T09:35:57+00:00","og_image":[{"width":1470,"height":735,"url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/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":"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da Kinsta.","twitter_image":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","twitter_creator":"@olawanle_joel","twitter_site":"@kinsta_pt","twitter_misc":{"Escrito por":"Joel Olawanle","Tempo estimado de leitura":"26 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#article","isPartOf":{"@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/"},"author":{"name":"Joel Olawanle","@id":"https:\/\/kinsta.com\/pt\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07"},"headline":"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress","datePublished":"2025-05-16T07:48:52+00:00","dateModified":"2025-05-21T09:35:57+00:00","mainEntityOfPage":{"@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/"},"wordCount":2038,"publisher":{"@id":"https:\/\/kinsta.com\/pt\/#organization"},"image":{"@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","inLanguage":"pt-PT"},{"@type":"WebPage","@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/","url":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/","name":"Adicionar Interatividade em Slackbots para Gerenciar Sites WordPress","isPartOf":{"@id":"https:\/\/kinsta.com\/pt\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage"},"image":{"@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage"},"thumbnailUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","datePublished":"2025-05-16T07:48:52+00:00","dateModified":"2025-05-21T09:35:57+00:00","description":"Automatize o gerenciamento do WordPress com um Slackbot poderoso. Adicione interatividade, agendamento e monitoramento usando Node.js e a API da Kinsta.","breadcrumb":{"@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#breadcrumb"},"inLanguage":"pt-PT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/"]}]},{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#primaryimage","url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","contentUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2025\/05\/implement-interactivity-scheduling-and-monitoring-in-slackbots-for-managing-wordpress-sites.png","width":1470,"height":735},{"@type":"BreadcrumbList","@id":"https:\/\/kinsta.com\/pt\/blog\/adicionar-interatividade-agendamento-monitoramento-slackbot\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kinsta.com\/pt\/"},{"@type":"ListItem","position":2,"name":"Node.js","item":"https:\/\/kinsta.com\/pt\/topicos\/node-js\/"},{"@type":"ListItem","position":3,"name":"Implementar Interatividade, Agendamento e Monitoramento em Slackbots para Gerenciar Sites WordPress"}]},{"@type":"WebSite","@id":"https:\/\/kinsta.com\/pt\/#website","url":"https:\/\/kinsta.com\/pt\/","name":"Kinsta\u00ae","description":"Solu\u00e7\u00f5es de hospedagem Premium, r\u00e1pida e segura","publisher":{"@id":"https:\/\/kinsta.com\/pt\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kinsta.com\/pt\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-PT"},{"@type":"Organization","@id":"https:\/\/kinsta.com\/pt\/#organization","name":"Kinsta","url":"https:\/\/kinsta.com\/pt\/","logo":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/","url":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","contentUrl":"https:\/\/kinsta.com\/pt\/wp-content\/uploads\/sites\/3\/2023\/12\/kinsta-logo.jpeg","width":500,"height":500,"caption":"Kinsta"},"image":{"@id":"https:\/\/kinsta.com\/pt\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/kinstapt\/","https:\/\/x.com\/kinsta_pt","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\/pt\/#\/schema\/person\/efa7de30245ca15be5ce1dcacff89c07","name":"Joel Olawanle","image":{"@type":"ImageObject","inLanguage":"pt-PT","@id":"https:\/\/kinsta.com\/pt\/#\/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\/pt\/blog\/author\/joelolawanle\/"}]}},"acf":[],"_links":{"self":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/71847","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/users\/287"}],"replies":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/comments?post=71847"}],"version-history":[{"count":6,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/71847\/revisions"}],"predecessor-version":[{"id":71890,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/posts\/71847\/revisions\/71890"}],"alternate":[{"embeddable":true,"hreflang":"en","title":"English","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/en"},{"embeddable":true,"hreflang":"it","title":"Italian","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/it"},{"embeddable":true,"hreflang":"pt","title":"Portuguese","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/pt"},{"embeddable":true,"hreflang":"fr","title":"French","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/fr"},{"embeddable":true,"hreflang":"de","title":"German","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/de"},{"embeddable":true,"hreflang":"ja","title":"Japanese","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/jp"},{"embeddable":true,"hreflang":"nl","title":"Dutch","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/nl"},{"embeddable":true,"hreflang":"es","title":"Spanish","href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/translations\/es"},{"href":"https:\/\/kinsta.com\/pt\/wp-json\/kinsta\/v1\/posts\/71847\/tree"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/media\/71848"}],"wp:attachment":[{"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/media?parent=71847"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/tags?post=71847"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/kinsta.com\/pt\/wp-json\/wp\/v2\/topic?post=71847"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}