Files
goodbuddy/src/main/channels/wechat-sidecar-client.ts
T

122 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
wechatSidecarCredentialMessageSchema,
wechatSidecarMessageSchema,
type WechatSidecarCommand,
type WechatSidecarCredentialMessage,
type WechatSidecarMessage,
type WechatSidecarStartAccountCommand
} from './wechat-sidecar-protocol'
export interface WechatSidecarChild {
postMessage(message: unknown): void
kill(): boolean
on(event: 'message', listener: (message: unknown) => void): this
once(
event: 'exit',
listener: (code: number | null) => void
): this
once(
event: 'error',
listener: (error: Error) => void
): this
}
export type WechatSidecarLauncher = () => WechatSidecarChild
type SidecarListener = (
message: WechatSidecarMessage | WechatSidecarCredentialMessage
) => void
export class WechatSidecarClient {
private child?: WechatSidecarChild
private readonly listeners = new Set<SidecarListener>()
private exitError?: Error
constructor(private readonly launch: WechatSidecarLauncher) {}
start(): void {
if (this.child) {
return
}
this.exitError = undefined
const child = this.launch()
this.child = child
child.on('message', (raw) => {
const payload =
raw !== null &&
typeof raw === 'object' &&
'data' in raw
? raw.data
: raw
const publicMessage = wechatSidecarMessageSchema.safeParse(payload)
if (publicMessage.success) {
this.publish(publicMessage.data)
return
}
const credential =
wechatSidecarCredentialMessageSchema.safeParse(payload)
if (credential.success) {
this.publish(credential.data)
}
})
child.once('error', (error) => {
this.exitError = new Error(
`微信 Sidecar 异常:${error.message.slice(0, 300)}`
)
this.publish({
type: 'status',
status: 'failed',
detail: this.exitError.message
})
})
child.once('exit', (code) => {
if (this.child !== child) {
return
}
this.child = undefined
if (code !== 0 && this.exitError === undefined) {
this.publish({
type: 'status',
status: 'failed',
detail: `微信 Sidecar 已退出(${code ?? '未知状态'})`
})
}
})
}
send(
command: WechatSidecarCommand | WechatSidecarStartAccountCommand
): void {
if (!this.child) {
throw this.exitError ?? new Error('微信 Sidecar 尚未启动')
}
this.child.postMessage(command)
}
subscribe(listener: SidecarListener): () => void {
this.listeners.add(listener)
return () => this.listeners.delete(listener)
}
stop(): void {
const child = this.child
this.child = undefined
if (!child) {
return
}
try {
child.postMessage({ type: 'shutdown' })
} finally {
setTimeout(() => child.kill(), 2_000).unref()
}
}
private publish(
message: WechatSidecarMessage | WechatSidecarCredentialMessage
): void {
for (const listener of this.listeners) {
listener(message)
}
}
}