后端迁移会话页面模块

This commit is contained in:
yml
2026-05-21 15:30:25 +08:00
parent 3176880b2e
commit c7726dde32
2 changed files with 81 additions and 21 deletions
@@ -1,4 +1,4 @@
// @ts-nocheck
/// <reference lib="dom" />
import fs from 'node:fs/promises'
import path from 'node:path'
@@ -7,16 +7,68 @@ import { isRetryableQrCaptureError, logBrowserSessionDebug } from './session-log
import { captureQqQrImage, ensureQqLoginReady, extractQqQrState } from './session-qq.js'
import { captureWxQrImage, ensureWxLoginReady, extractWxQrState } from './session-wx.js'
type LoginType = 'qq' | 'wx' | string
type LocatorLike = {
count: () => Promise<number>
first: () => LocatorLike
click: (options?: { timeout?: number }) => Promise<void>
}
type BrowserPageLike = {
locator: (selector: string) => LocatorLike
waitForTimeout: (milliseconds: number) => Promise<void>
evaluate: <T = any>(callback: any) => Promise<T>
reload: (options?: { waitUntil?: string }) => Promise<unknown>
goto: (url: string, options?: { waitUntil?: string }) => Promise<unknown>
}
type SessionForQrCapture = {
page: BrowserPageLike
loginType: LoginType
sessionDir: string
qrImagePath?: string
qrImageBase64?: string
qrUpdatedAt?: string
updatedAt?: string
}
type CaptureSessionQrOptions = {
ensureLoginReady?: boolean
ensureLoginTab?: typeof ensureLoginTab
captureQrImageWithRetry?: typeof captureQrImageWithRetry
readFile?: (path: string, encoding: BufferEncoding) => Promise<string>
now?: () => string
}
type CaptureQrImageWithRetryOptions = {
captureQqQrImage?: (page: BrowserPageLike, qrImagePath: string) => Promise<unknown>
captureWxQrImage?: (page: BrowserPageLike, qrImagePath: string) => Promise<unknown>
ensureLoginTab?: typeof ensureLoginTab
isRetryableQrCaptureError?: (error: unknown) => boolean
logBrowserSessionDebug?: (scope: string, detail?: unknown) => void
}
type EnsureLoginTabOptions = {
ensureQqLoginReady?: (page: BrowserPageLike) => Promise<unknown>
ensureWxLoginReady?: (page: BrowserPageLike) => Promise<unknown>
}
type ExtractQrStateOptions = {
extractQqQrState?: (page: BrowserPageLike) => Promise<unknown>
extractWxQrState?: (page: BrowserPageLike) => Promise<unknown>
}
export async function captureSessionQr(
session,
session: SessionForQrCapture,
{
ensureLoginReady = true,
ensureLoginTab: ensureLoginTabImpl = ensureLoginTab,
captureQrImageWithRetry: captureQrImageWithRetryImpl = captureQrImageWithRetry,
readFile = fs.readFile,
now = () => new Date().toISOString(),
} = {},
) {
}: CaptureSessionQrOptions = {},
): Promise<void> {
if (ensureLoginReady) {
await ensureLoginTabImpl(session.page, session.loginType)
}
@@ -33,19 +85,19 @@ export async function captureSessionQr(
}
export async function captureQrImageWithRetry(
page,
loginType,
qrImagePath,
page: BrowserPageLike,
loginType: LoginType,
qrImagePath: string,
{
captureQqQrImage: captureQqQrImageImpl = captureQqQrImage,
captureWxQrImage: captureWxQrImageImpl = captureWxQrImage,
ensureLoginTab: ensureLoginTabImpl = ensureLoginTab,
isRetryableQrCaptureError: isRetryableQrCaptureErrorImpl = isRetryableQrCaptureError,
logBrowserSessionDebug: logBrowserSessionDebugImpl = logBrowserSessionDebug,
} = {},
) {
}: CaptureQrImageWithRetryOptions = {},
): Promise<void> {
const maxAttempts = loginType === 'qq' ? 4 : 3
let lastError = null
let lastError: unknown = null
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
try {
@@ -94,13 +146,13 @@ export async function captureQrImageWithRetry(
}
export async function ensureLoginTab(
page,
loginType,
page: BrowserPageLike,
loginType: LoginType,
{
ensureQqLoginReady: ensureQqLoginReadyImpl = ensureQqLoginReady,
ensureWxLoginReady: ensureWxLoginReadyImpl = ensureWxLoginReady,
} = {},
) {
}: EnsureLoginTabOptions = {},
): Promise<void> {
const tabSelector = loginType === 'wx' ? '.wx-tab' : '.qc-tab'
const loginButton = page.locator('#unlogin')
let interacted = false
@@ -139,9 +191,9 @@ export async function ensureLoginTab(
}
}
export async function extractHostState(page) {
export async function extractHostState(page: BrowserPageLike) {
return page.evaluate(() => {
const isVisible = (element) => {
const isVisible = (element: Element | null) => {
if (!element) {
return false
}
@@ -162,7 +214,7 @@ export async function extractHostState(page) {
})
}
export async function reloadActivityPageForPresentation(page, activityUrl) {
export async function reloadActivityPageForPresentation(page: BrowserPageLike, activityUrl: string): Promise<void> {
try {
await page.reload({ waitUntil: 'domcontentloaded' })
} catch {
@@ -173,13 +225,13 @@ export async function reloadActivityPageForPresentation(page, activityUrl) {
}
export async function extractQrState(
page,
loginType,
page: BrowserPageLike,
loginType: LoginType,
{
extractQqQrState: extractQqQrStateImpl = extractQqQrState,
extractWxQrState: extractWxQrStateImpl = extractWxQrState,
} = {},
) {
}: ExtractQrStateOptions = {},
): Promise<unknown> {
if (loginType === 'qq') {
return extractQqQrStateImpl(page)
}