Communitygithub.com

terryso/wechat-channels-video

Publica un borrador de video en WeChat Channels utilizando Ego Lite a través de ego-browser: primero verifica el inicio de sesión (las sesiones caducan rápido), luego sube el video, completa la descripción, título corto, etiquetas, ubicación y portada, y finalmente guarda solo el borrador. Falla suavemente si Channels no ha iniciado sesión e indica al humano que escanee el código QR dentro de Ego. Para uso personal. Úsalo cuando el usuario solicite publicar en 视频号 / Channels assistant.

¿Qué es wechat-channels-video?

Esta habilidad permite publicar un **borrador** de video en WeChat Channels (视频号) utilizando Ego Lite y ego-browser, sin llegar a publicarlo automáticamente (la publicación final queda para el humano mediante código QR). Está diseñada para uso personal y es especialmente útil cuando el usuario pide 'post a 视频号' o 'Channels assistant'. La habilidad soporta plataformas que integren ego-browser (como Claude Code, Cursor, etc.) y su flujo principal incluye: verificar el estado de inicio de sesión con `check-login.mjs`, subir un video local mediante `serve-file.mjs` e inyección de archivos en el DOM, rellenar campos como descripción, título corto, etiquetas, ubicación y portada, y guardar el borrador. Se basa en la documentación de `references/login.md` y `references/inputs.md`. Es importante tener en cuenta que el proceso debe ejecutarse en el orden correcto: primero verificar el login, luego subir el video y guardar inmediatamente un borrador inicial, después completar los metadatos y guardar final. La habilidad maneja correctamente la interfaz de Wujie (shadow DOM) dentro de la página de Channels, utiliza `execCommand` para la descripción, setters nativos para el título corto, y `waitForFileChooser()` para la portada. Finaliza siempre con `handOff()` para que el humano publique o con `finish({ keep: [] })`, evitando ocupar el espacio de forma permanente.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace wechat-channels-video?

Usa la sesión de Ego Lite en 视频号助手 para guardar un borrador de video. No hagas clic en "Publicar" — la publicación con código QR es solo para humanos.

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

Instalar Ego primero

Instala Ego Lite y asegúrate de que ego-browser esté en PATH. Si falta, haz un soft-fail / informa al usuario que lo instale; no intentes ejecutar el navegador directamente.

Antes de cada publicación: compuerta de inicio de sesión (obligatorio)

El estado de inicio de sesión en Channels caduca muy rápido. El agente debe ejecutar esto antes de subir o rellenar formularios:

node scripts/check-login.mjs
# Si necesitas dejar la ventana para que el humano escanee:
node scripts/check-login.mjs --keep
Código de salidaSignificadoQué hace el agente
0Sesión iniciadaInforma al usuario con login_tip y continúa el flujo
2Sin sesión / sin ego-browserMuestra how_to_login / how_to_login_zh, se detiene. Pide al humano que escanee el código QR en Ego y luego vuelva a ejecutar check-login
1Otro errorReporta el error JSON, no finjas que se publicó

Consulta references/login.md para más detalles. No implementes otra habilidad de verificación de cookies.

Entradas (genéricas, sin rutas específicas del producto)

El llamante proporciona los campos de video y texto; consulta references/inputs.md. Esta habilidad no usa rutas fijas como ~/….

Como mínimo se necesita: ruta local del video, description, short_title. Opcionales: tags, cover, ubicación/original/evento.

Scripts CLI ligeros (parte estable)

ego-browser nodejs no reenvía los argumentos de shell; check-login ya está empaquetado con un entry temporal + ego-browser nodejs -e.

# Desde la raíz del repositorio
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 /ruta/absoluta/al/video.mp4 --port 18765

# Desde el directorio de la habilidad instalada también puedes usar rutas relativas
node scripts/check-login.mjs
node scripts/serve-file.mjs --file /ruta/absoluta/al/video.mp4 --port 18765
  • check-login: Única compuerta de inicio de sesión; al finalizar, por defecto finish({ keep: [] }) (excepto con --keep).
  • serve-file: Servidor de archivos local con CORS para que la página pueda hacer fetch; imprime { url, bytes, port } y termina con SIGINT. No uses base64 para videos grandes.

No conviertas toda la UI de publicación en un script gigante: wujie, clics por coordenadas, y waitForFileChooser para la portada requieren que el agente vea snapshots/capturas. Los scripts se encargan de la lógica determinista (login y transferencia de archivos); el formulario se maneja según los pasos del agente a continuación.

Referencia rápida de la API de ego-browser

Cada ego-browser nodejs es un proceso nuevo; TaskSpace / Page persisten entre llamadas, pero las variables JS no.

