Communitygithub.com

terryso/wechat-channels-video

Publique um rascunho de vídeo do WeChat Channels usando Ego Lite via ego-browser — primeiro verifique o login (as sessões expiram rapidamente), envie o vídeo, preencha descrição/título curto/tópicos/localização/capa, salve apenas como rascunho. Falha suavemente se o Channels estiver desconectado e informa o usuário para escanear o QR dentro do Ego. Uso pessoal. Use quando o usuário solicitar publicação no 视频号 / Channels assistant.

O que é wechat-channels-video?

Esta skill permite que agentes de IA (como Claude Code, Cursor, Codex) publiquem rascunhos de vídeo no WeChat Channels (视频号) de forma automatizada, usando o navegador headless **Ego Lite** e o utilitário `ego-browser`. O fluxo principal envolve: verificar o estado de login (que expira rapidamente), iniciar um servidor de arquivos local para servir o vídeo, navegar pela interface do wujie (micro front-end) para acessar a página de publicação, fazer upload do vídeo via DataTransfer, preencher os campos de descrição, título curto, tópicos e localização usando `execCommand` e setters nativos, e, por fim, salvar o rascunho. A skill salva **apenas rascunhos** — a publicação final (que requer escaneamento de QR code) é deixada para o humano. O design da skill é modular: scripts Node.js separados gerenciam a verificação de login e o servidor de arquivos, enquanto o agente interage com a página usando as APIs do Ego Lite (`page.snapshot`, `page.evaluate`, `page.mouse.click`, `page.waitForFileChooser`). Isso evita scripts monolíticos e frágeis. A documentação inclui uma tabela de códigos de saída para o script de login, um guia de referência rápida das APIs do Ego Lite, e uma lista detalhada de armadilhas (pitfalls) a evitar, como a necessidade de usar a navegação pela UI em vez de URLs diretos, e a limitação de que cliques em texto/referências dentro do wujie são interceptados, exigindo o uso de coordenadas ou `evaluate`. A skill é robusta para uso pessoal e lida com falhas de login de forma graciosa, orientando o usuário a escanear o QR code.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/terryso/ego-skills/tree/main/skills/wechat-channels-video

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que wechat-channels-video faz?

Use a sessão do Ego Lite para salvar rascunhos de vídeo no 视频号助手. Não clique em "Publicar" — a publicação via QR é feita apenas por humanos.

Save a WeChat Channels draft through Ego Lite. Never click Publish — QR publish is human-only.

Instalação do Ego

Primeiro, instale o Ego Lite e certifique-se de que ego-browser está no PATH. Se estiver ausente, falhe suavemente / informe o usuário para instalá-lo. Não use um navegador comum.

Antes de cada publicação: Verificação de Login (Obrigatório)

O estado de login do WeChat Channels expira rapidamente. O agente deve executar o seguinte antes de fazer upload ou preencher formulários:

node scripts/check-login.mjs
# Para manter a janela aberta para o usuário escanear o QR:
node scripts/check-login.mjs --keep
Código de saídaSignificadoAção do Agente
0LogadoInforme login_tip ao usuário e prossiga com o fluxo
2Não logado / ego-browser ausenteExiba how_to_login / how_to_login_zh, pare. Peça ao usuário para escanear o QR no Ego e depois execute check-login novamente
1Outro erroReporte o erro JSON, não finja que foi publicado

Consulte references/login.md para mais detalhes. Não implemente uma skill separada de verificação de cookies.

Entrada (Genérica, sem caminhos de produto locais)

A parte chamadora fornece os campos de vídeo e texto. Consulte references/inputs.md. Esta skill não fixa nenhum diretório de negócios ~/….

Mínimo necessário: caminho local do video, description, short_title. Opcionais: tags, cover, localização/original/atividade.

Scripts Leves via CLI (Parte Estável)

ego-browser nodejs não repassa argumentos de shell; check-login já está encapsulado usando um entry temporário + ego-browser nodejs -e.

