Communitygithub.com

terryso/wechat-channels-video

Publier un brouillon de vidéo sur WeChat Channels via Ego Lite (ego-browser) — vérification de connexion d'abord (les sessions expirent rapidement), upload de la vidéo, remplissage de la description/titre court/sujets/localisation/couverture, enregistrement du brouillon uniquement. Échec doux si déconnecté de Channels, invitation à scanner le QR dans Ego. Usage personnel. Utiliser quand l'utilisateur demande de poster sur 视频号 / Channels assistant.

Qu'est-ce que wechat-channels-video ?

Cette compétence permet à un agent IA de sauvegarder un brouillon de vidéo sur la plateforme WeChat Channels (视频号) via l'interface d'administration (https://channels.weixin.qq.com) en utilisant le navigateur tête-rapide Ego Lite et la commande `ego-browser`. Elle est conçue pour un usage personnel uniquement : l'agent ne publie jamais la vidéo (le clic sur « Publier » est réservé à l'humain via scan QR), mais gère tout le workflow jusqu'à l'enregistrement du brouillon. La compétence est principalement destinée aux agents intégrés à des environnements comme Claude Code, Cursor ou Codex qui interagissent avec des applications web via un navigateur headless. La fonctionnalité clé est la vérification impérative de la session de connexion avant chaque opération (via `node scripts/check-login.mjs`), car les sessions WeChat expirent rapidement. Si l'utilisateur n'est pas connecté, l'agent s'arrête et demande à l'humain de scanner un QR code dans Ego. Ensuite, l'agent exécute un pipeline précis : démarrage d'un serveur de fichiers local, injection du fichier vidéo dans l'interface web, remplissage de la description (via `execCommand`), du titre court (via native setter), de sujets, de la localisation et éventuellement de la couverture (via `waitForFileChooser`), suivi d'un premier enregistrement de brouillon immédiat après l'upload, puis d'un second après le remplissage. Le workflow évite les pièges comme les pages blanches dues au lazy loading, les clics interceptés dans les shadow DOM de wujie, et les pertes de session. À la fin, l'agent libère les ressources et, si nécessaire, passe la main à l'humain via `handOff()` pour la publication réelle.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait wechat-channels-video ?

Utilisez une session Ego Lite dans l'assistant Channels pour enregistrer un brouillon vidéo. Ne cliquez pas sur « Publier » (la publication par QR est réservée à l'humain).

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

Installation préalable : Ego

Installez d'abord Ego Lite et assurez-vous que ego-browser est dans le PATH. En cas d'absence, faites un soft-fail / informez l'utilisateur de l'installer, n'utilisez pas un navigateur classique.

Avant chaque publication : vérification de connexion (obligatoire)

La session de connexion à Channels expire très rapidement. L'agent doit exécuter la commande suivante avant toute opération d'upload ou de remplissage de formulaire :

node scripts/check-login.mjs
# Si la fenêtre doit être laissée à l'utilisateur pour scanner le QR :
node scripts/check-login.mjs --keep
Code de sortieSignificationAction de l'agent
0ConnectéInforme l'utilisateur du login_tip, continue le processus.
2Non connecté / ego-browser absentAffiche how_to_login / how_to_login_zh, s'arrête. Demande à l'utilisateur de scanner le QR dans Ego, puis de relancer check-login.
1Autre erreurSignale l'error JSON, ne fait pas comme si la publication avait eu lieu.

Voir les détails dans references/login.md. N'utilisez pas une autre compétence de vérification de cookies.

Entrées (génériques, sans chemin produit local)

L'appelant fournit le chemin de la vidéo et les champs de texte, voir references/inputs.md. Cette compétence n'utilise pas de répertoire métier en dur de type ~/….

Minimum requis : chemin local video, description, short_title. Optionnels : tags, cover, emplacement/original/activité.

Scripts CLI fins (partie stable)

ego-browser nodejs ne transmet pas les arguments shell ; check-login est déjà encapsulé avec une entrée temporaire + ego-browser nodejs -e.

# Depuis la racine du dépôt
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 /chemin/absolu/vers/video.mp4 --port 18765

# Depuis le répertoire de la compétence installée, vous pouvez aussi utiliser le chemin relatif
node scripts/check-login.mjs
node scripts/serve-file.mjs --file /chemin/absolu/vers/video.mp4 --port 18765
  • check-login : unique porte de vérification de connexion ; se termine par défaut par finish({ keep: [] }) (sauf si --keep est utilisé).
  • serve-file : service de fichiers CORS local pour les fetch de la page ; imprime { url, bytes, port }, se termine par SIGINT. N'utilisez pas base64 pour les vidéos volumineuses.

Ne pas regrouper toute l'interface de publication en un seul script monolithique : wujie, les clics par coordonnées, le téléchargement de couverture avec waitForFileChooser nécessitent que l'agent observe les snapshots/captures d'écran. Les scripts gèrent les parties déterministes (connexion, transfert de fichier) ; le formulaire est traité par les étapes de l'agent ci-dessous.

Aide-mémoire de l'API ego-browser

Chaque appel à ego-browser nodejs crée un nouveau processus ; TaskSpace / Page persistent entre les appels, mais les variables JavaScript ne persistent pas.

