@dcrays/dcgchat-test 0.6.12 → 0.6.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/index.js +305 -0
  2. package/openclaw.plugin.json +41 -12
  3. package/package.json +8 -35
  4. package/README.md +0 -167
  5. package/index.ts +0 -35
  6. package/src/agent.ts +0 -568
  7. package/src/bot.ts +0 -646
  8. package/src/channel/index.ts +0 -329
  9. package/src/channel/outboundMedia.ts +0 -144
  10. package/src/channel/outboundTarget.ts +0 -62
  11. package/src/channel/uploadMediaUrl.ts +0 -76
  12. package/src/cron/message.ts +0 -114
  13. package/src/cron/params.ts +0 -164
  14. package/src/cron/toolGuard.ts +0 -466
  15. package/src/cron/types.ts +0 -15
  16. package/src/gateway/index.ts +0 -430
  17. package/src/gateway/security.ts +0 -95
  18. package/src/gateway/socket.ts +0 -256
  19. package/src/libs/ali-oss-6.23.0.tgz +0 -0
  20. package/src/libs/axios-1.13.6.tgz +0 -0
  21. package/src/libs/md5-2.3.0.tgz +0 -0
  22. package/src/libs/mime-types-3.0.2.tgz +0 -0
  23. package/src/libs/unzipper-0.12.3.tgz +0 -0
  24. package/src/libs/ws-8.19.0.tgz +0 -0
  25. package/src/monitor.ts +0 -269
  26. package/src/request/api.ts +0 -80
  27. package/src/request/oss.ts +0 -200
  28. package/src/request/request.ts +0 -191
  29. package/src/request/userInfo.ts +0 -44
  30. package/src/session.ts +0 -28
  31. package/src/skill.ts +0 -114
  32. package/src/tool.ts +0 -603
  33. package/src/tools/messageTool.ts +0 -334
  34. package/src/tools/toolCallGuard.ts +0 -327
  35. package/src/transport.ts +0 -217
  36. package/src/types.ts +0 -139
  37. package/src/utils/agentErrors.ts +0 -116
  38. package/src/utils/constant.ts +0 -60
  39. package/src/utils/env-config.ts +0 -19
  40. package/src/utils/formatLlmInputEvent.ts +0 -48
  41. package/src/utils/gatewayMsgHandler.ts +0 -147
  42. package/src/utils/global.ts +0 -309
  43. package/src/utils/inboundTurnState.ts +0 -66
  44. package/src/utils/log.ts +0 -77
  45. package/src/utils/mediaAttached.ts +0 -244
  46. package/src/utils/mediaEmitter.ts +0 -54
  47. package/src/utils/outboundAssistantText.ts +0 -117
  48. package/src/utils/params.ts +0 -104
  49. package/src/utils/passTxt.ts +0 -4
  50. package/src/utils/resolveRegisterConfig.ts +0 -33
  51. package/src/utils/searchFile.ts +0 -228
  52. package/src/utils/sessionState.ts +0 -137
  53. package/src/utils/sessionTermination.ts +0 -228
  54. package/src/utils/streamMerge.ts +0 -150
  55. package/src/utils/subagentRunMap.ts +0 -71
  56. package/src/utils/undiciFetchInterceptor.ts +0 -346
  57. package/src/utils/workspaceFilePaths.ts +0 -18
  58. package/src/utils/wsMessageHandler.ts +0 -124
  59. package/src/utils/zipExtract.ts +0 -97
  60. package/src/utils/zipPath.ts +0 -24
