wechat-channels-video은(는) 무엇을 하나요?
Ego Lite 세션을 사용해 视频号助手에 동영상 초안을 저장합니다. '발행'을 클릭하지 않습니다(QR 발행은 사람이 직접).
먼저 Ego 설치
Ego Lite를 먼저 설치하고 ego-browser가 PATH에 있어야 합니다. 없으면 soft-fail 처리하거나 사용자에게 설치를 알리고, 절대 브라우저를 강제로 실행하지 마세요.
게시 전: 로그인 게이트 (필수)
위챗 채널스 로그인 상태는 매우 빨리 만료됩니다. 에이전트는 업로드나 양식 작성 전에 반드시 다음을 실행해야 합니다:
node scripts/check-login.mjs
# 사람이 QR을 스캔할 수 있도록 창을 열어둘 경우:
node scripts/check-login.mjs --keep
| 종료 코드 | 의미 | 에이전트 동작 |
|---|---|---|
0 | 로그인됨 | login_tip을 사용자에게 알리고 계속 진행 |
2 | 로그인되지 않음 / ego-browser 없음 | how_to_login / how_to_login_zh를 표시하고 중단. 사람이 Ego에서 QR을 스캔한 후 check-login 재실행 |
1 | 기타 오류 | JSON error 보고, 발행된 것처럼 속이지 않음 |
자세한 내용은 references/login.md를 참조하세요. 별도의 cookie-healthcheck 스킬을 만들지 마세요.
입력 (범용, 로컬 제품 경로 없음)
호출자는 동영상 및 텍스트 필드를 제공합니다. references/inputs.md 참조. 본 스킬은 절대 ~/… 비즈니스 디렉토리를 하드코딩하지 않습니다.
최소 필요: 로컬 video 경로, description, short_title. 선택 사항: tags, cover, 위치/원본/활동.
CLI 간단 스크립트 (안정적인 부분)
ego-browser nodejs는 shell argv를 전달하지 않습니다. check-login은 임시 entry + ego-browser nodejs -e로 처리되었습니다.
# 저장소 루트
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 /absolute/path/to/video.mp4 --port 18765
# 설치된 스킬 디렉토리에서는 상대 경로도 사용 가능
node scripts/check-login.mjs
node scripts/serve-file.mjs --file /absolute/path/to/video.mp4 --port 18765
- check-login: 유일한 로그인 하드 게이트; 종료 시 기본값
finish({ keep: [] })(--keep제외). - serve-file: 페이지
fetch를 위한 로컬 CORS 파일 서버;{ url, bytes, port }출력, SIGINT로 종료. 큰 동영상은 base64로 인코딩하지 마세요.
전체 게시 UI를 하나의 거대한 스크립트로 만들지 마세요: wujie, 좌표 클릭, 썸네일 waitForFileChooser는 에이전트가 snapshot/스크린샷을 보고 판단해야 합니다. 스크립트는 결정 가능한 로그인 및 파일 전송만 담당합니다. 양식은 아래 에이전트 단계를 따릅니다.
ego-browser API 빠른 참조
매 ego-browser nodejs는 새 프로세스입니다. TaskSpace / Page는 호출 간에 유지되지만 JS 변수는 유지되지 않습니다.
| API | 설명 |
|---|---|
taskSpace(name|id) | 사용자 목표당 하나의 space; spaceId를 기록해 재사용 |
task.page("p1") | 첫 화면은 goto로 재사용, 마구 페이지를 열지 마세요 |
page.snapshot() | 관찰 (open shadow를 볼 수 있음); 클릭은 wujie 내에서 자주 차단됨 |
page.evaluate(fn, arg) | 하나의 JSON 직렬화 가능 인수만 받음 (긴 텍스트는 여기로 전달) |
page.mouse.click(x, y) | wujie 내 팝업에서 자주 사용 |
page.waitForFileChooser() | 썸네일의 유일한 안전한 업로드 경로 |
task.handOff() | 사람에게 QR 게시 / 직접 로그인을 넘김 |
task.finish({ keep: [] }) | 마무리, space 종료; 사람이 계속 볼 필요가 있을 때는 handOff 사용, 장기간 머신 점유 금지 |
중요: snapshot은 wujie shadow를 볼 수 있음; ref/text 클릭은 pointer events 차단 오류를 발생시킴. wujie 내 동작은 evaluate 안에서 .click() 또는 좌표 mouse.click 사용.
에이전트 흐름 (로그인 통과 후)
0. 게시 페이지로 라우팅
직접 goto /platform/post/create 하지 마세요 (지연 로딩으로 흰 화면). UI 경로를 따르세요:
홈 → 왼쪽 메뉴 '콘텐츠 관리' → '동영상' → 오른쪽 '동영상 게시'
준비 완료: wujie 내에 '동영상 설명', '짧은 제목' 텍스트, 숨겨진 input[type=file][accept*="video"], '초안 저장' 버튼이 있어야 합니다.
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. 동영상 업로드
먼저 serve-file을 시작하고(기본 포트 18765), 페이지에서 fetch + DataTransfer로 숨겨진 파일 input에 주입합니다 (동영상 컴포넌트는 isTrusted 검사를 하지 않음; 썸네일은 함):
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' });
검증은 DOM만 확인: '转发/赞/评论' 미리보기 카드, 진행률 100% 안정화. performance 리소스에서 업로드 POST는 잡히지 않습니다. 업로드 중에는 양식을 작성하지 마세요; wujie가 잠시 null이 되면 기다렸다가 다시 확인하고, 새로고침하지 마세요.
1.5 체크포인트: 먼저 초안 저장
업로드 완료 후 즉시 '초안 저장'을 한 번 클릭한 다음, 설명/짧은 제목/위치를 입력하고 마지막에 다시 저장합니다. 중간에 문제가 생겨 큰 파일을 다시 업로드하는 것을 방지합니다.
2. 설명 + 태그
설명은 .input-editor (contenteditable 아님). execCommand 사용; 텍스트는 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. 짧은 제목
실제 <input>이므로 반드시 native setter를 사용; input.value로 검증 (innerText에는 input 값이 없음).
4. 위치
기본적으로 도시가 포함됩니다. 안전한 방법: mouse.click으로 열기 → snapshot/스크린샷 → '위치 표시 안 함' 클릭 → 필드 다시 읽어 확인. evaluate 좌표가 숨겨진 레이어를 클릭할 수 있습니다.
5. 초안 저장 (종점)
'초안 저장' 클릭. 성공 신호: '저장됨' + 하단 버튼이 회색으로 변함. 그런 다음 goto로 초안함 확인. 초안함이 보이기 전에 편집기를 떠나지 마세요.
선택 사항: 원본 표시, 활동 검색 (native setter로 검색어 입력; 검색 결과 없으면 '활동 참여 안 함' 선택).
6. 썸네일 (선택 사항)
유일한 안전한 방법: waitForFileChooser() + 신뢰할 수 있는 '썸네일 업로드' 클릭 + chooser.setFiles(<절대 경로>). input.files 합성 / OS 파일 선택기 방식은 실제로 실패했습니다. 확인 버튼이 돌아가는 것을 멈추고 미리보기가 기본 프레임으로 돌아가지 않을 때까지 기다린 후 초안 저장.
7. 마무리
serve-file중지 (포트 해제).- 사람이 게시해야 함: 초안함에서
handOff(). - 그렇지 않으면
finish({ keep: [] }), space를 장기간 점유하지 마세요.
주의사항 목록
- wujie shadow:
wujie-app.shadowRoot에서 시작. - create 직접 링크 흰 화면: UI 경로로 게시 페이지 진입.
- wujie 내 ref/text 클릭 차단됨 → evaluate 또는 좌표 클릭 사용.
- 긴 텍스트는 evaluate 파라미터로만 전달.
- 설명은 execCommand 사용; 짧은 제목은 native setter 사용.
- 위치는 두 단계 좌표 + 다시 읽어 검증.
- 썸네일은 waitForFileChooser만 가능.
- 업로드 검증은 DOM만 확인; 업로드 후 먼저 초안 저장.
- 초안이 저장되기 전에 네비게이션으로 떠나지 않음.
- 로그인 페이지 '로딩 실패'는 허위 경보; handOff로 사람이 QR 스캔.
- 게시할 때마다 check-login; exit 2면 반드시 중단.
- 게시는 사람이 하는 최종 단계; 본 스킬은 초안까지만.
테스트
npm test