# Na raiz do repositório
node skills/wechat-channels-video/scripts/check-login.mjs
node skills/wechat-channels-video/scripts/check-login.mjs --keep
node skills/wechat-channels-video/scripts/serve-file.mjs --file /caminho/absoluto/para/video.mp4 --port 18765

# No diretório da skill instalada, também pode usar caminhos relativos
node scripts/check-login.mjs
node scripts/serve-file.mjs --file /caminho/absoluto/para/video.mp4 --port 18765
  • check-login: Única barreira de login; termina com finish({ keep: [] }) por padrão (exceto com --keep).
  • serve-file: Servidor de arquivos local com CORS para fetch da página; imprime { url, bytes, port }, termina com SIGINT. Para vídeos grandes, não use base64.

Não transforme toda a interface de publicação em um script gigante: wujie, cliques em coordenadas, waitForFileChooser para capa exigem que o agente veja snapshots/screenshots. Os scripts cuidam de tarefas determinísticas (login e upload de arquivos); o preenchimento do formulário segue os passos do agente abaixo.

Referência Rápida da API ego-browser

Cada ego-browser nodejs é um novo processo; TaskSpace / Page persistem entre chamadas, mas variáveis JS não.

APIDescrição
taskSpace(name|id)Um espaço por objetivo do usuário; anote o spaceId para reutilizar
task.page("p1")Reuse a página inicial com goto, não abra páginas desnecessariamente
page.snapshot()Observação (consegue ver através do shadow DOM do wujie); cliques dentro do wujie são frequentemente bloqueados
page.evaluate(fn, arg)Aceita apenas um argumento serializável em JSON (textos longos vão aqui)
page.mouse.click(x, y)Útil para modais dentro do wujie
page.waitForFileChooser()Único caminho confiável para upload de capa
task.handOff()Passa o controle para o humano (publicação via QR / login presencial)
task.finish({ keep: [] })Finaliza e fecha o espaço; use handOff se o humano precisar continuar, não ocupe a máquina por muito tempo

Importante: snapshot enxerga o shadow DOM do wujie; cliques em texto/referência disparam "intercepts pointer events". Ações dentro do wujie usem .click() dentro de evaluate ou cliques por coordenada mouse.click.

Fluxo do Agente (Após Login Bem-sucedido)

0. Navegar para a Página de Publicação

Não vá diretamente para /platform/post/create (carregamento preguiçoso causa tela branca). Siga o caminho pela UI:

Página inicial → Coluna esquerda "Gestão de Conteúdo" → "Vídeos" → Botão "Publicar Vídeo" à direita

Pronto: dentro do wujie, aparecem os textos "Descrição do vídeo" e "Título curto", além de um input[type=file][accept*="video"] oculto e o botão "Salvar rascunho".

const state = await page.evaluate(() => {
  const app = document.querySelector('wujie-app');
  if (!app?.shadowRoot) return { wujie: false };
  const t = app.shadowRoot.textContent || '';
  return {
    wujie: true,
    hasDesc: t.includes('视频描述'),
    hasShort: t.includes('短标题'),
    hasUpload: !!app.shadowRoot.querySelector('input[type=file][accept*="video"]'),
    hasSave: t.includes('保存草稿'),
  };
});

1. Upload do Vídeo

Primeiro, inicie serve-file (porta padrão 18765). Depois, na página, use fetch + DataTransfer para injetar no input de arquivo oculto (o componente de vídeo não reforça isTrusted; a capa, sim):

const r = await page.evaluate(async (p) => {
  const root = document.querySelector('wujie-app').shadowRoot;
  const input = root.querySelector('input[type=file][accept*="video"]');
  const resp = await fetch(p.url);
  const dt = new DataTransfer();
  dt.items.add(new File([await resp.blob()], p.name, { type: 'video/mp4' }));
  input.files = dt.files;
  input.dispatchEvent(new Event('input', { bubbles: true }));
  input.dispatchEvent(new Event('change', { bubbles: true }));
  return 'injected ' + dt.files[0].size;
}, { url: 'http://127.0.0.1:18765/video.mp4', name: 'video.mp4' });

