From e2c0e703eac51d45f0d63b2590148290505adbb4 Mon Sep 17 00:00:00 2001 From: Bxio Date: Sat, 10 Oct 2026 19:28:05 +0100 Subject: [PATCH] Clarify supported templates and finish release documentation --- README.md | 54 ++++++++++++++++++++++---------------------- public/app.js | 2 +- public/fonts/OFL.txt | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 25359ac..c1a8971 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,51 @@ -# TS6 Botflow +# TS6 Botflow -Painel local XanaX para gerir bots TeamSpeak, com login Discord e editor visual de flows. Mant?m os log?tipos originais e o tema flat roxo. As pastas dos projetos de origem permanecem intactas. +Painel local XanaX para gerir bots TeamSpeak, com login Discord e editor visual de flows. Mantém os logótipos originais e o tema flat roxo. As pastas dos projetos de origem permanecem intactas. ## Funcionalidades -- Dashboard com bots, flows e execu??es guardados na base de dados. -- Editor com blocos arrast?veis, liga??es, condi??es, propriedades, zoom e grava??o. -- Blocos: temporizador, in?cio manual, informa??es do servidor, clientes, ordena??o, limite, prepara??o de texto, edi??o da descri??o de canal, mensagem, espera e registo. -- Simula??o identificada com dados de exemplo, sem enviar pedidos ao TeamSpeak. -- Execu??o manual ou por temporizador, com hist?rico e resultado de cada passo. Um bot n?o executa duas vezes em simult?neo. -- Liga??es WebQuery, teste de liga??o e API keys cifradas em repouso. -- Vari?veis por bot, acess?veis como `{{variables.nome}}`. Outros valores: `{{server.virtualserver_clientsonline}}`, `{{count}}`, `{{top}}`, `{{text}}` e `{{time}}`. +- Dashboard com bots, flows e execuções guardados na base de dados. +- Editor com blocos arrastáveis, ligações, condições, propriedades, zoom e gravação. +- Blocos: temporizador, início manual, informações do servidor, clientes, ordenação, limite, preparação de texto, edição da descrição de canal, mensagem, espera e registo. +- Simulação identificada com dados de exemplo, sem enviar pedidos ao TeamSpeak. +- Execução manual ou por temporizador, com histórico e resultado de cada passo. Um bot não executa duas vezes em simultâneo. +- Ligações WebQuery, teste de ligação e API keys cifradas em repouso. +- Variáveis por bot: `{{variables.nome}}`. Outros valores: `{{server.virtualserver_clientsonline}}`, `{{count}}`, `{{top}}`, `{{text}}` e `{{time}}`. -O primeiro arranque cria um rascunho **Estat?sticas**, desativado, para atualizar a descri??o do canal **21** a cada 30 segundos. O top inicial ordena as pessoas atualmente online pela data de entrada; n?o ? um ranking hist?rico de tempo acumulado. +O primeiro arranque cria um rascunho **Estatísticas**, desativado, para atualizar a descrição do canal **21** a cada 30 segundos. O top inicial ordena as pessoas atualmente online pela data de entrada; não é um ranking histórico de tempo acumulado. -Esta vers?o n?o inclui reprodu??o de m?sica, eventos SSH, webhooks, edi??o de permiss?es nem gest?o de perfis pelo painel. Cada bot tem um flow. O editor disponibiliza os blocos implementados acima. +Esta versão não inclui reprodução de música, eventos SSH, webhooks, edição de permissões nem gestão de perfis pelo painel. Cada bot tem um flow. O editor disponibiliza os blocos implementados acima. ## Docker / Portainer -Para Web editor com imagem constru?da no Gitea, consultar [deploy/README.md](deploy/README.md) e usar [deploy/bot-stack.yaml](deploy/bot-stack.yaml). Cada push em `main` executa os testes e publica a imagem. Depois do workflow verde, atualizar a stack no Portainer com download da imagem e recria??o do container. Restart sozinho n?o descarrega a nova imagem. +Para Web editor com imagem construída no Gitea, consultar [deploy/README.md](deploy/README.md) e usar [deploy/bot-stack.yaml](deploy/bot-stack.yaml). Cada push em `main` executa os testes e publica a imagem. Depois do workflow verde, atualizar a stack no Portainer com download da imagem e recriação do container. Restart sozinho não descarrega a nova imagem. -Para construir localmente com Docker Compose, usar `compose.yaml`. A aplica??o escuta na porta 3080 e preserva os dados num volume montado em `/app/data`. +Para construir localmente com Docker Compose, usar `compose.yaml`. A aplicação escuta na porta 3080 e preserva os dados num volume montado em `/app/data`. -Vari?veis de ambiente: +Variáveis de ambiente: -- `APP_URL`: endere?o do navegador, por exemplo `http://192.168.88.99:3080`. -- `DISCORD_CLIENT_ID`: ID da aplica??o Discord. -- `DISCORD_CLIENT_SECRET`: segredo OAuth da aplica??o. -- `OWNER_DISCORD_ID`: ID num?rico da conta do dono. -- `ALLOWED_DISCORD_IDS`: IDs de leitores separados por v?rgulas. Por defeito, `723613831196311603`; valor vazio remove todos os leitores. +- `APP_URL`: endereço do navegador, por exemplo `http://192.168.88.99:3080`. +- `DISCORD_CLIENT_ID`: ID da aplicação Discord. +- `DISCORD_CLIENT_SECRET`: segredo OAuth da aplicação. +- `OWNER_DISCORD_ID`: ID numérico da conta do dono. +- `ALLOWED_DISCORD_IDS`: IDs de leitores separados por vírgulas. Por defeito, `723613831196311603`; valor vazio remove todos os leitores. No Discord Developer Portal, adicionar o redirect exato `http://192.168.88.99:3080/auth/discord/callback`, ajustando ao `APP_URL`. O navegador e o container precisam de Internet para o OAuth; o site pode continuar apenas na rede local. -O **Dono** pode configurar, guardar, simular e executar. O **Leitor** pode consultar. Outras contas s?o recusadas; as permiss?es s?o tamb?m verificadas no servidor. +O **Dono** pode configurar, guardar, simular e executar. O **Leitor** pode consultar. Outras contas são recusadas; as permissões são também verificadas no servidor. ## Ligar ao TeamSpeak -Entrar com a conta do dono. Em **Liga??es**, configurar o endere?o WebQuery HTTP/HTTPS, o ID do servidor virtual e a API key TeamSpeak. Por exemplo, `http://teamspeak6:10080` funciona quando ambos os containers partilham uma rede Docker com esse nome resolv?vel. Testar a liga??o, escolher esse servidor na configura??o do bot, simular e s? depois ativar. +Entrar com a conta do dono. Em **Ligações**, configurar o endereço WebQuery HTTP/HTTPS, o ID do servidor virtual e a API key TeamSpeak. Por exemplo, `http://teamspeak6:10080` funciona quando ambos os containers partilham uma rede Docker com esse nome resolvível. Testar a ligação, escolher esse servidor na configuração do bot, simular e só depois ativar. -O temporizador aceita intervalos a partir de um segundo. A execu??o seguinte aguarda a conclus?o da anterior; pedidos de rede e blocos de espera podem prolongar o intervalo efetivo. A configura??o de permiss?es e prote??o contra flood do TeamSpeak deve permitir os pedidos do bot. +O temporizador aceita intervalos a partir de um segundo. A execução seguinte aguarda a conclusão da anterior; pedidos de rede e blocos de espera podem prolongar o intervalo efetivo. A configuração de permissões e proteção contra flood do TeamSpeak deve permitir os pedidos do bot. -Sess?es e flows usam SQLite no volume. A chave de cifra das API keys fica em `/app/data/credentials.key`; preservar o volume completo nas c?pias de seguran?a. O hist?rico ret?m as ?ltimas 500 execu??es. N?o adicionar `.env` nem dados ao Git. +Sessões e flows usam SQLite no volume. A chave de cifra das API keys fica em `/app/data/credentials.key`; preservar o volume completo nas cópias de segurança. O histórico retém as últimas 500 execuções. Não adicionar `.env` nem dados ao Git. -## Desenvolvimento e valida??o +## Desenvolvimento e validação -Node 22.15+, mesmas vari?veis de ambiente e `npm start`. Executar `npm test` para verificar autentica??o, restri??es de acesso, persist?ncia, simula??o e execu??o WebQuery com transporte de teste. O build Docker executa os testes antes da publica??o. +Node 22.15+, mesmas variáveis de ambiente e `npm start`. Executar `npm test` para verificar autenticação, restrições de acesso, persistência, simulação e execução WebQuery com transporte de teste. O build Docker executa os testes antes da publicação. -O editor foi verificado em navegador desktop e m?vel. A execu??o no TeamSpeak real depende da liga??o e API key configuradas na instala??o; os testes locais n?o alteram o servidor real. +O editor foi verificado em navegador desktop e móvel. A execução no TeamSpeak real depende da ligação e API key configuradas na instalação; os testes locais não alteram o servidor real. -A fonte Public Sans ? distribu?da em `public/fonts` com a licen?a OFL. Os log?tipos foram copiados sem altera??es do projeto existente. +A fonte Public Sans é distribuída em `public/fonts` com a licença OFL. Os logótipos foram copiados sem alterações do projeto existente. diff --git a/public/app.js b/public/app.js index 2aa3d8b..135dd1d 100644 --- a/public/app.js +++ b/public/app.js @@ -21,7 +21,7 @@ function navigate(name){if(dirty&&!confirm('Sair sem guardar as alterações do function botCard(bot){const runs=data.executions.filter(e=>e.botId===bot.id);return `
${icon(bot.color==='red'?'shield':bot.color==='orange'?'music':bot.color==='cyan'?'users':'bot')}

