后端迁移会话页面模块
This commit is contained in:
+73
-21
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user