Projeto: cutuCÃO — Bot Discord do ASSERT Lab (CIn-UFPE) Mascote: Vira-lata caramelo com megafone Objetivo: Automatizar o acompanhamento de orientandos, substituindo o Carl-bot por uma solução própria com controle total. Stack: TypeScript + discord.js Hospedagem: Nuvem (gratuito ou baixo custo) Repositório: cutucao-bot
O cutuCÃO é o bot Discord do grupo de pesquisa ASSERT Lab. Como um bom vira-lata caramelo, ele é leal, persistente e não te deixa em paz até você fazer o que precisa fazer. Ele automatiza o ritual de check-in semanal dos orientandos, fornece visibilidade passiva ao orientador, e oferece comandos utilitários para o dia a dia do servidor.
O cutuCÃO se comunica com tom amigável, levemente humorado, usando metáforas caninas quando apropriado. Ele "cutuca" com carinho, não com cobrança agressiva. Exemplos:
- Lembrete: "🐕 Au au! Segunda-feira, hora do check-in!"
- Cobrança: "🐕 Oi, sumido(a)! Seu check-in desta semana ainda não apareceu. Tá tudo bem?"
- Resumo: "🐕 Relatório da matilha — semana de DD/MM a DD/MM"
- O aluno é o protagonista. O bot lembra, mas não substitui a responsabilidade do aluno.
- Visibilidade passiva para o orientador. Resumos automáticos, não microgerenciamento.
- Configuração simples. O orientador não precisa editar código para adicionar/remover alunos ou canais.
- Leve e confiável. Mínimo de dependências, máximo de uptime.
Lembrete (segunda-feira, 09:00 horário de Brasília):
- O bot envia uma mensagem em cada canal da categoria "Orientações" toda segunda às 09:00.
- A mensagem inclui o template de check-in e uma menção ao aluno do canal.
- Formato da mensagem:
🐕 **Au au! Check-in Semanal — [data no formato DD/MM/YYYY]**
Hora de atualizar seu progresso! Use o template abaixo:
**O que fiz desde o último check-in:**
-
**O que pretendo fazer esta semana:**
-
**Onde estou travado (se aplicável):**
-
**Próximo marco/entrega:**
-
Cobrança automática (quarta-feira, 09:00):
- Na quarta-feira, o bot verifica quais canais de orientação não tiveram mensagem do aluno desde o lembrete de segunda.
- Para esses canais, envia uma mensagem gentil de lembrete:
🐕 Oi, sumido(a)! Seu check-in desta semana ainda não apareceu. Tá tudo bem por aí?
Se precisar de ajuda com algo, é só dizer!
- Se o aluno não postar por duas semanas consecutivas, o bot envia uma mensagem privada (DM) ao orientador informando quais alunos estão inativos há 2+ semanas.
Sexta-feira, 18:00 (horário de Brasília):
O bot envia uma mensagem privada (DM) ao orientador com um resumo da semana:
🐕 **Relatório da matilha — ASSERT Lab**
Semana de [DD/MM] a [DD/MM]
✅ Postaram check-in (X/Y):
- Fagner Fernandes (phd)
- Alana Fernandes (msc)
- Daniel Oliveira (bsc)
- ...
❌ Não postaram (X/Y):
- Anderson Marinho (msc) — 1 semana sem check-in
- Thiago Rocha (msc) — ⚠️ 3 semanas sem check-in
- ...
📈 Taxa de adesão: XX%
O resumo inclui um contador de semanas consecutivas sem check-in para identificar padrões de inatividade.
Quando um novo membro entra no servidor, o bot envia uma mensagem no canal #boas-vindas-e-regras:
🐕 Bem-vindo(a) à matilha do ASSERT Lab, **[nome do usuário]**!
Leia as regras fixadas neste canal para entender como o servidor funciona.
Se você é orientando(a), seu canal individual está na categoria **Orientações**.
Dúvidas? É só perguntar no #café!
| Comando | Descrição | Quem pode usar |
|---|---|---|
/template |
Exibe o template de check-in para copiar e colar | Todos |
/status |
Mostra o status de check-in da semana (quem postou, quem não) | Orientador |
/resumo @aluno |
Mostra o histórico de check-ins do aluno (últimas 4 semanas) | Orientador |
/uso |
Estatísticas do banco (registros, canais, tamanho, breakdown por nível) | Orientador |
/exportar |
Exporta registros em JSON (DM, filtros: canal/nível/período/tudo) | Orientador |
/limpar |
Remove registros do banco com confirmação obrigatória por botão | Orientador |
/config canais |
Lista os canais monitorados e permite adicionar/remover | Orientador |
/config orientador |
Define quem recebe os resumos semanais (por ID do Discord) | Orientador |
/config horarios |
Ajusta horários de lembrete, cobrança e resumo | Orientador |
/ajuda |
Exibe lista de comandos disponíveis | Todos |
Três comandos administrativos lidam diretamente com a base de check-ins. Todos exigem ORIENTADOR_ID, respondem ephemeral, e nunca são executados em servidores que não o GUILD_ID configurado.
/uso — Estatísticas do banco. Resposta inclui total de registros, número de canais distintos monitorados, semana mais antiga e mais recente (formatadas como DD/MM/YYYY a partir da segunda-feira da semana ISO), tamanho do arquivo SQLite em KB/MB, e breakdown por nível usando os labels configurados em config.json (ex: "Doutorado: X registros, Mestrado: Y registros").
/exportar — Exporta registros como JSON. Parâmetros:
escopo(obrigatório, choice):tudo,canal,nivel,periodo.canal(channel) — quandoescopo=canal.nivel(string choice, derivado deappConfig.prefixos) — quandoescopo=nivel.meses(integer, default 6, 1–240) — quandoescopo=periodo.
O JSON contém metadados (exportacao.data ISO 8601, versao, servidor, filtro), registros[] (canal, nível, label, semana, checkin_realizado boolean, data_checkin ISO ou null, semanas_consecutivas_sem_checkin), e resumo (totais e taxa de adesão). O arquivo é enviado por DM ao orientador como attachment; a resposta no canal é apenas confirmação ephemeral. Acima de 25 MB, o bot recusa o envio e sugere fatiar por canal/período.
/limpar — Remove registros. Parâmetros:
escopo(obrigatório, choice):tudo,canal,canais,nivel.canal(channel),nivel(string choice),lista(string com nomes separados por vírgula) conforme o escopo.
Fluxo de confirmação obrigatório (a deleção NUNCA executa sem pelo menos uma interação por botão):
- Resposta inicial ephemeral com o total impactado e três botões: Exportar antes e limpar, Limpar sem exportar, Cancelar.
- Exportar antes e limpar → executa a exportação por DM e, se bem-sucedida, deleta. Se a exportação falhar (DM bloqueada, arquivo grande), a limpeza é abortada.
- Limpar sem exportar → exibe confirmação final com botões Confirmar limpeza e Cancelar.
- Confirmar limpeza → executa a deleção e responde com o total removido.
- Cancelar ou expiração (TTL de 5 minutos) → descarta o pendente sem deletar.
O estado da intenção de limpeza fica em memória (Map indexado pelo interaction.id do slash command), não no banco. Botões clicados por usuários diferentes do ORIENTADOR_ID são rejeitados.
- O bot identifica automaticamente os canais da categoria "Orientações" pelo nome da categoria.
- Canais cujo nome começa com
phd-,msc-oubsc-são tratados como canais de orientação. - Quando um novo canal é criado nessa categoria, o bot começa a monitorá-lo automaticamente — sem necessidade de configuração manual.
- O nível do aluno (phd/msc/bsc) é extraído do prefixo do canal.
interface ServerConfig {
guildId: string;
orientadorUserId: string; // ID do orientador que recebe resumos
categoriaOrientacoes: string; // Nome ou ID da categoria "Orientações"
canalBoasVindas: string; // ID do canal de boas-vindas
horarioLembrete: string; // Cron expression (default: "0 9 * * 1" — seg 09:00)
horarioCobranca: string; // Cron expression (default: "0 9 * * 3" — qua 09:00)
horarioResumo: string; // Cron expression (default: "0 18 * * 5" — sex 18:00)
timezone: string; // Default: "America/Recife"
}interface CheckinRecord {
canalId: string;
nomeCanal: string; // ex: "msc-alana-fernandes"
nivel: "phd" | "msc" | "bsc";
semana: string; // formato ISO week: "2026-W14"
checkinRealizado: boolean;
dataCheckin: Date | null;
semanasConsecutivasSemCheckin: number;
}Para manter o bot leve e sem custo:
- Opção recomendada: SQLite (arquivo local) via
better-sqlite3. Simples, sem servidor, suficiente para o volume de dados. - Alternativa se precisar de acesso remoto: Turso (SQLite na nuvem, plano gratuito generoso).
- O banco armazena apenas configurações e histórico de check-ins. Não armazena mensagens.
cutucao-bot/
├── src/
│ ├── index.ts # Entry point, client setup
│ ├── config.ts # Carrega configuração do banco
│ ├── database.ts # Setup e queries SQLite
│ ├── commands/
│ │ ├── template.ts # /template
│ │ ├── status.ts # /status
│ │ ├── resumo.ts # /resumo @aluno
│ │ ├── config.ts # /config (subcomandos)
│ │ └── ajuda.ts # /ajuda
│ ├── events/
│ │ ├── ready.ts # Inicialização, registro de comandos
│ │ ├── guildMemberAdd.ts # Boas-vindas
│ │ ├── messageCreate.ts # Detecta check-ins nos canais monitorados
│ │ └── channelCreate.ts # Detecta novos canais na categoria Orientações
│ ├── jobs/
│ │ ├── scheduler.ts # Setup do node-cron
│ │ ├── lembrete.ts # Job de segunda 09:00
│ │ ├── cobranca.ts # Job de quarta 09:00
│ │ └── resumo.ts # Job de sexta 18:00
│ └── utils/
│ ├── canais.ts # Helpers para identificar canais de orientação
│ ├── formatters.ts # Formatação de mensagens
│ └── semana.ts # Cálculo de semana ISO, datas
├── package.json
├── tsconfig.json
├── .env # DISCORD_TOKEN, GUILD_ID, ORIENTADOR_ID
└── README.md
{
"dependencies": {
"discord.js": "^14.x",
"node-cron": "^3.x",
"better-sqlite3": "^11.x",
"dotenv": "^16.x"
},
"devDependencies": {
"typescript": "^5.x",
"@types/node": "^20.x",
"@types/better-sqlite3": "^7.x",
"@types/node-cron": "^3.x"
}
}Guilds— acesso a canais e categoriasGuildMessages— ler mensagens nos canais monitoradosGuildMembers— detectar novos membros (boas-vindas)MessageContent— ler conteúdo das mensagens (para detectar check-ins)
O bot precisa saber se o aluno "fez o check-in" na semana. A regra é simples:
- Qualquer mensagem enviada pelo aluno (não pelo bot) no canal de orientação entre segunda 00:00 e domingo 23:59 conta como check-in da semana.
- Não é necessário que o aluno use o template exato — qualquer mensagem é válida. A ideia é detectar atividade, não policiar formato.
- O bot escuta o evento
messageCreatenos canais monitorados e registra a primeira mensagem da semana como check-in.
1. Cron dispara às 09:00 (America/Recife)
2. Bot lista canais da categoria "Orientações"
3. Para cada canal com prefixo phd-/msc-/bsc-:
a. Envia a mensagem de lembrete com o template
b. Registra no banco que a semana começou (checkinRealizado = false)
4. Log no console: "🐕 Lembretes enviados para X canais"
1. Cron dispara às 09:00 (America/Recife)
2. Bot consulta o banco: quais canais não tiveram check-in desde segunda?
3. Para cada canal sem check-in:
a. Envia mensagem de lembrete gentil
4. Log no console: "🐕 Cobranças enviadas para X canais"
1. Cron dispara às 18:00 (America/Recife)
2. Bot consulta o banco: status de todos os canais na semana
3. Agrupa em "postaram" e "não postaram"
4. Calcula semanas consecutivas sem check-in
5. Formata e envia DM ao orientador
6. Log no console: "🐕 Resumo semanal enviado"
1. Evento guildMemberAdd dispara
2. Bot envia mensagem no canal #boas-vindas-e-regras
3. Log no console: "🐕 Boas-vindas enviadas para [nome]"
# .env
DISCORD_TOKEN=seu_token_aqui
GUILD_ID=id_do_servidor
ORIENTADOR_ID=id_do_usuario_orientador
DATABASE_PATH=./data/cutucao.db
TZ=America/RecifeOpção recomendada: Railway (https://railway.app)
- Plano gratuito: $5/mês de crédito gratuito (mais que suficiente para um bot leve).
- Deploy direto do GitHub.
- Suporte nativo a Node.js/TypeScript.
- Persistent disk para o SQLite.
- Boa confiabilidade e uptime.
| Plataforma | Custo | Observações |
|---|---|---|
| Render (render.com) | Gratuito (com limitações) | Plano free pode suspender após inatividade — ruim para bots que precisam estar sempre online. |
| Fly.io | Gratuito (com limites) | Bom para bots leves. Requer um pouco mais de configuração. |
| Oracle Cloud Free Tier | Gratuito permanente | VM completa gratuita. Mais trabalho de setup, mas sem limites de tempo. |
| VPS própria | ~$5/mês | DigitalOcean, Hetzner. Controle total. |
# 1. Criar repositório no GitHub com o código do bot
# 2. Conectar o repositório ao Railway
# 3. Configurar variáveis de ambiente no dashboard do Railway
# 4. Deploy automático a cada pushAntes de desenvolver, é necessário criar a aplicação do bot:
- Acesse https://discord.com/developers/applications
- Clique em "New Application" → nome: "cutuCÃO"
- Vá em "Bot" no menu lateral
- Clique em "Reset Token" e copie o token (guarde-o com segurança — é a senha do bot)
- Ative os intents em "Privileged Gateway Intents":
- ✅ Server Members Intent
- ✅ Message Content Intent
- Vá em "OAuth2" → "URL Generator"
- Scopes:
bot,applications.commands - Permissions:
Send Messages,Read Message History,View Channels,Use Slash Commands
- Scopes:
- Copie a URL gerada e abra no navegador para adicionar o bot ao servidor
Esta seção define os requisitos de segurança do cutuCÃO. O bot opera em um servidor acadêmico com dados de alunos reais — nomes, padrões de atividade, e potencialmente informações sensíveis sobre progresso acadêmico. A proteção desses dados e do servidor é obrigatória, não opcional.
| Risco | Severidade | Vetor | Mitigação |
|---|---|---|---|
| Vazamento do token do bot | Crítica | Token commitado no GitHub ou exposto em logs | Variáveis de ambiente + .gitignore + rotação periódica |
| Escalação de privilégios via bot | Alta | Bot com permissões excessivas é comprometido | Princípio do menor privilégio nas permissões |
| Injeção de comandos via input do usuário | Alta | Nomes de canais ou parâmetros de comandos manipulados | Sanitização e validação rigorosa de toda entrada |
| Acesso não autorizado a comandos administrativos | Alta | Aluno executa /config ou /status |
Verificação de role/userId em todos os comandos restritos |
| Exposição do banco de dados | Média | Acesso ao arquivo SQLite no servidor de hospedagem | Permissões de arquivo + backup criptografado |
| Abuso de rate limit da API do Discord | Média | Bug ou loop envia mensagens em massa | Rate limiting interno + circuit breaker |
| Dados pessoais em logs | Média | Logs expõem nomes, IDs ou conteúdo de mensagens | Política de logging sem PII |
| Dependências comprometidas (supply chain) | Média | Pacote npm malicioso | Lock file + auditoria + dependências mínimas |
| Bot adicionado a servidor não autorizado | Baixa | Alguém usa o link de convite em outro servidor | Restrição a guild único |
| DM spam via bot | Baixa | Bug faz bot enviar DMs em loop | Limites internos de envio de DMs |
Token do bot:
- NUNCA commitado no repositório. O
.gitignoredeve incluir.env,*.db,data/, e qualquer arquivo de configuração local. - Armazenado exclusivamente via variáveis de ambiente da plataforma de hospedagem (Railway, Fly.io, etc.).
- Rotação do token a cada 6 meses ou imediatamente em caso de suspeita de comprometimento (via Discord Developer Portal → Bot → Reset Token).
- Se o repositório for público, habilitar o GitHub Secret Scanning para detecção automática de tokens expostos.
Arquivo .gitignore obrigatório:
.env
.env.*
*.db
data/
node_modules/
dist/
Variáveis de ambiente no CI/CD:
- Nunca usar variáveis de ambiente em arquivos de configuração do repositório (ex:
docker-compose.ymlcom valores hardcoded). - No Railway/Fly.io, configurar via dashboard — nunca via CLI em logs compartilhados.
Permissões do bot no Discord:
O bot deve solicitar APENAS as permissões estritamente necessárias. A spec original já lista as corretas, mas reforçando o que NÃO deve ter:
| Permissão | Necessária? | Justificativa |
|---|---|---|
Send Messages |
✅ Sim | Enviar lembretes e respostas |
Read Message History |
✅ Sim | Verificar check-ins |
View Channels |
✅ Sim | Listar canais da categoria |
Use Slash Commands |
✅ Sim | Comandos utilitários |
Administrator |
❌ NUNCA | Dá controle total — risco inaceitável |
Manage Channels |
❌ Não | O bot não cria/deleta canais |
Manage Roles |
❌ Não | O bot não gerencia cargos |
Manage Messages |
❌ Não | O bot não deleta mensagens de outros |
Mention Everyone |
❌ Não | O bot não precisa pingar @everyone |
Ban/Kick Members |
❌ NUNCA | Fora do escopo |
Posição do cargo do bot na hierarquia de roles:
- O cargo do cutuCÃO deve ficar abaixo do cargo de Orientador e de qualquer cargo administrativo.
- Deve ficar acima apenas dos cargos que precisa interagir (para enviar mensagens nos canais privados).
Restrição a guild único:
// No evento ready ou em cada comando, verificar:
if (interaction.guildId !== process.env.GUILD_ID) {
return; // Ignora silenciosamente
}O bot deve recusar operar em qualquer servidor que não seja o GUILD_ID configurado. Se alguém obtiver o link de convite e adicionar o bot a outro servidor, ele não deve funcionar.
Toda entrada do usuário é potencialmente maliciosa. Isso inclui:
- Parâmetros de slash commands (
/config horarios,/resumo @aluno) - Nomes de canais (usados para extrair nível phd/msc/bsc)
- Nomes de usuário (usados em mensagens de boas-vindas)
Regras de sanitização:
// Validar cron expressions antes de aceitar
function isValidCron(expr: string): boolean {
// Whitelist de padrões seguros — não aceitar expressões arbitrárias
const safePattern = /^(\d{1,2})\s(\d{1,2})\s\*\s\*\s([0-6])$/;
return safePattern.test(expr);
}
// Sanitizar nomes para exibição em mensagens
function sanitizeDisplayName(name: string): string {
// Remove formatação Markdown que poderia ser usada para injection
return name.replace(/[*_~`|>@#]/g, "");
}
// Validar IDs do Discord (sempre numéricos)
function isValidDiscordId(id: string): boolean {
return /^\d{17,20}$/.test(id);
}
// Validar prefixo de canal
function isOrientacaoChannel(name: string): boolean {
return /^(phd|msc|bsc)-[a-z0-9-]+$/.test(name);
}Proteção contra menções indesejadas:
- Ao construir mensagens que incluam nomes de usuário, usar
allowedMentions: { parse: [] }para evitar que o bot seja usado como vetor de mention spam. - Exceção: o lembrete de check-in que menciona o aluno do canal, onde o mention é intencional.
Os comandos administrativos (/status, /resumo, /config) devem ter verificação dupla:
// Verificação por userId (primária)
if (interaction.user.id !== process.env.ORIENTADOR_ID) {
await interaction.reply({
content: "🐕 Esse comando é só pro orientador!",
ephemeral: true, // Só quem executou vê a resposta
});
return;
}
// Verificação por role (secundária, para futura expansão)
const member = interaction.member as GuildMember;
if (!member.roles.cache.some((role) => role.name === "Orientador")) {
await interaction.reply({ content: "🐕 Acesso negado.", ephemeral: true });
return;
}Respostas ephemeral: Todos os comandos administrativos devem usar ephemeral: true por padrão, para que os dados (lista de quem não fez check-in, configurações) não fiquem visíveis para outros membros do canal.
O cutuCÃO lida com dados de alunos reais. Mesmo sendo um bot interno, as seguintes práticas devem ser seguidas:
Minimização de dados:
- O banco armazena APENAS: ID do canal, nome do canal, semana, e se houve check-in.
- NUNCA armazenar: conteúdo de mensagens, IDs de usuários dos alunos, endereços de e-mail, ou qualquer dado pessoal além do estritamente necessário.
- O nome do aluno é derivado do nome do canal (que é público no servidor), não de dados pessoais adicionais.
Retenção de dados:
- O histórico de check-ins é mantido indefinidamente no banco de dados. Não há deleção automática.
- O campo
visualizacao.meses_resumo_padraoemconfig.jsoncontrola o filtro de visualização no dashboard futuro — não é usado para deletar dados. - Quem precisar limpar o histórico manualmente pode usar um cliente SQLite diretamente no arquivo de banco.
Logging seguro:
- Logs no console NUNCA devem incluir: conteúdo de mensagens, tokens, IDs completos de usuários.
- Formato seguro de log:
"🐕 Check-in registrado em canal msc-*** (semana 2026-W14)"— truncar nomes nos logs. - Em produção, usar nível de log
info(semdebugque poderia expor dados).
Segurança do SQLite:
// Habilitar WAL mode para integridade em caso de crash
db.pragma("journal_mode = WAL");
// Usar prepared statements SEMPRE — nunca concatenar strings em queries
// ✅ CORRETO
db.prepare("SELECT * FROM checkins WHERE canalId = ?").get(canalId);
// ❌ NUNCA
db.exec(`SELECT * FROM checkins WHERE canalId = '${canalId}'`);Backup:
- Configurar backup automático semanal do arquivo
.db(no Railway, usar o persistent disk). - Se usar Turso, os backups são automáticos.
- O arquivo de banco NUNCA deve estar no repositório Git.
Permissões de arquivo:
- O arquivo
.dbdeve ter permissões600(leitura/escrita apenas pelo dono do processo).
Um bug ou condição de corrida pode fazer o bot enviar centenas de mensagens em sequência, o que violaria os rate limits do Discord e potencialmente resultaria no banimento do bot.
// Rate limiter interno
const MESSAGE_LIMIT = 20; // Máximo de mensagens por ciclo de job
const DM_LIMIT = 3; // Máximo de DMs por ciclo
const COOLDOWN_MS = 1000; // Delay entre mensagens
async function enviarComRateLimit(
canais: Channel[],
mensagem: string,
): Promise<void> {
let enviados = 0;
for (const canal of canais) {
if (enviados >= MESSAGE_LIMIT) {
console.warn("🐕 Rate limit interno atingido. Abortando envio.");
break;
}
await canal.send(mensagem);
enviados++;
await sleep(COOLDOWN_MS);
}
}Circuit breaker: Se qualquer job falhar 3 vezes consecutivas, desabilitar temporariamente e notificar o orientador via DM.
Dependências mínimas (já definido na spec):
- Apenas 4 dependências de produção:
discord.js,node-cron,better-sqlite3,dotenv. - Quanto menos dependências, menor a superfície de ataque.
Práticas obrigatórias:
- Usar
package-lock.jsone sempre commitar o lock file. - Rodar
npm auditantes de cada deploy e corrigir vulnerabilidades de severidade alta/crítica. - Fixar versões major das dependências (já feito com
^14.xetc.). - Habilitar Dependabot ou Renovate no GitHub para alertas automáticos de vulnerabilidades.
Se o repositório for público (open source):
- Garantir que
.env,*.dbedata/estejam no.gitignoreANTES do primeiro commit. - Habilitar GitHub Secret Scanning e Push Protection para bloquear commits com tokens.
- Usar GitHub Environments com proteção de branch para secrets de deploy.
- O
README.mddeve instruir contribuidores a NUNCA commitar credenciais.
Branch protection:
- Branch
mainprotegida: sem push direto, requer PR. - Isso previne que um contribuidor (aluno) acidentalmente exponha credenciais.
Railway / Fly.io / qualquer PaaS:
- Habilitar 2FA (autenticação de dois fatores) na conta da plataforma de hospedagem.
- Habilitar 2FA na conta do Discord Developer Portal.
- Habilitar 2FA na conta do GitHub.
- Revisar periodicamente quem tem acesso ao projeto na plataforma de hospedagem.
- Não compartilhar credenciais de acesso com alunos — se um aluno precisar contribuir com código, ele faz via PR no GitHub, sem acesso ao deploy.
Se houver suspeita de comprometimento:
- Imediato: Rotacionar o token do bot no Discord Developer Portal (Reset Token).
- Em seguida: Verificar logs da plataforma de hospedagem para atividade anormal.
- Depois: Revisar histórico de commits no GitHub para exposição acidental de credenciais.
- Se o banco foi comprometido: O impacto é baixo (sem dados pessoais sensíveis), mas regenerar o banco e notificar os membros do servidor.
- Documentar: Registrar o incidente e as ações tomadas para referência futura.
Antes de colocar o cutuCÃO em produção, verificar todos os itens:
-
.envestá no.gitignoree NÃO foi commitado em nenhum ponto do histórico - Token do bot está configurado APENAS via variáveis de ambiente da plataforma
- Bot tem APENAS as permissões listadas na seção 9.3 (sem Administrator)
- Cargo do bot está posicionado corretamente na hierarquia de roles
- Todos os comandos administrativos verificam
ORIENTADOR_IDantes de executar - Respostas de comandos administrativos usam
ephemeral: true - Todas as queries SQL usam prepared statements
- Nenhum log expõe conteúdo de mensagens ou tokens
- Rate limiter interno está implementado nos jobs de envio
- Restrição de guild único está implementada
-
npm auditexecutado sem vulnerabilidades de severidade alta -
package-lock.jsoncommitado no repositório - 2FA habilitado no Discord Developer Portal, GitHub e plataforma de hospedagem
- GitHub Secret Scanning habilitado (se repositório público)
- Backup do banco de dados configurado
- Retenção de dados — histórico mantido indefinidamente (sem deleção automática)
- Setup do projeto (TypeScript, discord.js, SQLite)
- Implementar
.gitignorecom proteção de credenciais (seção 9.2) - Restrição de guild único (seção 9.3)
- Detecção automática de canais de orientação
- Lembrete de check-in (segunda 09:00) com rate limiting (seção 9.8)
- Detecção de mensagens como check-in
- Cobrança automática (quarta 09:00)
- Resumo semanal para o orientador (sexta 18:00) com ephemeral DM
- Controle de acesso a comandos (seção 9.5)
- Sanitização de entrada em todos os inputs (seção 9.4)
- Deploy no Railway com 2FA habilitado
- Executar checklist de segurança (seção 9.13)
- Mensagem de boas-vindas com nome sanitizado
- Comando
/template - Comando
/status(ephemeral) - Comando
/resumo #canal(ephemeral) - Comando
/ajuda
-
config.jsonexterno: categorias, prefixos+labels, canal de boas-vindas, horários, escalação, visualização - Configuração de canais monitorados via
config.json(categorias + prefixos) - Configuração de horários via
config.jsoncom validação de cron na inicialização - Comando
/config canaispara reconfiguração em tempo real (sem reiniciar) - Comando
/config orientadorcom validação de ID - Comando
/config horariospara reconfiguração em tempo real (sem reiniciar) - Detecção automática de novos canais na categoria
- Habilitar Dependabot/Renovate no GitHub
- Dashboard web simples para visualizar status dos check-ins
- Integração com Google Calendar para próximas reuniões
- Notificação de marcos/deadlines próximos (qualificação, defesa)
- Métricas de longo prazo (taxa de adesão por mês, evolução)
- M1: Externalizar mensagens para arquivo JSON + serviço de templates
- M2: Implementar padrão Repository para checkins e configurações
- M3: Container de injeção de dependências
- M4: Testes automatizados com Vitest (unitários + integração)
- M5: CI/CD com GitHub Actions (typecheck, testes, audit)
- M6: Logs estruturados + alertas por DM em falhas de jobs
- Comando
/uso— estatísticas do banco (registros, canais, tamanho, breakdown por nível) - Comando
/exportar— exportação em JSON com filtros (canal/nível/período/tudo) entregue por DM - Comando
/limpar— remoção de registros com confirmação obrigatória por botão (com opção de exportar antes) - Repositório expandido:
contarRegistros,contarRegistrosPorNivel,registroMaisAntigo,registroMaisRecente,exportarRegistros,limparRegistros(todos via prepared statements) - Testes para os novos métodos do repositório, helpers de exportação, e fluxo de confirmação do
/limpar
O arquivo config.json na raiz do projeto (versionado no Git) permite customizar o bot sem editar código:
{
"categorias": ["Orientações"],
"prefixos": { "phd": "Doutorado", "msc": "Mestrado", "bsc": "Graduação" },
"canal_boas_vindas": "boas-vindas-e-regras",
"horarios": {
"lembrete": "0 9 * * 1",
"cobranca": "0 9 * * 3",
"resumo": "0 18 * * 5"
},
"escalacao": {
"semanas_para_cobranca_dm": 2,
"semanas_para_alerta": 3
},
"visualizacao": {
"semanas_historico_padrao": 4,
"meses_resumo_padrao": 6
}
}categorias: lista de nomes de categorias Discord a monitorar (suporta múltiplas).prefixos: mapeamento prefixo→label; as chaves determinam os prefixos válidos de canais, os valores são exibidos nos relatórios ("Doutorado" em vez de "phd").canal_boas_vindas: nome do canal (sem#) para mensagens de boas-vindas.horarios: expressões cron para os três jobs. Expressão inválida impede o bot de iniciar.escalacao: limiares de inatividade para envio de DM ao orientador e exibição de⚠️ .visualizacao: número de semanas exibidas pelo/resumo #canale meses para filtro futuro no dashboard.
Se config.json não existir, o bot usa os valores padrão acima (retrocompatibilidade total).
- M7: Dashboard web para visualização de status e métricas
- Edição de mensagens/templates via dashboard
- Configuração de horários e parâmetros via dashboard
- Autenticação OAuth2 via Discord
- Métricas históricas de adesão
Ao iniciar o desenvolvimento com Claude Code, use este documento como contexto. Sugestão de prompt inicial:
Leia o arquivo SPEC.md que contém a especificação completa do cutuCÃO —
o bot Discord do ASSERT Lab para acompanhamento de orientandos acadêmicos.
O cutuCÃO é um vira-lata caramelo com megafone que cutuca os alunos
para fazerem seus check-ins semanais.
Comece pela Fase 1 (MVP): setup do projeto, detecção de canais,
lembrete de check-in, detecção de mensagens, cobrança automática,
e resumo semanal.
IMPORTANTE: A seção 9 (Segurança) é obrigatória. Implemente todas
as mitigações desde o início — especialmente: proteção de credenciais,
princípio do menor privilégio, sanitização de entrada, controle de acesso
a comandos, rate limiting interno, e prepared statements no SQLite.
Use discord.js v14, node-cron, better-sqlite3, e TypeScript.
Siga a arquitetura de diretórios descrita na spec.
O tom das mensagens do bot deve ser amigável e levemente humorado,
com referências caninas quando apropriado.
- O cutuCÃO substitui completamente o Carl-bot para as funcionalidades de check-in. O Carl-bot pode ser removido do servidor após o bot estar estável.
- O bot não armazena conteúdo das mensagens — apenas registra se houve atividade no canal. Isso respeita a privacidade dos alunos.
- O logo do cutuCÃO (vira-lata caramelo com megafone) deve ser usado como avatar do bot no Discord Developer Portal.
- O código será open source e pode ser adaptado por outros grupos de pesquisa.