${esc(bot.name)}

${bot.enabled?'Ativo':'Rascunho'}

${esc(bot.description||'Personaliza o flow deste bot.')}

${icon('flow')}1 flow${icon('play')}${runs.length} execuções${icon('clock')}${ago(runs[0]?.startedAt)}
`;} function wireBots(){document.querySelectorAll('[data-open]').forEach(b=>b.onclick=()=>{botId=b.dataset.open;navigate('editor');});document.querySelectorAll('[data-config]').forEach(b=>b.onclick=()=>configureBot(b.dataset.config));const create=document.querySelector('#create-bot');if(create)create.onclick=()=>newBot();document.querySelectorAll('[data-toggle]').forEach(b=>b.onclick=async()=>{try{const bot=data.bots.find(bot=>bot.id===b.dataset.toggle);await api(`/api/bots/${bot.id}`,{method:'PUT',body:JSON.stringify({enabled:!bot.enabled})});await refresh();navigate(page);toast('Estado atualizado.');}catch(e){toast(e.message,true);}});} function dashboardPage(){const today=new Date().toLocaleDateString('pt-PT',{timeZone:'Europe/Lisbon'});document.querySelector('#content').innerHTML=heading('Dashboard','Uma visão geral dos teus bots e automações.',button('create-bot','Criar bot'))+`
${[['Bots ativos',data.bots.filter(b=>b.enabled).length,'bot','green'],['Execuções hoje',data.executions.filter(e=>new Date(e.startedAt).toLocaleDateString('pt-PT',{timeZone:'Europe/Lisbon'})===today).length,'play','cyan'],['Flows guardados',data.bots.length,'flow','purple'],['Servidores configurados',data.connections.length,'link','orange']].map(([label,n,i,c])=>`
${icon(i)}
${label}${n}
`).join('')}