APIDescription
`taskSpace(nameid)`
task.page("p1")Réutilisez la page initiale avec goto, n'ouvrez pas de nouvelles pages inutilement.
page.snapshot()Observabilité (voit à travers l'ombre ouverte) ; les clics sont souvent interceptés dans wujie.
page.evaluate(fn, arg)Accepte un seul argument sérialisable JSON (les longs textes passent par ici).
page.mouse.click(x, y)Utile pour les popups dans wujie.
page.waitForFileChooser()Seule méthode fiable pour le téléchargement de couverture.
task.handOff()Passe la main à l'humain pour publier par QR / se connecter en direct.
task.finish({ keep: [] })Ferme l'espace à la fin ; utilisez handOff si l'humain doit continuer, n'occupez pas la machine longtemps.

Point clé : le snapshot voit le shadow wujie ; les clics par ref/texte échouent avec une interception des événements pointeur. Dans wujie, utilisez .click() dans evaluate ou mouse.click par coordonnées.

Processus de l'agent (après connexion réussie)

0. Naviguer vers la page de publication

N'utilisez pas directement goto /platform/post/create (page blanche due au lazy loading). Suivez le parcours UI :

Accueil → barre de gauche « Gestion du contenu » → « Vidéo » → bouton droit « Publier une vidéo »

État attendu : les textes « Description de la vidéo », « Titre court » apparaissent dans wujie, ainsi qu'un input[type=file][accept*="video"] caché et un bouton « Enregistrer le brouillon ».

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. Télécharger la vidéo

Lancez d'abord serve-file (port par défaut 18765), puis dans la page, effectuez un fetch + injection via DataTransfer dans l'input file caché (le composant vidéo n'est pas renforcé par isTrusted ; la couverture, oui) :

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' });

Vérification : observez uniquement le DOM (apparition de la carte de prévisualisation « Transmettre/J'aime/Commenter », progression stable à 100%). Les ressources performance ne capturent pas la requête POST d'upload. Ne remplissez pas le formulaire pendant l'upload ; si wujie devient temporairement null, attendez un moment, ne rafraîchissez pas.

1.5 Point de contrôle : enregistrer d'abord le brouillon

Après l'upload, cliquez immédiatement une fois sur « Enregistrer le brouillon », puis remplissez la description/titre court/localisation, et enfin enregistrez à nouveau. Cela évite de devoir retélécharger un gros fichier en cas d'échec en cours de route.

2. Description + sujets

La description utilise .input-editor (non contenteditable). Utilisez execCommand ; le texte est passé via le second paramètre 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. Titre court

C'est un vrai <input>, doit utiliser le native setter ; vérifiez en lisant input.value (innerText ne contient pas la valeur de l'input).

4. Localisation

Par défaut, une ville est souvent présente. Méthode fiable : mouse.click pour ouvrir → snapshot/capture → cliquez sur « Ne pas afficher la localisation » → relisez le champ pour confirmer. Les coordonnées dans evaluate peuvent cliquer sur une couche cachée.

5. Enregistrer le brouillon (point final)

Cliquez sur « Enregistrer le brouillon ». Signal de succès : « Enregistré » + le bouton en bas devient gris. Ensuite, allez dans la boîte de brouillons pour vérifier. Ne quittez pas l'éditeur avant que le brouillon ne soit visible dans la boîte.

Optionnel : déclarer original, recherche d'activité (native setter pour écrire le terme de recherche ; si aucun résultat, « Ne pas participer à l'activité »).

6. Couverture (optionnel)

Seule méthode fiable : waitForFileChooser() + clic de confiance sur « Télécharger la couverture » + chooser.setFiles(<chemin absolu>). La méthode de synthèse input.files / boîte de dialogue OS a été testée et a échoué. Attendez que le bouton de confirmation cesse de tourner et que l'aperçu ne revienne plus à l'image par défaut avant de sauvegarder le brouillon.

7. Finalisation

  • Arrêtez serve-file (libération du port).
  • Si l'humain doit publier : handOff() dans la boîte de brouillons.
  • Sinon, finish({ keep: [] }), n'occupez pas l'espace longtemps.

Liste des pièges à éviter

  1. Shadow wujie : les requêtes partent de wujie-app.shadowRoot.
  2. Lien direct create → page blanche : utilisez le parcours UI pour accéder à la page de publication.
  3. Dans wujie, les clics par ref/texte sont interceptés → utilisez evaluate ou clic par coordonnées.
  4. Les longs textes passent uniquement par le paramètre de evaluate.
  5. Description : utilisez execCommand ; titre court : native setter.
  6. Localisation : deux étapes (coordonnées + relecture de vérification).
  7. Couverture : uniquement waitForFileChooser.
  8. Vérification de l'upload : uniquement le DOM ; après l'upload, enregistrez d'abord le brouillon.
  9. Ne naviguez pas avant que le brouillon ne soit enregistré.
  10. « Échec du chargement » de la page de connexion est un faux problème ; handOff laisse l'humain scanner le QR.
  11. Vérifiez la connexion avant chaque publication ; code de sortie 2 = arrêt obligatoire.
  12. La publication est un point final humain ; cette compétence ne va que jusqu'au brouillon.

Tests

npm test

Skills associés