¿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 salida | Significado | Qué hace el agente |
|---|---|---|
0 | Sesión iniciada | Informa al usuario con login_tip y continúa el flujo |
2 | Sin sesión / sin ego-browser | Muestra 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 |
1 | Otro error | Reporta 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.
| API | Descripción |
|---|---|
| `taskSpace(name | id)` |
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
- Shadow DOM de wujie: las consultas comienzan desde
wujie-app.shadowRoot. - Ruta directa a create da pantalla en blanco: debes entrar a la página de publicación a través de la UI.
- Los clics por selector de texto/referencia dentro de wujie son interceptados → usa evaluate o clics por coordenadas.
- Los textos largos solo se pasan a través del parámetro de evaluate.
- La descripción usa execCommand; el título corto usa setter nativo.
- La ubicación requiere dos pasos: coordenadas + verificación de lectura.
- La portada solo con waitForFileChooser.
- La verificación de la subida solo con DOM; después de subir, guarda el borrador primero.
- No navegues fuera del editor antes de que el borrador esté guardado.
- "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.
- Ejecuta check-login antes de cada publicación; si el código de salida es 2, debes detenerte.
- Publicar es un punto final para el humano; esta habilidad solo llega hasta el borrador.
Pruebas
npm test