koishi-plugin-vr-fever 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/weibo.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -0
- package/lib/service/login.d.ts +1 -1
- package/lib/service/login.js +7 -4
- package/lib/service/poll.js +12 -8
- package/lib/service/weibo-fetch.js +10 -2
- package/lib/util/constants.js +2 -1
- package/lib/util/puppeteer-cookie.d.ts +4 -3
- package/lib/util/puppeteer-cookie.js +15 -13
- package/package.json +1 -1
package/lib/commands/weibo.js
CHANGED
|
@@ -111,7 +111,7 @@ const registerWeiboCommand = (ctx, config, pollWeibo) => {
|
|
|
111
111
|
if (message === "login") {
|
|
112
112
|
try {
|
|
113
113
|
await (0, puppeteer_cookie_1.ensurePuppeteerBrowser)(ctx);
|
|
114
|
-
await (0, login_1.getQRcode)(ctx, argv.session);
|
|
114
|
+
await (0, login_1.getQRcode)(ctx, argv.session, config.isDebugMode);
|
|
115
115
|
}
|
|
116
116
|
catch (error) {
|
|
117
117
|
(0, send_msg_1.sendMsg)((0, puppeteer_cookie_1.formatPuppeteerError)(error), argv.session);
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -24,6 +24,9 @@ exports.Config = koishi_1.Schema.object({
|
|
|
24
24
|
isTextMode: koishi_1.Schema.boolean()
|
|
25
25
|
.default(false)
|
|
26
26
|
.description("开启后以文本推送微博,关闭则以截图图片推送"),
|
|
27
|
+
isDebugMode: koishi_1.Schema.boolean()
|
|
28
|
+
.default(false)
|
|
29
|
+
.description("开启后保存调试截图(登录二维码、微博推送截图等)"),
|
|
27
30
|
});
|
|
28
31
|
async function apply(ctx, config) {
|
|
29
32
|
ctx.model.extend("weibo_cookies", {
|
package/lib/service/login.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Context, Session } from "koishi";
|
|
2
2
|
/** 打开微博登录页,截图二维码并等待扫码完成 */
|
|
3
|
-
export declare const getQRcode: (ctx: Context, session: Session) => Promise<boolean>;
|
|
3
|
+
export declare const getQRcode: (ctx: Context, session: Session, isDebugMode?: boolean) => Promise<boolean>;
|
|
4
4
|
export declare const checkLoginStatus: (ctx: Context) => Promise<boolean | null>;
|
package/lib/service/login.js
CHANGED
|
@@ -4,8 +4,9 @@ exports.checkLoginStatus = exports.getQRcode = void 0;
|
|
|
4
4
|
const constants_1 = require("../util/constants");
|
|
5
5
|
const puppeteer_cookie_1 = require("../util/puppeteer-cookie");
|
|
6
6
|
const send_msg_1 = require("../util/send-msg");
|
|
7
|
+
const timer_1 = require("../util/timer");
|
|
7
8
|
/** 打开微博登录页,截图二维码并等待扫码完成 */
|
|
8
|
-
const getQRcode = async (ctx, session) => {
|
|
9
|
+
const getQRcode = async (ctx, session, isDebugMode = false) => {
|
|
9
10
|
await (0, puppeteer_cookie_1.ensurePuppeteerBrowser)(ctx);
|
|
10
11
|
const page = await ctx.puppeteer.page();
|
|
11
12
|
if (!page) {
|
|
@@ -14,14 +15,16 @@ const getQRcode = async (ctx, session) => {
|
|
|
14
15
|
}
|
|
15
16
|
try {
|
|
16
17
|
(0, send_msg_1.sendMsg)("开启网站中...", session);
|
|
17
|
-
await (0, puppeteer_cookie_1.navigatePage)(page, constants_1.CONSTANTS.WEIBO_PASSPORT_URL);
|
|
18
|
+
await (0, puppeteer_cookie_1.navigatePage)(page, constants_1.CONSTANTS.WEIBO_PASSPORT_URL, (0, timer_1.getWaitMs)(0.4));
|
|
18
19
|
(0, send_msg_1.sendMsg)("正在切换到扫码登录...", session);
|
|
19
|
-
const qrResult = await (0, puppeteer_cookie_1.captureLoginQrFromPage)(page,
|
|
20
|
+
const qrResult = await (0, puppeteer_cookie_1.captureLoginQrFromPage)(page, (0, timer_1.getWaitMs)(0.4), isDebugMode);
|
|
20
21
|
if (qrResult.debugDir) {
|
|
21
22
|
(0, send_msg_1.sendMsg)(`调试截图已保存到: ${qrResult.debugDir}`, session);
|
|
22
23
|
}
|
|
23
24
|
if (!qrResult.detected || !qrResult.dataUrl) {
|
|
24
|
-
(0, send_msg_1.sendMsg)(
|
|
25
|
+
(0, send_msg_1.sendMsg)(isDebugMode
|
|
26
|
+
? "未找到二维码,请查看调试目录中的 page.png / meta.json"
|
|
27
|
+
: "未找到二维码,请重试", session);
|
|
25
28
|
return false;
|
|
26
29
|
}
|
|
27
30
|
const base64 = qrResult.dataUrl.replace(/^data:image\/\w+;base64,/, "");
|
package/lib/service/poll.js
CHANGED
|
@@ -79,14 +79,18 @@ const createPollWeibo = (ctx, config, sendMsgOnebot) => {
|
|
|
79
79
|
for (let i = 0; i < images.length; i++) {
|
|
80
80
|
const image = images[i];
|
|
81
81
|
const postCount = Math.min(constants_1.CONSTANTS.POSTS_PER_SCREENSHOT, entry.timeline.length - i * constants_1.CONSTANTS.POSTS_PER_SCREENSHOT);
|
|
82
|
-
const saved =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
const saved = config.isDebugMode
|
|
83
|
+
? await (0, save_screenshot_1.saveScreenshotDebug)(image, {
|
|
84
|
+
uid,
|
|
85
|
+
name,
|
|
86
|
+
chunkIndex: i,
|
|
87
|
+
totalChunks: images.length,
|
|
88
|
+
postCount,
|
|
89
|
+
})
|
|
90
|
+
: null;
|
|
91
|
+
if (saved) {
|
|
92
|
+
ctx.logger.info(`截图已保存: ${saved.filePath} (${saved.sizeKB} KB, ${name} ${i + 1}/${images.length})`);
|
|
93
|
+
}
|
|
90
94
|
await (0, send_msg_1.sendImg)(image, session);
|
|
91
95
|
}
|
|
92
96
|
}
|
|
@@ -10,9 +10,17 @@ const getWeiboByUID = async (weiboUID, ctx, _session) => {
|
|
|
10
10
|
if (!weiboHttp) {
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
|
-
const
|
|
14
|
-
|
|
13
|
+
const profilePath = `/ajax/profile/info?uid=${weiboUID}&scene=profile`;
|
|
14
|
+
const fetchProfile = () => weiboHttp
|
|
15
|
+
.get(profilePath)
|
|
15
16
|
.then((res) => res?.data || null);
|
|
17
|
+
let profile;
|
|
18
|
+
try {
|
|
19
|
+
profile = await fetchProfile();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
profile = await fetchProfile();
|
|
23
|
+
}
|
|
16
24
|
const timeline = await weiboHttp
|
|
17
25
|
.get(`/ajax/statuses/mymblog?uid=${weiboUID}&page=1&feature=0`)
|
|
18
26
|
.then((res) => res?.data || null);
|
package/lib/util/constants.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.REGEX = exports.CONSTANTS = void 0;
|
|
4
|
-
const WEIBO_PASSPORT_URL = "https://passport.weibo.com/";
|
|
4
|
+
const WEIBO_PASSPORT_URL = "https://passport.weibo.com/sso/signin?entry=account&source=sinassopage&url=https%3A%2F%2Fmy.sina.com.cn";
|
|
5
|
+
// const WEIBO_PASSPORT_URL = "https://passport.weibo.com/";
|
|
5
6
|
const WEB_TIMEOUT = 120000;
|
|
6
7
|
const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
7
8
|
const WEIBO_SAMPLE_UID = "8376019184";
|
|
@@ -21,20 +21,21 @@ export declare function waitForLogin(page: any, timeoutMs?: number): Promise<boo
|
|
|
21
21
|
export declare function collectFullCookiesAfterLogin(page: any): Promise<AnyCookie[]>;
|
|
22
22
|
export interface QrLoginOptions {
|
|
23
23
|
timeoutMs?: number;
|
|
24
|
+
saveDebug?: boolean;
|
|
24
25
|
onPageCreated?: (page: any) => void | Promise<void>;
|
|
25
26
|
onQrCaptured?: (dataUrl: string | null) => void | Promise<void>;
|
|
26
27
|
}
|
|
27
28
|
/** 微博登录页默认可能是短信登录,需先点击 scan.png 图标所在父 span 切换到「扫码登录」 */
|
|
28
29
|
export declare function clickWeiboQrLoginTab(page: any): Promise<boolean>;
|
|
29
30
|
/** 导出登录页截图到 data/weibo-login-debug 便于排查白图问题 */
|
|
30
|
-
export declare function saveLoginQrDebug(page: any, element: any | null, label: string): Promise<string>;
|
|
31
|
+
export declare function saveLoginQrDebug(page: any, element: any | null, label: string, saveDebug?: boolean): Promise<string | null>;
|
|
31
32
|
/** 切换到扫码登录并等待二维码出现 */
|
|
32
|
-
export declare function captureLoginQrFromPage(page: any, timeoutMs?: number): Promise<{
|
|
33
|
+
export declare function captureLoginQrFromPage(page: any, timeoutMs?: number, saveDebug?: boolean): Promise<{
|
|
33
34
|
dataUrl: string | null;
|
|
34
35
|
detected: boolean;
|
|
35
36
|
debugDir?: string;
|
|
36
37
|
}>;
|
|
37
|
-
export declare function captureQrFromPage(page: any, timeoutMs?: number, debugLabel?: string): Promise<{
|
|
38
|
+
export declare function captureQrFromPage(page: any, timeoutMs?: number, debugLabel?: string, saveDebug?: boolean): Promise<{
|
|
38
39
|
dataUrl: string | null;
|
|
39
40
|
detected: boolean;
|
|
40
41
|
debugDir?: string;
|
|
@@ -336,7 +336,9 @@ async function waitForQrElementReady(page) {
|
|
|
336
336
|
});
|
|
337
337
|
}
|
|
338
338
|
/** 导出登录页截图到 data/weibo-login-debug 便于排查白图问题 */
|
|
339
|
-
async function saveLoginQrDebug(page, element, label) {
|
|
339
|
+
async function saveLoginQrDebug(page, element, label, saveDebug = false) {
|
|
340
|
+
if (!saveDebug)
|
|
341
|
+
return null;
|
|
340
342
|
await promises_1.default.mkdir(LOGIN_QR_DEBUG_DIR, { recursive: true });
|
|
341
343
|
const stamp = `${label}-${Date.now()}`;
|
|
342
344
|
const meta = { label, savedAt: new Date().toISOString() };
|
|
@@ -417,26 +419,26 @@ async function saveLoginQrDebug(page, element, label) {
|
|
|
417
419
|
}
|
|
418
420
|
const bufferToDataUrl = (buffer) => `data:image/png;base64,${buffer.toString("base64")}`;
|
|
419
421
|
/** 切换到扫码登录并等待二维码出现 */
|
|
420
|
-
async function captureLoginQrFromPage(page, timeoutMs = 15000) {
|
|
422
|
+
async function captureLoginQrFromPage(page, timeoutMs = 15000, saveDebug = false) {
|
|
421
423
|
for (let i = 0; i < 3; i++) {
|
|
422
424
|
await clickWeiboQrLoginTab(page);
|
|
423
425
|
await wait(1000);
|
|
424
|
-
const qrResult = await captureQrFromPage(page, 5000, `attempt-${i + 1}
|
|
426
|
+
const qrResult = await captureQrFromPage(page, 5000, `attempt-${i + 1}`, saveDebug);
|
|
425
427
|
if (qrResult.detected)
|
|
426
428
|
return qrResult;
|
|
427
429
|
}
|
|
428
|
-
return captureQrFromPage(page, timeoutMs, "final");
|
|
430
|
+
return captureQrFromPage(page, timeoutMs, "final", saveDebug);
|
|
429
431
|
}
|
|
430
|
-
async function captureQrFromPage(page, timeoutMs = 1500, debugLabel = "capture") {
|
|
432
|
+
async function captureQrFromPage(page, timeoutMs = 1500, debugLabel = "capture", saveDebug = false) {
|
|
431
433
|
let element = null;
|
|
432
434
|
try {
|
|
433
435
|
element = await waitForQrElement(page, timeoutMs);
|
|
434
436
|
if (!element) {
|
|
435
|
-
const debugDir = await saveLoginQrDebug(page, null, `${debugLabel}-missing
|
|
436
|
-
return { dataUrl: null, detected: false, debugDir };
|
|
437
|
+
const debugDir = await saveLoginQrDebug(page, null, `${debugLabel}-missing`, saveDebug);
|
|
438
|
+
return { dataUrl: null, detected: false, debugDir: debugDir ?? undefined };
|
|
437
439
|
}
|
|
438
440
|
await waitForQrElementReady(page);
|
|
439
|
-
const debugDir = await saveLoginQrDebug(page, element, debugLabel);
|
|
441
|
+
const debugDir = await saveLoginQrDebug(page, element, debugLabel, saveDebug);
|
|
440
442
|
const box = await element.boundingBox();
|
|
441
443
|
if (box?.width && box?.height) {
|
|
442
444
|
const clipBuffer = await page.screenshot({
|
|
@@ -450,20 +452,20 @@ async function captureQrFromPage(page, timeoutMs = 1500, debugLabel = "capture")
|
|
|
450
452
|
return {
|
|
451
453
|
dataUrl: bufferToDataUrl(Buffer.from(clipBuffer)),
|
|
452
454
|
detected: true,
|
|
453
|
-
debugDir,
|
|
455
|
+
debugDir: debugDir ?? undefined,
|
|
454
456
|
};
|
|
455
457
|
}
|
|
456
458
|
const elementBuffer = await element.screenshot();
|
|
457
459
|
return {
|
|
458
460
|
dataUrl: bufferToDataUrl(Buffer.from(elementBuffer)),
|
|
459
461
|
detected: true,
|
|
460
|
-
debugDir,
|
|
462
|
+
debugDir: debugDir ?? undefined,
|
|
461
463
|
};
|
|
462
464
|
}
|
|
463
465
|
catch (error) {
|
|
464
|
-
const debugDir = await saveLoginQrDebug(page, element, `${debugLabel}-error
|
|
466
|
+
const debugDir = await saveLoginQrDebug(page, element, `${debugLabel}-error`, saveDebug).catch(() => null);
|
|
465
467
|
console.error("captureQrFromPage failed:", error, "debugDir:", debugDir);
|
|
466
|
-
return { dataUrl: null, detected: false, debugDir };
|
|
468
|
+
return { dataUrl: null, detected: false, debugDir: debugDir ?? undefined };
|
|
467
469
|
}
|
|
468
470
|
}
|
|
469
471
|
async function loginWithQrViaService(ctx, opts = {}) {
|
|
@@ -477,7 +479,7 @@ async function loginWithQrViaService(ctx, opts = {}) {
|
|
|
477
479
|
try {
|
|
478
480
|
await opts.onPageCreated?.(page);
|
|
479
481
|
await gotoAndWait(page, WEIBO_PASSPORT_URL, opts.timeoutMs || 120000);
|
|
480
|
-
const qrResult = await captureLoginQrFromPage(page, opts.timeoutMs ? Math.min(opts.timeoutMs, 15000) : 15000);
|
|
482
|
+
const qrResult = await captureLoginQrFromPage(page, opts.timeoutMs ? Math.min(opts.timeoutMs, 15000) : 15000, opts.saveDebug ?? false);
|
|
481
483
|
if (!qrResult.detected) {
|
|
482
484
|
// Take a screenshot for debugging
|
|
483
485
|
const screenshot = await page.screenshot({ encoding: "base64" });
|