Os teus bots

${data.bots.map(botCard).join('')}

Atividade recente

${data.executions.slice(0,4).map(runRow).join('')||empty('clock','Ainda sem execuções','Simula um flow para acompanhar aqui cada passo.')}
${icon('flow')}

O teu primeiro flow

O rascunho Estatísticas prepara a descrição do canal 21. Escolhe a ligação e simula antes de ativar.

`;wireBots();document.querySelector('#all-bots').onclick=()=>navigate('bots');document.querySelector('#all-runs').onclick=()=>navigate('executions');document.querySelector('#first-flow').onclick=()=>navigate('editor');} -function botsPage(){document.querySelector('#content').innerHTML=heading('Bots','Gere os teus bots do TeamSpeak num só lugar.',button('create-bot','Criar bot'))+`
${data.bots.length} bots · ${data.bots.filter(b=>b.enabled).length} ativos
${data.bots.map(botCard).join('')}

Começar um novo bot

Rascunhos para personalizar no editor
${[['Estatísticas','green','bot'],['Moderação','red','shield'],['Boas-vindas','cyan','users'],['Música','orange','music']].map(([name,c,i])=>``).join('')}
`;wireBots();document.querySelector('#search-bots').oninput=e=>{document.querySelector('#bot-list').innerHTML=data.bots.filter(b=>b.name.toLowerCase().includes(e.target.value.toLowerCase())).map(botCard).join('');wireBots();};document.querySelectorAll('[data-template]').forEach(b=>b.onclick=()=>newBot(b.dataset.template));} +function botsPage(){document.querySelector('#content').innerHTML=heading('Bots','Gere os teus bots do TeamSpeak num só lugar.',button('create-bot','Criar bot'))+`
${data.bots.length} bots · ${data.bots.filter(b=>b.enabled).length} ativos
${data.bots.map(botCard).join('')}