@@ -1,256 +0,0 @@
1
- import type { OpenClawConfig } from 'openclaw/plugin-sdk'
2
- import { getDcgchatRuntime, getOpenClawConfig } from '../utils/global.js'
3
- import { GatewayConnection, type GatewayConfig } from './index.js'
4
- import { dcgLogger } from '../utils/log.js'
5
- import { openclawConfigLooksUsable } from '../utils/resolveRegisterConfig.js'
6
- import { WS_HEARTBEAT_INTERVAL_MS, WS_RECONNECT_DELAY_MS } from '../utils/constant.js'
7
-
8
- type OpenClawGatewaySection = {
9
- port?: number
10
- bind?: string
11
- tls?: { enabled?: boolean }
12
- auth?: { mode?: string; token?: string }
13
- remote?: { url?: string }
14
- }
15
-
16
- function getGatewaySection(cfg: OpenClawConfig): OpenClawGatewaySection | undefined {
17
- return (cfg as OpenClawConfig & { gateway?: OpenClawGatewaySection }).gateway
18
- }
19
-
20
- /**
21
- * 从 OpenClaw 配置解析本地/远程 Gateway WebSocket 与 token。
22
- * 可用环境变量覆盖:OPENCLAW_GATEWAY_URL、OPENCLAW_GATEWAY_TOKEN。
23
- */
24
- export function resolveGatewayClientConfig(cfg: OpenClawConfig | null): GatewayConfig {
25
- if (!cfg) {
26
- throw new Error('OpenClaw 配置未初始化(需先完成插件 register)')
27
- }
28
- const g = getGatewaySection(cfg)
29
- const token = process.env.OPENCLAW_GATEWAY_TOKEN || g?.auth?.token || ''
30
- if (!token) {
31
- throw new Error('缺少 Gateway token:请在 openclaw.json 的 gateway.auth.token 设置,或设置环境变量 OPENCLAW_GATEWAY_TOKEN')
32
- }
33
-
34
- const scheme = g?.tls?.enabled === true ? 'wss' : 'ws'
35
- const url = process.env.OPENCLAW_GATEWAY_URL || g?.remote?.url || `${scheme}://127.0.0.1:${g?.port ?? 18789}`
36
-
37
- return {
38
- url,
39
- token,
40
- role: 'operator',
41
- scopes: ['operator.admin']
42
- }
43
- }
44
- /** register 尚未写入全局缓存时,回退到 runtime.loadConfig(与 registerFull 内解析方式一致) */
45
- function resolveOpenClawConfigForPersistentGateway(): OpenClawConfig | null {
46
- const cached = getOpenClawConfig()
47
- if (openclawConfigLooksUsable(cached)) return cached
48
- try {
49
- const fresh = getDcgchatRuntime().config.loadConfig() as OpenClawConfig
50
- return openclawConfigLooksUsable(fresh) ? fresh : null
51
- } catch {
52
- return null
53
- }
54
- }
55
-
56
- function resolveConfigSafe(): GatewayConfig | null {
57
- try {
58
- const cfg = resolveOpenClawConfigForPersistentGateway()
59
- if (!cfg) {
60
- dcgLogger('Gateway 持久连接未启动: 无可用 OpenClaw 配置(plugins.register 未完成或 loadConfig 失败)', 'error')
61
- return null
62
- }
63
- return resolveGatewayClientConfig(cfg)
64
- } catch (e) {
65
- dcgLogger(`Gateway 持久连接未启动: ${e}`, 'error')
66
- return null
67
- }
68
- }
69
-
70
- /** 任意 method + params,由调用方决定载荷 */
71
- export type GatewayRpcPayload = {
72
- method: string
73
- params?: Record<string, unknown>
74
- }
75
-
76
- let persistentConn: GatewayConnection | null = null
77
- let pingTimer: ReturnType<typeof setInterval> | null = null
78
- let reconnectTimer: ReturnType<typeof setTimeout> | null = null
79
- /** register 可能被调用多次,只保留一个「延迟首次连接」定时器,避免同一时刻触发两次 connect */
80
- let startupConnectTimer: ReturnType<typeof setTimeout> | null = null
81
- let connectInFlight = false
82
- let socketStopped = true
83
- /** 用于忽略「已被替换的旧连接」上的 close/error */
84
- let socketGeneration = 0
85
-
86
- function clearPingTimer(): void {
87
- if (pingTimer) {
88
- clearInterval(pingTimer)
89
- pingTimer = null
90
- }
91
- }
92
-
93
- function clearReconnectTimer(): void {
94
- if (reconnectTimer) {
95
- clearTimeout(reconnectTimer)
96
- reconnectTimer = null
97
- }
98
- }
99
-
100
- function startPingTimer(gw: GatewayConnection): void {
101
- clearPingTimer()
102
- pingTimer = setInterval(() => {
103
- if (!gw.isConnected()) return
104
- try {
105
- gw.ping()
106
- } catch (e) {
107
- dcgLogger(`Gateway ping 失败: ${e}`, 'error')
108
- }
109
- }, WS_HEARTBEAT_INTERVAL_MS)
110
- }
111
-
112
- function scheduleReconnect(): void {
113
- if (socketStopped || reconnectTimer) return
114
- reconnectTimer = setTimeout(() => {
115
- reconnectTimer = null
116
- void connectPersistentGateway()
117
- }, WS_RECONNECT_DELAY_MS)
118
- }
119
-
120
- function attachSocketLifecycle(gw: GatewayConnection, generation: number): void {
121
- const ws = gw.getWebSocket()
122
- if (!ws) return
123
-
124
- const onDown = () => {
125
- if (generation !== socketGeneration) return
126
- clearPingTimer()
127
- if (persistentConn === gw) {
128
- persistentConn = null
129
- }
130
- dcgLogger('Gateway WebSocket 已断开,将在 5s 后重连', 'error')
131
- if (!socketStopped) {
132
- scheduleReconnect()
133
- }
134
- }
135
-
136
- ws.once('close', onDown)
137
- ws.once('error', onDown)
138
- }
139
-
140
- async function connectPersistentGateway(): Promise<void> {
141
- if (socketStopped) return
142
- if (persistentConn?.isConnected()) return
143
- if (connectInFlight) return
144
-
145
- const cfg = resolveConfigSafe()
146
- if (!cfg) return
147
-
148
- connectInFlight = true
149
- try {
150
- socketGeneration += 1
151
- const generation = socketGeneration
152
-
153
- if (persistentConn) {
154
- try {
155
- persistentConn.close()
156
- } catch {
157
- /* ignore */
158
- }
159
- persistentConn = null
160
- }
161
-
162
- const gw = new GatewayConnection(cfg)
163
- await gw.connect()
164
- if (socketStopped) {
165
- try {
166
- gw.close()
167
- } catch {
168
- /* ignore */
169
- }
170
- return
171
- }
172
- if (generation !== socketGeneration) {
173
- try {
174
- gw.close()
175
- } catch {
176
- /* ignore */
177
- }
178
- return
179
- }
180
- persistentConn = gw
181
- attachSocketLifecycle(gw, generation)
182
- startPingTimer(gw)
183
- dcgLogger(`Gateway 持久连接成功 connId=${gw.getConnId() ?? '?'}`)
184
- } catch (e) {
185
- dcgLogger(`Gateway 连接失败: ${e}`, 'error')
186
- persistentConn = null
187
- clearPingTimer()
188
- if (!socketStopped) {
189
- scheduleReconnect()
190
- }
191
- } finally {
192
- connectInFlight = false
193
- }
194
- }
195
-
196
- /**
197
- * 插件 register 后调用:建立到 OpenClaw Gateway 的长连接,30s 一次 WebSocket ping,断线每 5s 重连。
198
- */
199
- export function startDcgchatGatewaySocket(): void {
200
- socketStopped = false
201
- clearReconnectTimer()
202
- if (startupConnectTimer != null) return
203
- startupConnectTimer = setTimeout(() => {
204
- startupConnectTimer = null
205
- void connectPersistentGateway()
206
- }, 0)
207
- }
208
-
209
- export function isDcgchatGatewaySocketConnected(): boolean {
210
- return persistentConn?.isConnected() ?? false
211
- }
212
-
213
- /** `gateway_start` 等场景下,持久连接可能仍在异步 connect;轮询至连通或超时 */
214
- const DEFAULT_WAIT_GATEWAY_MS = 120_000
215
- const DEFAULT_WAIT_GATEWAY_POLL_MS = 150
216
-
217
- export async function waitForDcgchatGatewaySocketConnected(opts?: {
218
- timeoutMs?: number
219
- intervalMs?: number
220
- signal?: AbortSignal
221
- }): Promise<boolean> {
222
- const timeoutMs = opts?.timeoutMs ?? DEFAULT_WAIT_GATEWAY_MS
223
- const intervalMs = opts?.intervalMs ?? DEFAULT_WAIT_GATEWAY_POLL_MS
224
- const deadline = Date.now() + timeoutMs
225
- for (;;) {
226
- if (opts?.signal?.aborted) return false
227
- if (isDcgchatGatewaySocketConnected()) return true
228
- if (Date.now() >= deadline) return isDcgchatGatewaySocketConnected()
229
- await new Promise<void>((r) => setTimeout(r, intervalMs))
230
- }
231
- }
232
-
233
- /**
234
- * 在已连接时调用 Gateway RPC;`params` 完全由调用方传入,不做固定结构。
235
- */
236
- export async function callGatewayMethod<T = unknown>(
237
- method: string,
238
- params: Record<string, unknown> = {},
239
- opts?: { timeoutMs?: number }
240
- ): Promise<T> {
241
- const gw = persistentConn
242
- if (!gw?.isConnected()) {
243
- throw new Error('Gateway 未连接(等待重连或检查配置)')
244
- }
245
- return gw.callMethod<T>(method, params, opts)
246
- }
247
-
248
- /**
249
- * 使用对象形式发送 RPC。
250
- */
251
- export async function sendGatewayRpc<T = unknown>(
252
- payload: GatewayRpcPayload,
253
- opts?: { timeoutMs?: number }
254
- ): Promise<T> {
255
- return callGatewayMethod<T>(payload.method, payload.params ?? {}, opts)
256
- }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/monitor.ts DELETED
@@ -1,269 +0,0 @@
1
- import type { OpenClawConfig, RuntimeEnv } from 'openclaw/plugin-sdk'
2
- import WebSocket from 'ws'
3
- import { resolveAccount } from './channel/index.js'
4
- import type { ResolvedDcgchatAccount } from './types.js'
5
- import { setWsConnection, getOpenClawConfig, compressFields } from './utils/global.js'
6
- import { dcgLogger } from './utils/log.js'
7
- import { isWsOpen } from './transport.js'
8
- import { handleParsedWsMessage } from './utils/wsMessageHandler.js'
9
- import { WS_HEARTBEAT_INTERVAL_MS, WS_RECONNECT_DELAY_MS } from './utils/constant.js'
10
-
11
- export type MonitorDcgchatOpts = {
12
- config?: OpenClawConfig
13
- runtime?: RuntimeEnv
14
- abortSignal?: AbortSignal
15
- accountId?: string
16
- statusSink?: (patch: {
17
- connected?: boolean
18
- running?: boolean
19
- lastConnectedAt?: number | null
20
- lastDisconnect?:
21
- | string
22
- | {
23
- at: number
24
- status?: number
25
- error?: string
26
- loggedOut?: boolean
27
- }
28
- | null
29
- lastError?: string | null
30
- lastStartAt?: number | null
31
- lastStopAt?: number | null
32
- lastInboundAt?: number | null
33
- lastOutboundAt?: number | null
34
- reconnectAttempts?: number
35
- healthState?: string
36
- }) => void
37
- }
38
-
39
- function buildConnectUrl(account: ResolvedDcgchatAccount): string {
40
- const { wsUrl, botToken, userId, domainId, appId } = account
41
- const url = new URL(wsUrl)
42
- if (botToken) url.searchParams.set('bot_token', botToken)
43
- if (userId) url.searchParams.set('_userId', userId)
44
- url.searchParams.set('_domainId', domainId || '1000')
45
- url.searchParams.set('_appId', appId || '100')
46
- return url.toString()
47
- }
48
-
49
- export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<void> {
50
- const { abortSignal, accountId } = opts
51
-
52
- const config = getOpenClawConfig()
53
- if (!config) {
54
- dcgLogger('no OpenClaw config available', 'error')
55
- return
56
- }
57
-
58
- const account = resolveAccount(config, accountId ?? 'default')
59
-
60
- if (!account.wsUrl) {
61
- dcgLogger(` wsUrl not configured`, 'error')
62
- return
63
- }
64
-
65
- let shouldReconnect = true
66
- let ws: WebSocket | null = null
67
- let heartbeatTimer: ReturnType<typeof setInterval> | null = null
68
- let heartbeatLogCounter = 0
69
- let lastReportedStatus: 'connecting' | 'connected' | 'disconnected' | 'error' | '' = ''
70
-
71
- const isOpenclawBotHeartbeat = (raw: WebSocket.RawData, parsedMsg: { messageType?: string }): boolean => {
72
- if (parsedMsg?.messageType === 'openclaw_bot_heartbeat') return true
73
- if (typeof raw === 'object' && raw !== null && !Buffer.isBuffer(raw)) {
74
- return (raw as { messageType?: string }).messageType === 'openclaw_bot_heartbeat'
75
- }
76
- return false
77
- }
78
-
79
- const stopHeartbeat = () => {
80
- if (heartbeatTimer) {
81
- clearInterval(heartbeatTimer)
82
- heartbeatTimer = null
83
- }
84
- }
85
-
86
- const reportChannelStatus = (status: 'connecting' | 'connected' | 'disconnected' | 'error', extra?: Record<string, unknown>) => {
87
- if (lastReportedStatus === status) return
88
- lastReportedStatus = status
89
- if (!opts.statusSink) return
90
-
91
- if (status === 'connecting') {
92
- opts.statusSink({
93
- running: true,
94
- lastError: null,
95
- lastDisconnect: null
96
- })
97
- return
98
- }
99
-
100
- if (status === 'connected') {
101
- opts.statusSink({
102
- running: true,
103
- connected: true,
104
- lastConnectedAt: Date.now(),
105
- lastError: null,
106
- lastDisconnect: null
107
- })
108
- return
109
- }
110
-
111
- if (status === 'disconnected') {
112
- opts.statusSink({
113
- connected: false,
114
- lastDisconnect: {
115
- at: Date.now(),
116
- status: typeof extra?.code === 'number' ? extra.code : undefined,
117
- error: typeof extra?.reason === 'string' ? extra.reason : undefined
118
- }
119
- })
120
- return
121
- }
122
-
123
- opts.statusSink({
124
- connected: false,
125
- lastError: typeof extra?.error === 'string' ? extra.error : String(extra?.error ?? 'WebSocket error')
126
- })
127
- }
128
-
129
- function onHeartbeat() {
130
- if (isWsOpen()) {
131
- const heartbeat = {
132
- messageType: 'openclaw_bot_heartbeat',
133
- _userId: Number(account.userId) || 0,
134
- source: 'client',
135
- content: {
136
- bot_token: account.botToken,
137
- status: '1'
138
- }
139
- }
140
- ws?.send(JSON.stringify(heartbeat))
141
- }
142
- }
143
- const startHeartbeat = () => {
144
- stopHeartbeat()
145
- heartbeatTimer = setInterval(() => {
146
- onHeartbeat()
147
- }, WS_HEARTBEAT_INTERVAL_MS)
148
- }
149
-
150
- /** 移除当前 ws 实例上的事件监听器,防止重连时在旧实例上累积监听器 */
151
- let removeWsListeners: (() => void) | null = null
152
-
153
- const connect = () => {
154
- if (!shouldReconnect) return
155
-
156
- // 清理旧 ws 实例上的监听器(若有)
157
- if (removeWsListeners) {
158
- removeWsListeners()
159
- removeWsListeners = null
160
- }
161
-
162
- const start = Date.now()
163
- const connectUrl = buildConnectUrl(account)
164
- dcgLogger(`connecting to ${connectUrl}`)
165
- ws = new WebSocket(connectUrl)
166
-
167
- const currentWs = ws
168
-
169
- const onOpen = () => {
170
- dcgLogger(`socket connected, 用时: ${ Date.now() - start}ms`,)
171
- setWsConnection(currentWs)
172
- reportChannelStatus('connecting')
173
- startHeartbeat()
174
- onHeartbeat()
175
- }
176
-
177
- const onMessage = async (data: WebSocket.RawData) => {
178
- let parsed: { messageType?: string; content: any }
179
- try {
180
- parsed = JSON.parse(data.toString())
181
- if (parsed.messageType !== 'openclaw_bot_heartbeat') {
182
- dcgLogger(`[后端=>插件]-[消息解析成功]: ${JSON.stringify(compressFields(parsed, ['title']))}`)
183
- }
184
- } catch {
185
- dcgLogger(`[后端=>插件]-[消息解析失败]: invalid JSON received`, 'error')
186
- return
187
- }
188
-
189
- const payloadStr = data.toString()
190
- const heartbeat = isOpenclawBotHeartbeat(data, parsed)
191
- if (heartbeat) {
192
- heartbeatLogCounter += 1
193
- if (heartbeatLogCounter % 10 === 0 || heartbeatLogCounter === 1) {
194
- dcgLogger(`心跳链接, ${payloadStr}`)
195
- }
196
- reportChannelStatus('connected')
197
- return
198
- }
199
-
200
- try {
201
- parsed.content = JSON.parse(parsed.content)
202
- } catch {
203
- dcgLogger(`用户消息content字段解析失败: invalid JSON received`, 'error')
204
- return
205
- }
206
- try {
207
- await handleParsedWsMessage(parsed, payloadStr, account.accountId)
208
- } catch (err) {
209
- dcgLogger(`WS onMessage: handleParsedWsMessage failed: ${String(err)}`, 'error')
210
- }
211
- }
212
-
213
- const onClose = (code: number, reason: Buffer) => {
214
- stopHeartbeat()
215
- // 移除当前 ws 上的所有监听器
216
- currentWs.removeListener('message', onMessage)
217
- currentWs.removeListener('close', onClose)
218
- currentWs.removeListener('error', onError)
219
- currentWs.removeListener('open', onOpen)
220
- removeWsListeners = null
221
- setWsConnection(null)
222
- reportChannelStatus('disconnected', { code, reason: reason?.toString() || '' })
223
- dcgLogger(`disconnected (code=${code}, reason=${reason?.toString() || ''})`, 'error')
224
- if (shouldReconnect) {
225
- dcgLogger(`reconnecting in ${WS_RECONNECT_DELAY_MS}ms...`)
226
- setTimeout(connect, WS_RECONNECT_DELAY_MS)
227
- }
228
- }
229
-
230
- const onError = (e: Error) => {
231
- reportChannelStatus('error', { error: String(e) })
232
- dcgLogger(`WebSocket error: ${String(e)}`, 'error')
233
- }
234
-
235
- currentWs.on('open', onOpen)
236
- currentWs.on('message', onMessage)
237
- currentWs.on('close', onClose)
238
- currentWs.on('error', onError)
239
-
240
- removeWsListeners = () => {
241
- currentWs.removeListener('open', onOpen)
242
- currentWs.removeListener('message', onMessage)
243
- currentWs.removeListener('close', onClose)
244
- currentWs.removeListener('error', onError)
245
- }
246
- }
247
-
248
- connect()
249
-
250
- await new Promise<void>((resolve) => {
251
- if (abortSignal?.aborted) {
252
- shouldReconnect = false
253
- ws?.close()
254
- resolve()
255
- return
256
- }
257
- abortSignal?.addEventListener(
258
- 'abort',
259
- () => {
260
- dcgLogger(`socket stopping`)
261
- stopHeartbeat()
262
- shouldReconnect = false
263
- ws?.close()
264
- resolve()
265
- },
266
- { once: true }
267
- )
268
- })
269
- }
@@ -1,80 +0,0 @@
1
- import { post } from './request.js'
2
- import type { IStsToken, IStsTokenReq } from '../types.js'
3
- import { getUserTokenCache, setUserTokenCache } from './userInfo.js'
4
- import { getParamsDefaults } from '../utils/params.js'
5
- import { dcgLogger } from '../utils/log.js'
6
-
7
- export const getStsToken = async (name: string, botToken: string, isPrivate: 1 | 0) => {
8
- // 确保 userToken 已缓存(如果未缓存会自动获取并缓存)
9
- await getUserToken(botToken)
10
-
11
- try {
12
- const response = await post<IStsTokenReq, IStsToken>('/user/getStsTokenByAsync', { sourceFileName: name, isPrivate }, { botToken })
13
-
14
- if (!response || !response.data || !response.data.bucket) {
15
- throw new Error(`获取 OSS 临时凭证失败: response=${JSON.stringify(response)}`)
16
- }
17
-
18
- return response.data
19
- } catch (err) {
20
- dcgLogger(`getStsToken error: ${String(err)}`, 'error')
21
- throw new Error(`获取 OSS 临时凭证失败: ${String(err)}`)
22
- }
23
- }
24
- export const generateSignUrl = async (file_url: string, botToken: string) => {
25
- try {
26
- // 确保 userToken 已缓存(如果未缓存会自动获取并缓存)
27
- await getUserToken(botToken)
28
-
29
- const response = await post<{ loudPlatform: number; fileName: string }, { filePath?: string }>('/user/generateSignUrl', { loudPlatform: 0, fileName: file_url }, { botToken })
30
- if (response.code !== 0 || !response?.data || !response.data?.filePath) {
31
- throw new Error(`生成 OSS 链接失败: response=${JSON.stringify(response)}`)
32
- }
33
- return response.data.filePath
34
- } catch (error) {
35
- dcgLogger(`generateSignUrl error: ${String(error)}`, 'error')
36
- throw new Error(`生成 OSS 链接失败: ${String(error)}`)
37
- }
38
- }
39
-
40
- /**
41
- * 通过 botToken 查询 userToken
42
- * @param botToken 机器人 token
43
- * @returns userToken
44
- */
45
- export const queryUserTokenByBotToken = async (botToken: string): Promise<string> => {
46
- const p = getParamsDefaults()
47
- const response = await post<{ botToken: string; _userId: number }, { token: string }>('/organization/queryUserTokenByBotTokenV2', {
48
- botToken,
49
- _userId: p.userId ?? 0
50
- })
51
-
52
- if (!response || !response.data || !response.data.token) {
53
- dcgLogger('获取绑定的用户信息失败: token:' + botToken + '|' + JSON.stringify(response), 'error')
54
- return ''
55
- }
56
-
57
- return response.data.token
58
- }
59
-
60
- /**
61
- * 获取 userToken(优先从缓存获取,缓存未命中则调用 API)
62
- * @param botToken 机器人 token
63
- * @returns userToken
64
- */
65
- export const getUserToken = async (botToken: string): Promise<string> => {
66
- // 1. 尝试从缓存获取
67
- const cachedToken = getUserTokenCache(botToken)
68
- if (cachedToken) {
69
- return cachedToken
70
- }
71
-
72
- // 2. 缓存未命中,调用 API 获取
73
- dcgLogger(`[api] cache miss, fetching userToken for botToken=${botToken.slice(0, 10)}...`)
74
- const userToken = await queryUserTokenByBotToken(botToken)
75
-
76
- // 3. 缓存新获取的 token
77
- setUserTokenCache(botToken, userToken)
78
-
79
- return userToken
80
- }