Files
test1/remote-control/bots/dingtalk/index.ts
2026-03-20 07:33:46 +00:00

143 lines
4.4 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 { DWClient, TOPIC_ROBOT, EventAck } from 'dingtalk-stream'
import { handleRobotCallback } from './handlers'
import { closeConnectionsForPlatform } from '@/remote-control/shared/nova-bridge'
import { BotLogger } from '@/remote-control/shared/logger'
import { ConfigManager, type ConfigChangeEvent } from '@/remote-control/config/manager'
let client: DWClient | null = null
let currentClientId: string = ''
let currentClientSecret: string = ''
let startTime: number = 0
let messagesProcessed: number = 0
let lastError: Error | null = null
export interface BotStatus {
platform: string
status: 'connected' | 'disconnected' | 'connecting'
uptime?: number
messagesProcessed: number
error?: string
}
export async function startBot(clientId: string, clientSecret: string): Promise<void> {
if (!clientId || !clientSecret) {
const msg = '钉钉 Client ID 或 Client Secret 为空,跳过启动'
console.warn(`[DingTalk Bot] ${msg}`)
BotLogger.log({ platform: 'dingtalk', eventType: 'error', severity: 'warning', message: msg })
return
}
// 幂等检查:如果已有活跃连接且凭据未变,直接跳过
if (client && currentClientId === clientId && currentClientSecret === clientSecret) {
console.log('[DingTalk Bot] 已有活跃连接,跳过重复启动')
return
}
// 如果已有旧连接(凭据变更),先停止
if (client) {
console.log('[DingTalk Bot] 凭据已变更,先停止旧连接')
await stopBot()
}
currentClientId = clientId
currentClientSecret = clientSecret
try {
const newClient = new DWClient({
clientId,
clientSecret,
debug: false,
})
newClient.registerCallbackListener(TOPIC_ROBOT, handleRobotCallback(newClient, () => { messagesProcessed++ }))
newClient.registerAllEventListener(() => {
return { status: EventAck.SUCCESS }
})
// 先设置 client再 connect保证闭包和模块变量指向同一实例
client = newClient
client.connect()
startTime = Date.now()
lastError = null
console.log('[DingTalk Bot] 已启动,等待消息...')
BotLogger.log({
platform: 'dingtalk',
eventType: 'connection',
severity: 'info',
message: '钉钉 Bot 已启动',
})
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err))
lastError = error
client = null
currentClientId = ''
currentClientSecret = ''
console.error('[DingTalk Bot] 启动失败:', error.message)
BotLogger.log({
platform: 'dingtalk',
eventType: 'error',
severity: 'error',
message: `钉钉 Bot 启动失败: ${error.message}`,
details: { hint: '请检查 Client ID 和 Client Secret 是否正确' },
})
throw error
}
}
export async function stopBot(): Promise<void> {
closeConnectionsForPlatform('dingtalk')
if (client) {
client.disconnect()
client = null
currentClientId = ''
currentClientSecret = ''
startTime = 0
console.log('[DingTalk Bot] 已停止')
BotLogger.log({
platform: 'dingtalk',
eventType: 'disconnection',
severity: 'info',
message: '钉钉 Bot 已停止',
})
}
}
export function getStatus(): BotStatus {
return {
platform: 'dingtalk',
status: client ? 'connected' : 'disconnected',
uptime: client && startTime ? Math.floor((Date.now() - startTime) / 1000) : undefined,
messagesProcessed,
error: lastError?.message,
}
}
const configManager = ConfigManager.getInstance()
configManager.on('config-changed', async (event: ConfigChangeEvent) => {
const { newConfig } = event
const platformConfig = newConfig.dingtalk
try {
if (platformConfig.enabled && !client) {
await startBot(platformConfig.clientId, platformConfig.clientSecret)
} else if (!platformConfig.enabled && client) {
await stopBot()
} else if (platformConfig.enabled && client) {
if (platformConfig.clientId !== currentClientId || platformConfig.clientSecret !== currentClientSecret) {
await stopBot()
await startBot(platformConfig.clientId, platformConfig.clientSecret)
}
}
lastError = null
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error))
BotLogger.log({
platform: 'dingtalk',
eventType: 'error',
severity: 'error',
message: `配置变更处理失败: ${lastError.message}`,
})
}
})