Começar um novo bot

Rascunhos para personalizar no editor
${[['Estatísticas','green','bot'],['Personalizado','purple','flow']].map(([name,c,i])=>``).join('')}
`;wireBots();document.querySelector('#search-bots').oninput=e=>{document.querySelector('#bot-list').innerHTML=data.bots.filter(b=>b.name.toLowerCase().includes(e.target.value.toLowerCase())).map(botCard).join('');wireBots();};document.querySelectorAll('[data-template]').forEach(b=>b.onclick=()=>newBot(b.dataset.template));} function modal(title,body,submit){const dialog=document.querySelector('#dialog');dialog.innerHTML=``;dialog.showModal();document.querySelector('#close-dialog').onclick=document.querySelector('#cancel').onclick=()=>dialog.close();document.querySelector('#modal-form').onsubmit=async e=>{e.preventDefault();try{await submit(new FormData(e.target));dialog.close();await refresh();dirty=false;navigate(page);toast('Guardado.');}catch(error){toast(error.message,true);}};} function newBot(name='Novo bot'){modal('Criar bot',`

O bot começa desativado. Usa o flow de estatísticas como base ou substitui os blocos.

`,async form=>{const bot=await api('/api/bots',{method:'POST',body:JSON.stringify(Object.fromEntries(form))});botId=bot.id;page='editor';});} function configureBot(id){botId=id;const bot=selectedBot();modal('Configuração do bot',`
Geral

Escolhe uma ligação para executar o flow no TeamSpeak.

`,f=>api(`/api/bots/${bot.id}`,{method:'PUT',body:JSON.stringify(Object.fromEntries(f))}));document.querySelector('#config-vars').onclick=()=>{document.querySelector('#dialog').close();navigate('variables');};} diff --git a/public/fonts/OFL.txt b/public/fonts/OFL.txt index 9eef1e9..547185a 100644 --- a/public/fonts/OFL.txt +++ b/public/fonts/OFL.txt @@ -18,7 +18,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The