APIDescripción
`taskSpace(nameid)`
task.page("p1")En la primera pantalla, reutiliza la página con goto; no abras páginas de más
page.snapshot()Observa (puede ver a través de shadow DOM); los clics suelen ser interceptados dentro de wujie
page.evaluate(fn, arg)Solo acepta un argumento serializable en JSON (para textos largos, pásalos aquí)
page.mouse.click(x, y)Útil para capas emergentes dentro de wujie
page.waitForFileChooser()Única forma fiable de subir la portada
task.handOff()Entrega el control al humano para escanear QR / iniciar sesión / publicar
task.finish({ keep: [] })Finaliza y cierra el space; si el humano necesita continuar, usa handOff, no ocupes la máquina por mucho tiempo

Clave: snapshot ve el shadow DOM de wujie; los clics por selector de texto/referencia reportan "intercepts pointer events". Para acciones dentro de wujie, usa .click() dentro de evaluate o clics por coordenadas con mouse.click.

Flujo del agente (después de iniciar sesión)

0. Navegar a la página de publicación

No uses goto directo a /platform/post/create (carga perezosa y pantalla en blanco). Sigue la ruta UI:

Inicio → Columna izquierda "Gestión de contenido" → "Videos" → Botón derecho "Publicar video"

Cuando esté listo: dentro de wujie aparecen los textos "Descripción del video" y "Título corto", y hay un input[type=file][accept*="video"] oculto y un botón "Guardar borrador".

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('Descripción del video'),
    hasShort: t.includes('Título corto'),
    hasUpload: !!app.shadowRoot.querySelector('input[type=file][accept*="video"]'),
    hasSave: t.includes('Guardar borrador'),
  };
});

1. Subir el video

Primero inicia serve-file (puerto por defecto 18765), luego dentro de la página haz fetch + inyecta con DataTransfer en el input de archivo oculto (el componente de video no verifica isTrusted; la portada sí):

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 solo con el DOM: aparece la tarjeta de vista previa con "Compartir/Me gusta/Comentarios" y la barra de progreso llega al 100%. No captures recursos de performance porque no se ve el POST de subida. Mientras se sube, no rellenes el formulario; si wujie se vuelve null momentáneamente, espera un poco y vuelve a intentar, no recargues.

1.5 Punto de control: guardar borrador primero

Después de que la subida se complete, inmediatamente haz clic en "Guardar borrador" una vez, luego rellena la descripción, título corto, ubicación, etc., y finalmente guarda de nuevo. Esto evita tener que re-subir el archivo grande si algo sale mal.

2. Descripción + etiquetas

La descripción es un .input-editor (no contenteditable). Usa execCommand; el texto se pasa como segundo parámetro de 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 corto

Es un <input> real, debes usar el setter nativo; verifica leyendo input.value (innerText no contiene el valor del input).

4. Ubicación

Por defecto suele venir con la ciudad. La forma segura: mouse.click para abrir → snapshot/captura → haz clic en "No mostrar ubicación" → vuelve a leer el campo para confirmar. Usar coordenadas en evaluate puede hacer clic en capas ocultas.

5. Guardar borrador (punto final)

Haz clic en "Guardar borrador". Señal de éxito: aparece "Guardado" + el botón inferior se vuelve gris. Luego navega a la papelera de borradores para verificar. No salgas del editor antes de que el borrador sea visible en la papelera.

Opcional: declarar original, búsqueda de eventos (setter nativo para escribir el término de búsqueda; si no se encuentra, "No participar en el evento").

6. Portada (opcional)

Única forma fiable: waitForFileChooser() + clic real en "Subir portada" + chooser.setFiles(<ruta absoluta>). Inyectar input.files o usar el cuadro de diálogo del sistema operativo ha fallado en las pruebas. Después de que el botón de confirmación deje de girar y la vista previa no vuelva al fotograma predeterminado, guarda el borrador.

7. Finalización

  • Detén serve-file (libera el puerto).
  • Si el humano necesita publicar: handOff() desde la papelera de borradores.
  • De lo contrario, finish({ keep: [] }), no ocupes el space por mucho tiempo.

Lista de errores a evitar

  1. Shadow DOM de wujie: las consultas comienzan desde wujie-app.shadowRoot.
  2. Ruta directa a create da pantalla en blanco: debes entrar a la página de publicación a través de la UI.
  3. Los clics por selector de texto/referencia dentro de wujie son interceptados → usa evaluate o clics por coordenadas.
  4. Los textos largos solo se pasan a través del parámetro de evaluate.
  5. La descripción usa execCommand; el título corto usa setter nativo.
  6. La ubicación requiere dos pasos: coordenadas + verificación de lectura.
  7. La portada solo con waitForFileChooser.
  8. La verificación de la subida solo con DOM; después de subir, guarda el borrador primero.
  9. No navegues fuera del editor antes de que el borrador esté guardado.
  10. "Error de carga" en la página de inicio de sesión es una pista falsa; usa handOff para que el humano escanee el QR.
  11. Ejecuta check-login antes de cada publicación; si el código de salida es 2, debes detenerte.
  12. Publicar es un punto final para el humano; esta habilidad solo llega hasta el borrador.

Pruebas

npm test

Skills relacionados