Verificação apenas pelo DOM: deve aparecer o card de prévia "Compartilhar/Curtir/Comentar" e a barra de progresso em 100%. O recurso de performance não captura o POST de upload. Durante o upload, não preencha formulários; se o wujie ficar null momentaneamente, aguarde e tente novamente, não recarregue.

1.5 Ponto de Verificação: Salvar Rascunho Primeiro

Assim que o upload terminar, imediatamente clique em "Salvar rascunho" uma vez. Depois preencha descrição/título curto/localização e salve novamente. Isso evita refazer o upload de arquivos grandes se algo der errado.

2. Descrição + Tópicos

A descrição é um .input-editor (não contenteditable). Use execCommand; o texto é passado como segundo argumento do evaluate:

await page.evaluate((p) => {
  const ed = document.querySelector('wujie-app').shadowRoot.querySelector('.input-editor');
  ed.focus();
  document.execCommand('selectAll', false, null);
  document.execCommand('delete', false, null);
  document.execCommand('insertText', false, p.desc);
}, { desc });
await page.evaluate((p) => {
  const ed = document.querySelector('wujie-app').shadowRoot.querySelector('.input-editor');
  ed.focus();
  document.execCommand('insertParagraph');
  document.execCommand('insertText', false, p.tags);
  ed.blur();
}, { tags });

3. Título Curto

É um <input> verdadeiro, deve usar setter nativo; verifique lendo input.value (innerText não contém o valor do input).

4. Localização

Normalmente já vem com a cidade. Abordagem segura: mouse.click para abrir o seletor → snapshot/screenshot → clique em "Não mostrar localização" → releia o campo para confirmar. Coordenadas no evaluate podem clicar em camadas ocultas.

5. Salvar Rascunho (Ponto Final)

Clique em "Salvar rascunho". Sinal de sucesso: texto "Salvo" aparece + botão inferior fica cinza. Depois, navegue para a lista de rascunhos para verificar. Não saia do editor antes de o rascunho aparecer na lista.

Opcional: declarar como original, pesquisa de atividade (use setter nativo para escrever o termo de busca; se não encontrar, escolha "Não participar da atividade").

6. Capa (Opcional)

Único caminho confiável: waitForFileChooser() + clique confiável em "Enviar capa" + chooser.setFiles(<caminho absoluto>). A abordagem de sintetizar input.files / usar caixa de diálogo do sistema já foi testada e falhou. Após o botão de confirmação parar de girar e a prévia não voltar ao quadro padrão, salve o rascunho.

7. Finalização

  • Pare o serve-file (libere a porta).
  • Se o humano precisar publicar: use handOff() na lista de rascunhos.
  • Caso contrário, finish({ keep: [] }), não ocupe o espaço por muito tempo.

Lista de Armadilhas

  1. Shadow DOM do wujie: consultas a partir de wujie-app.shadowRoot.
  2. URL direta create causa tela branca: entre pela UI na página de publicação.
  3. Cliques em texto/referência dentro do wujie são bloqueados → use evaluate ou clique por coordenada.
  4. Textos longos devem ser passados apenas como parâmetro do evaluate.
  5. Descrição use execCommand; título curto use setter nativo.
  6. Localização: clique por coordenada em duas etapas + releitura para verificar.
  7. Capa: apenas waitForFileChooser.
  8. Verificação de upload: apenas pelo DOM; após upload, salve rascunho primeiro.
  9. Não navegue para fora antes de o rascunho ser salvo.
  10. A página de login "falha ao carregar" é um falso positivo; use handOff para o humano escanear o QR.
  11. Execute check-login antes de cada publicação; código de saída 2 exige parada.
  12. Publicação é ponto final humano; esta skill só vai até o rascunho.

Testes

npm test

Habilidades Relacionadas