mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-14 06:39:48 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd5745f837 | |||
| cb26732559 | |||
| 9baf570caa |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.116.4"
|
||||
"version": "1.116.5"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.116.5 - 2026-05-14
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-wechat`: send WeChat login QR code to Telegram when `TELEGRAM_BOT_TOKEN` and `TELEGRAM_CHAT_ID` env vars are set, enabling headless / remote login flows (by @beforesun)
|
||||
|
||||
### Refactor
|
||||
- `baoyu-post-to-wechat`: harden Telegram QR notification — add 10s fetch timeout, defer the 2s QR-render wait until env vars are configured, and use viewport screenshot as fallback
|
||||
|
||||
## 1.116.4 - 2026-05-14
|
||||
|
||||
### Refactor
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.116.5 - 2026-05-14
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-wechat`:当设置 `TELEGRAM_BOT_TOKEN` 和 `TELEGRAM_CHAT_ID` 环境变量时,自动将微信登录二维码发送到 Telegram,支持无显示器/远程登录场景 (by @beforesun)
|
||||
|
||||
### 重构
|
||||
- `baoyu-post-to-wechat`:加固 Telegram QR 通知逻辑 —— 增加 10 秒 fetch 超时、未配置环境变量时不再无谓等待 2 秒、回退截图改为视口范围以减小体积
|
||||
|
||||
## 1.116.4 - 2026-05-14
|
||||
|
||||
### 重构
|
||||
|
||||
@@ -245,7 +245,7 @@ Files created:
|
||||
|-------|-----|
|
||||
| Missing API credentials | Follow guided setup in Step 2 |
|
||||
| Access token error | Verify credentials valid and not expired |
|
||||
| Not logged in (browser) | First run opens browser — scan QR to log in |
|
||||
| Not logged in (browser) | First run opens browser — scan QR to log in. Set `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID` to receive the QR image via Telegram |
|
||||
| Chrome not found | Set `WECHAT_BROWSER_CHROME_PATH` |
|
||||
| Title/summary missing | Use auto-generation or provide manually |
|
||||
| No cover image | Add frontmatter cover or place `imgs/cover.png` in article directory |
|
||||
|
||||
@@ -31,7 +31,98 @@ interface ArticleOptions {
|
||||
cdpPort?: number;
|
||||
}
|
||||
|
||||
async function sendQrToTelegram(session: ChromeSession): Promise<void> {
|
||||
const botToken = process.env.TELEGRAM_BOT_TOKEN;
|
||||
const chatId = process.env.TELEGRAM_CHAT_ID;
|
||||
if (!botToken || !chatId) return;
|
||||
|
||||
// Wait for QR to render before extracting
|
||||
await sleep(2000);
|
||||
|
||||
try {
|
||||
// Try to extract QR image from DOM first (avoids full-page screenshot noise)
|
||||
const domResult = await session.cdp.send<{ result: { value: string } }>('Runtime.evaluate', {
|
||||
expression: `
|
||||
(function() {
|
||||
const selectors = [
|
||||
'.login__type__container__scan img',
|
||||
'.login_img img',
|
||||
'#login_container img',
|
||||
'.qrcode img',
|
||||
'img[src*="qrcode"]',
|
||||
'img[src*="login"]',
|
||||
];
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el?.src && !el.src.startsWith('data:,')) return el.src.startsWith('data:') ? el.src : 'url:' + el.src;
|
||||
}
|
||||
const canvas = document.querySelector('canvas');
|
||||
if (canvas) try { return canvas.toDataURL('image/png'); } catch {}
|
||||
return '';
|
||||
})()
|
||||
`,
|
||||
returnByValue: true,
|
||||
}, { sessionId: session.sessionId });
|
||||
|
||||
const raw = (domResult.result.value as string) ?? '';
|
||||
let imgBuffer: Buffer;
|
||||
|
||||
if (raw.startsWith('data:image')) {
|
||||
imgBuffer = Buffer.from(raw.split(',')[1] ?? '', 'base64');
|
||||
} else if (raw.startsWith('url:')) {
|
||||
// Fetch inside Chrome to carry WeChat session cookies
|
||||
const imgUrl = raw.slice(4);
|
||||
const inBrowserFetch = await session.cdp.send<{ result: { value: string } }>('Runtime.evaluate', {
|
||||
expression: `
|
||||
(async () => {
|
||||
const resp = await fetch(${JSON.stringify(imgUrl)}, { credentials: 'include' });
|
||||
const buf = await resp.arrayBuffer();
|
||||
const bytes = new Uint8Array(buf);
|
||||
let b = '';
|
||||
for (let i = 0; i < bytes.length; i++) b += String.fromCharCode(bytes[i]);
|
||||
return btoa(b);
|
||||
})()
|
||||
`,
|
||||
returnByValue: true,
|
||||
awaitPromise: true,
|
||||
}, { sessionId: session.sessionId });
|
||||
imgBuffer = Buffer.from((inBrowserFetch.result.value as string) ?? '', 'base64');
|
||||
} else {
|
||||
// Fallback: viewport screenshot (smaller than full-page; QR is usually in viewport)
|
||||
const screenshotResp = await session.cdp.send<{ data: string }>(
|
||||
'Page.captureScreenshot', { format: 'png', captureBeyondViewport: false }, { sessionId: session.sessionId }
|
||||
);
|
||||
imgBuffer = Buffer.from(screenshotResp.data ?? '', 'base64');
|
||||
}
|
||||
|
||||
const boundary = `tgboundary${Date.now()}`;
|
||||
const parts: Buffer[] = [
|
||||
Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="chat_id"\r\n\r\n${chatId}\r\n`),
|
||||
Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="caption"\r\n\r\nWeChat QR code — please scan to log in\r\n`),
|
||||
Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="photo"; filename="qr.png"\r\nContent-Type: image/png\r\n\r\n`),
|
||||
imgBuffer,
|
||||
Buffer.from(`\r\n--${boundary}--\r\n`),
|
||||
];
|
||||
const tgResp = await fetch(`https://api.telegram.org/bot${botToken}/sendPhoto`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` },
|
||||
body: Buffer.concat(parts),
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
});
|
||||
const tgJson = await tgResp.json() as { ok: boolean; description?: string };
|
||||
if (tgJson.ok) {
|
||||
console.log('[wechat] QR code sent to Telegram.');
|
||||
} else {
|
||||
console.error('[wechat] Telegram send failed:', tgJson.description);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[wechat] Failed to send QR to Telegram:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForLogin(session: ChromeSession, timeoutMs = 120_000): Promise<boolean> {
|
||||
// Notify via Telegram if configured (no-op when env vars absent)
|
||||
await sendQrToTelegram(session);
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
const url = await evaluate<string>(session, 'window.location.href');
|
||||
@@ -557,7 +648,8 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
|
||||
|
||||
const url = await evaluate<string>(session, 'window.location.href');
|
||||
if (!url.includes('/cgi-bin/')) {
|
||||
console.log('[wechat] Not logged in. Please scan QR code...');
|
||||
const hasTelegram = !!(process.env.TELEGRAM_BOT_TOKEN && process.env.TELEGRAM_CHAT_ID);
|
||||
console.log(`[wechat] Not logged in. Please scan QR code...${hasTelegram ? ' (sending to Telegram)' : ''}`);
|
||||
const loggedIn = await waitForLogin(session);
|
||||
if (!loggedIn) throw new Error('Login timeout');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user