# Tasks
> Execute rotinas periódicas globais ou por guild sem misturar timers com os handlers do Discord.

- Área: Base do Core
- URL humana: https://ninenity.vercel.app/doc/tasks
- URL Markdown: https://ninenity.vercel.app/doc/markdown/tasks
- Pacotes e conceitos: Global, Guild, Controle manual

## Quando usar uma Task

- **Sincronização**: Atualizar cache, telemetria ou dados de uma API em intervalos previsíveis.
- **Manutenção**: Limpar registros expirados, revisar configurações ou renovar estados.
- **Rotina por guild**: Executar a mesma regra com `guildId` e configuração isolados.

> **Task não é um worker infinito:** A função `execute` deve terminar. O runtime agenda a próxima execução; não crie `while (true)` nem um segundo `setInterval` dentro dela.

## Task global

Uma task global roda uma vez por intervalo para o Client inteiro.

### Discord/Tasks/Telemetry.ts

```text

import Client from '../../Shared/Settings/Client'

Client.task({
  id: 'global-telemetry',
  timeout: '5m',
  runOnStart: true,
  scope: 'global',
  execute: async client => {
    console.log(
      '[Task:telemetry] guilds=' + client.guilds.cache.size +
      ' users=' + client.users.cache.size
    )
  }
})
```


| Campo | Função |
| --- | --- |
| `id` | Identificador único usado também no controle manual |
| `timeout` | Intervalo em ms ou texto como `30s`, `5m`, `1h` |
| `runOnStart` | Executa logo no boot antes de esperar o primeiro intervalo |
| `scope` | `global` executa uma vez; `guild` executa para cada servidor |
| `execute` | Função assíncrona que recebe o Client e, em guild, o `guildId` |

## Task por guild

O runtime chama a execução separadamente para cada guild e ativa o contexto usado por `appConfig()`.

### Discord/Tasks/GuildMaintenance.ts

```text

import Client from '../../Shared/Settings/Client'

Client.task({
  id: 'guild-maintenance',
  timeout: '10m',
  runOnStart: false,
  scope: 'guild',
  execute: async (client, guildId) => {
    if (!guildId) return

    const guild = client.guilds.cache.get(guildId)
    const config = client.appConfig(guildId)
    if (!guild || config.variables?.maintenanceEnabled === false) return

    await removeExpiredRecords(guildId)
    console.log('[Task:maintenance] guild=' + guild.name)
  }
})
```


> **Estado isolado:** Tasks de guild não compartilham o contexto de canais, cargos, configuração ou fila com outro servidor. Ainda assim, dados globais criados por você precisam ser indexados por `guildId`.

## Iniciar, parar e executar manualmente

Use os controles pelo `id` quando uma operação administrativa precisar alterar a rotina.

### controle em comando administrativo

```text

Client.slash({
  data: new SlashCommandBuilder()
    .setName('maintenance-run')
    .setDescription('Executa a manutenção agora.'),
  execute: async interaction => {
    await interaction.deferReply({ flags: 64 })
    await Client.runTask('guild-maintenance')
    await interaction.editReply('Manutenção executada.')
  }
})

// Pausa os próximos ciclos
Client.stopTask('guild-maintenance')

// Reativa o agendamento
Client.startTask('guild-maintenance')
```


`runTask()` dispara uma execução sem substituir o agendamento. `stopTask()` limpa o timer daquela task no Client atual; `startTask()` cria o ciclo novamente.

## Evite sobreposição

Se a execução pode durar mais que o intervalo, proteja a seção crítica com a fila do Core.

### task com fila

```text

Client.task({
  id: 'sync-catalog',
  timeout: '1m',
  scope: 'global',
  execute: async () => {
    await Client.queue('task:sync-catalog', async () => {
      const items = await catalogApi.list()
      await catalogRepository.replace(items)
      console.log('[Task:catalog] items=' + items.length)
    })
  }
})
```


- Escolha intervalos maiores que o tempo normal da operação.
- Defina timeout nas requisições externas chamadas pela task.
- Faça a rotina ser idempotente: repetir não deve duplicar dados.
- Registre apenas início, resultado e falhas úteis; evite logs a cada item processado.
