后端迁移会话活动信息模块

This commit is contained in:
yml
2026-05-21 15:37:15 +08:00
parent 150de31c74
commit e88b9d18fd
2 changed files with 102 additions and 17 deletions
@@ -1,9 +1,83 @@
// @ts-nocheck /// <reference lib="dom" />
import { extractHostState, reloadActivityPageForPresentation } from './session-page.js' import { extractHostState, reloadActivityPageForPresentation } from './session-page.js'
import { shouldRefreshLoggedInPresentation } from './session-state.js' import { shouldRefreshLoggedInPresentation } from './session-state.js'
export function createEmptyActivityInfo() { type BrowserPageLike = {
evaluate: <T = any>(callback: any) => Promise<T>
waitForTimeout: (milliseconds: number) => Promise<void>
}
type HostState = {
loginText?: string
unloginVisible?: boolean
loginedVisible?: boolean
}
export type ActivityInfo = {
nickname: string
role: {
ready: boolean
roleName: string
roleId: string
area: string
partition: string
platId: string
md5str: string
checkparam: string
}
form: {
cdkeyInputId: string
cdkeyValue: string
verifyInputId: string
verifyValue: string
verifyImgId: string
submitId: string
}
verify: {
visible: boolean
src: string
naturalWidth: number
naturalHeight: number
}
popup: {
visible: boolean
text: string
detail: string
}
}
type SessionForPresentation = {
page: BrowserPageLike
presentationSyncAttempts: number
}
type EnsureLoggedInPresentationOptions = {
hostState?: HostState | null
credentialReady?: boolean
activityUrl?: string
extractHostState?: (page: BrowserPageLike) => Promise<HostState>
reloadActivityPageForPresentation?: (page: BrowserPageLike, activityUrl: string) => Promise<unknown>
shouldRefreshLoggedInPresentation?: (hostState: HostState) => boolean
}
type EnsureActivityInfoReadyOptions = {
triggerRender?: boolean
timeoutMs?: number
extractActivityInfo?: (page: BrowserPageLike) => Promise<ActivityInfo>
triggerActivityRoleRender?: (page: BrowserPageLike) => Promise<unknown>
now?: () => number
}
declare global {
interface Window {
renderCdkey?: () => void
roleData?: Record<string, unknown>
isRole?: unknown
}
}
export function createEmptyActivityInfo(): ActivityInfo {
return { return {
nickname: '', nickname: '',
role: { role: {
@@ -39,7 +113,7 @@ export function createEmptyActivityInfo() {
} }
export async function ensureLoggedInPresentation( export async function ensureLoggedInPresentation(
session, session: SessionForPresentation,
{ {
hostState = null, hostState = null,
credentialReady = false, credentialReady = false,
@@ -47,8 +121,8 @@ export async function ensureLoggedInPresentation(
extractHostState: extractHostStateImpl = extractHostState, extractHostState: extractHostStateImpl = extractHostState,
reloadActivityPageForPresentation: reloadActivityPageForPresentationImpl = reloadActivityPageForPresentation, reloadActivityPageForPresentation: reloadActivityPageForPresentationImpl = reloadActivityPageForPresentation,
shouldRefreshLoggedInPresentation: shouldRefreshLoggedInPresentationImpl = shouldRefreshLoggedInPresentation, shouldRefreshLoggedInPresentation: shouldRefreshLoggedInPresentationImpl = shouldRefreshLoggedInPresentation,
} = {}, }: EnsureLoggedInPresentationOptions = {},
) { ): Promise<{ reloaded: boolean; hostState: HostState }> {
let nextHostState = hostState || (await extractHostStateImpl(session.page)) let nextHostState = hostState || (await extractHostStateImpl(session.page))
if (!credentialReady || !shouldRefreshLoggedInPresentationImpl(nextHostState)) { if (!credentialReady || !shouldRefreshLoggedInPresentationImpl(nextHostState)) {
@@ -76,15 +150,15 @@ export async function ensureLoggedInPresentation(
} }
export async function ensureActivityInfoReady( export async function ensureActivityInfoReady(
page, page: BrowserPageLike,
{ {
triggerRender = false, triggerRender = false,
timeoutMs = 8_000, timeoutMs = 8_000,
extractActivityInfo: extractActivityInfoImpl = extractActivityInfo, extractActivityInfo: extractActivityInfoImpl = extractActivityInfo,
triggerActivityRoleRender: triggerActivityRoleRenderImpl = triggerActivityRoleRender, triggerActivityRoleRender: triggerActivityRoleRenderImpl = triggerActivityRoleRender,
now = () => Date.now(), now = () => Date.now(),
} = {}, }: EnsureActivityInfoReadyOptions = {},
) { ): Promise<ActivityInfo> {
if (triggerRender) { if (triggerRender) {
await triggerActivityRoleRenderImpl(page) await triggerActivityRoleRenderImpl(page)
} }
@@ -104,7 +178,7 @@ export async function ensureActivityInfoReady(
return lastInfo return lastInfo
} }
export async function triggerActivityRoleRender(page) { export async function triggerActivityRoleRender(page: BrowserPageLike): Promise<void> {
try { try {
await page.evaluate(() => { await page.evaluate(() => {
const maybeRender = window.renderCdkey const maybeRender = window.renderCdkey
@@ -118,11 +192,11 @@ export async function triggerActivityRoleRender(page) {
} }
} }
export async function extractActivityInfo(page) { export async function extractActivityInfo(page: BrowserPageLike): Promise<ActivityInfo> {
try { try {
return await page.evaluate(() => { return await page.evaluate(() => {
const asString = (value) => (typeof value === 'string' ? value.trim() : '') const asString = (value: unknown) => (typeof value === 'string' ? value.trim() : '')
const isVisible = (element) => { const isVisible = (element: Element | null) => {
if (!element) { if (!element) {
return false return false
} }
@@ -131,8 +205,11 @@ export async function extractActivityInfo(page) {
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'
} }
const pick = (selector) => document.querySelector(selector) const pick = (selector: string) => document.querySelector(selector)
const pickBest = (selector, predicate = null) => { const pickBest = (
selector: string,
predicate: ((element: Element) => boolean) | null = null,
): Element | null => {
const matches = [...document.querySelectorAll(selector)] const matches = [...document.querySelectorAll(selector)]
const preferred = matches.find((element) => { const preferred = matches.find((element) => {
if (predicate && !predicate(element)) { if (predicate && !predicate(element)) {
@@ -170,7 +247,7 @@ export async function extractActivityInfo(page) {
const roleSelector = pick('[id^="milo_role_selector_"]') const roleSelector = pick('[id^="milo_role_selector_"]')
const roleNameText = const roleNameText =
textOf('#role_name') || textOf('#role_name') ||
asString(roleSelector?.selectedOptions?.[0]?.textContent || '') asString(roleSelector instanceof HTMLSelectElement ? roleSelector.selectedOptions?.[0]?.textContent || '' : '')
const verifyImg = pickBest('[id^="milo_verifyImg_"]', (node) => node instanceof HTMLImageElement) const verifyImg = pickBest('[id^="milo_verifyImg_"]', (node) => node instanceof HTMLImageElement)
const popup = pick('#pop2') const popup = pick('#pop2')
const cdkeyInput = pickBest( const cdkeyInput = pickBest(
@@ -215,8 +292,8 @@ export async function extractActivityInfo(page) {
verify: { verify: {
visible: isVisible(verifyImg), visible: isVisible(verifyImg),
src: asString(verifyImg?.getAttribute('src')), src: asString(verifyImg?.getAttribute('src')),
naturalWidth: Number(verifyImg?.naturalWidth || 0), naturalWidth: verifyImg instanceof HTMLImageElement ? Number(verifyImg.naturalWidth || 0) : 0,
naturalHeight: Number(verifyImg?.naturalHeight || 0), naturalHeight: verifyImg instanceof HTMLImageElement ? Number(verifyImg.naturalHeight || 0) : 0,
}, },
popup: { popup: {
visible: isVisible(popup), visible: isVisible(popup),
@@ -488,6 +488,14 @@
- `npm run typecheck` - `npm run typecheck`
- `npm run build` - `npm run build`
- `npm test` 共 139 个用例通过 - `npm test` 共 139 个用例通过
104. 腾讯浏览器会话 activity 信息模块迁移到 `.ts`
- `src/services/session/session-activity.ts`
105. 活动角色 / 表单 / 验证码 / 弹窗信息结构、登录态 presentation 同步、角色渲染触发与轮询已显式类型化
106. Docker 内验证通过:
- `src/services/session/session.test.js` 共 14 个用例通过
- `npm run typecheck`
- `npm run build`
- `npm test` 共 139 个用例通过
## 下一步建议 ## 下一步建议