@dcrays/dcgchat 0.3.31 → 0.3.35
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.
- package/package.json +1 -1
- package/src/channel.ts +23 -19
- package/src/cron.ts +18 -26
- package/src/gateway/index.ts +5 -2
- package/src/monitor.ts +7 -9
- package/src/request/api.ts +1 -1
- package/src/session.ts +19 -0
- package/src/transport.ts +3 -7
- package/src/utils/global.ts +6 -1
- package/src/utils/params.ts +1 -1
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ChannelPlugin, OpenClawConfig } from 'openclaw/plugin-sdk'
|
|
|
2
2
|
import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk'
|
|
3
3
|
import type { ResolvedDcgchatAccount, DcgchatConfig } from './types.js'
|
|
4
4
|
import { ossUpload } from './request/oss.js'
|
|
5
|
-
import { addSentMediaKey, getCronMessageId, getOpenClawConfig, hasSentMediaKey } from './utils/global.js'
|
|
5
|
+
import { addSentMediaKey, getCronMessageId, getInfoBySessionKey, getOpenClawConfig, hasSentMediaKey } from './utils/global.js'
|
|
6
6
|
import { isWsOpen, mergeDefaultParams, mergeSessionParams, sendFinal, wsSendRaw } from './transport.js'
|
|
7
7
|
import { dcgLogger, setLogger } from './utils/log.js'
|
|
8
8
|
import { getOutboundMsgParams, getParamsMessage } from './utils/params.js'
|
|
@@ -147,7 +147,7 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
147
147
|
})
|
|
148
148
|
},
|
|
149
149
|
messaging: {
|
|
150
|
-
normalizeTarget: (raw) =>
|
|
150
|
+
normalizeTarget: (raw) => raw || undefined,
|
|
151
151
|
targetResolver: {
|
|
152
152
|
looksLikeId: (raw) => Boolean(raw?.trim()),
|
|
153
153
|
hint: 'effectiveSessionKey(与 SessionKey 一致;勿填配置里的 WS userId)'
|
|
@@ -162,36 +162,40 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
162
162
|
outbound: {
|
|
163
163
|
deliveryMode: 'direct',
|
|
164
164
|
resolveTarget: ({ to }) => {
|
|
165
|
-
|
|
166
|
-
if (!normalized) {
|
|
165
|
+
if (!to) {
|
|
167
166
|
return { ok: false, error: new Error('target is empty') }
|
|
168
167
|
}
|
|
169
|
-
return { ok: true, to:
|
|
168
|
+
return { ok: true, to: to }
|
|
170
169
|
},
|
|
171
170
|
textChunkLimit: 4000,
|
|
172
171
|
sendText: async (ctx) => {
|
|
173
|
-
const isCron = ctx.to.indexOf('dcg-cron:') >= 0
|
|
174
|
-
const to = normalizeSessionTarget(ctx.to)
|
|
175
172
|
dcgLogger(`channel sendText to ${ctx.to} `)
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
const messageId = !!cronMsgId ? cronMsgId : isCron ? `${Date.now()}` : outboundCtx?.messageId
|
|
173
|
+
let messageId = ''
|
|
174
|
+
const to = normalizeSessionTarget(ctx.to)
|
|
179
175
|
if (isWsOpen()) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const sessionId =
|
|
186
|
-
|
|
176
|
+
const isCron = ctx.to.indexOf('dcg-cron:') >= 0
|
|
177
|
+
const outboundCtx = getOutboundMsgParams(to)
|
|
178
|
+
const content: Record<string, unknown> = { response: ctx.text }
|
|
179
|
+
if (isCron) {
|
|
180
|
+
messageId = getCronMessageId(to) || `${Date.now()}`
|
|
181
|
+
const { sessionId, agentId } = getInfoBySessionKey(to)
|
|
182
|
+
content.is_finish = -1
|
|
183
|
+
content.message_tags = { source: 'cron' }
|
|
187
184
|
const merged = mergeDefaultParams({
|
|
188
185
|
agentId: agentId,
|
|
189
186
|
sessionId: `${sessionId}`,
|
|
190
187
|
messageId: messageId,
|
|
191
|
-
is_finish: -1,
|
|
192
188
|
real_mobook: !sessionId ? 1 : ''
|
|
193
189
|
})
|
|
194
|
-
wsSendRaw(merged,
|
|
190
|
+
wsSendRaw(merged, content)
|
|
191
|
+
} else {
|
|
192
|
+
if (outboundCtx?.sessionId) {
|
|
193
|
+
messageId = outboundCtx?.messageId || `${Date.now()}`
|
|
194
|
+
const newCtx = { ...outboundCtx, messageId }
|
|
195
|
+
wsSendRaw(newCtx, content)
|
|
196
|
+
} else {
|
|
197
|
+
dcgLogger(`channel sendText to ${ctx.to} -> sessionId not found`, 'error')
|
|
198
|
+
}
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
return {
|
package/src/cron.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { getCronMessageId, getWorkspaceDir, getWsConnection, removeCronMessageId
|
|
|
6
6
|
import { ossUpload } from './request/oss.js'
|
|
7
7
|
import { dcgLogger } from './utils/log.js'
|
|
8
8
|
import { sendMessageToGateway } from './gateway/socket.js'
|
|
9
|
-
import { getEffectiveMsgParams } from './utils/params.js'
|
|
9
|
+
import { getEffectiveMsgParams, getParamsDefaults } from './utils/params.js'
|
|
10
10
|
|
|
11
11
|
export function getCronJobsPath(): string {
|
|
12
12
|
const workspaceDir = getWorkspaceDir()
|
|
@@ -45,23 +45,27 @@ function msgParamsToCtx(p: IMsgParams): IMsgParams | null {
|
|
|
45
45
|
return p
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const CRON_UPLOAD_DEBOUNCE_MS =
|
|
48
|
+
const CRON_UPLOAD_DEBOUNCE_MS = 2400
|
|
49
49
|
|
|
50
|
-
/** 待合并的上传上下文(短时间内多次调用只保留最后一次) */
|
|
51
|
-
let pendingCronUploadCtx: IMsgParams | null = null
|
|
52
50
|
let cronUploadFlushTimer: ReturnType<typeof setTimeout> | null = null
|
|
53
51
|
|
|
54
|
-
async function runCronJobsUpload(
|
|
52
|
+
async function runCronJobsUpload(sessionKey: string): Promise<void> {
|
|
55
53
|
const jobPath = getCronJobsPath()
|
|
54
|
+
const botToken = getParamsDefaults().botToken
|
|
56
55
|
if (fs.existsSync(jobPath)) {
|
|
57
56
|
try {
|
|
58
|
-
const url = await ossUpload(jobPath,
|
|
59
|
-
dcgLogger(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
const url = await ossUpload(jobPath, botToken ?? '', 0)
|
|
58
|
+
dcgLogger(`定时任务更新成功: ${url}`)
|
|
59
|
+
const sessionInfo = sessionKey?.split(':') || []
|
|
60
|
+
const sessionId = sessionInfo.at(-1) ?? ''
|
|
61
|
+
const agentId = sessionInfo.at(-2) ?? ''
|
|
62
|
+
const params = {
|
|
63
|
+
event_type: 'cron',
|
|
64
|
+
operation_type: 'install',
|
|
65
|
+
session_id: sessionId,
|
|
66
|
+
agent_id: agentId
|
|
63
67
|
}
|
|
64
|
-
sendEventMessage(url,
|
|
68
|
+
sendEventMessage(url, params)
|
|
65
69
|
} catch (error) {
|
|
66
70
|
dcgLogger(`${jobPath} upload failed: ${error}`, 'error')
|
|
67
71
|
}
|
|
@@ -70,14 +74,6 @@ async function runCronJobsUpload(msgCtx: IMsgParams): Promise<void> {
|
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
function flushCronUploadQueue(): void {
|
|
74
|
-
cronUploadFlushTimer = null
|
|
75
|
-
const ctx = pendingCronUploadCtx
|
|
76
|
-
pendingCronUploadCtx = null
|
|
77
|
-
if (!ctx) return
|
|
78
|
-
void runCronJobsUpload(ctx)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
77
|
/**
|
|
82
78
|
* 将 jobs.json 同步到 OSS 并推送事件。30s 内多次调用合并为一次上传;定时触发后清空待处理项,避免重复执行。
|
|
83
79
|
* @param msgCtx 可选;省略时使用当前会话 getEffectiveMsgParams(sessionKey) 快照
|
|
@@ -85,17 +81,13 @@ function flushCronUploadQueue(): void {
|
|
|
85
81
|
export function sendDcgchatCron(jobId: string): void {
|
|
86
82
|
const jobPath = getCronJobsPath()
|
|
87
83
|
const { sessionKey } = readCronJob(jobPath, jobId) || {}
|
|
88
|
-
const ctx = msgParamsToCtx(getEffectiveMsgParams(sessionKey))
|
|
89
|
-
if (!ctx) {
|
|
90
|
-
dcgLogger('sendDcgchatCron: no message context (missing token / params)', 'error')
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
84
|
dcgLogger(`sessionKey: ${sessionKey}, jobId: ${jobId}`)
|
|
94
|
-
pendingCronUploadCtx = ctx
|
|
95
85
|
if (cronUploadFlushTimer !== null) {
|
|
96
86
|
clearTimeout(cronUploadFlushTimer)
|
|
97
87
|
}
|
|
98
|
-
cronUploadFlushTimer = setTimeout(
|
|
88
|
+
cronUploadFlushTimer = setTimeout(() => {
|
|
89
|
+
runCronJobsUpload(sessionKey)
|
|
90
|
+
}, CRON_UPLOAD_DEBOUNCE_MS)
|
|
99
91
|
}
|
|
100
92
|
|
|
101
93
|
/**
|
package/src/gateway/index.ts
CHANGED
|
@@ -363,10 +363,13 @@ export class GatewayConnection {
|
|
|
363
363
|
if (msg.event === 'cron') {
|
|
364
364
|
dcgLogger(`[Gateway] 收到事件: ${JSON.stringify(msg)}`)
|
|
365
365
|
if (msg.payload?.action === 'added') {
|
|
366
|
-
sendDcgchatCron()
|
|
366
|
+
sendDcgchatCron(msg.payload?.jobId)
|
|
367
367
|
}
|
|
368
368
|
if (msg.payload?.action === 'updated') {
|
|
369
|
-
sendDcgchatCron()
|
|
369
|
+
sendDcgchatCron(msg.payload?.jobId as string)
|
|
370
|
+
}
|
|
371
|
+
if (msg.payload?.action === 'removed') {
|
|
372
|
+
sendDcgchatCron(msg.payload?.jobId as string)
|
|
370
373
|
}
|
|
371
374
|
if (msg.payload?.action === 'finished') {
|
|
372
375
|
finishedDcgchatCron(msg.payload?.jobId as string)
|
package/src/monitor.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { dcgLogger } from './utils/log.js'
|
|
|
9
9
|
import { onDisabledCronJob, onEnabledCronJob, onRemoveCronJob, onRunCronJob } from './cron.js'
|
|
10
10
|
import { ignoreToolCommand } from './utils/constant.js'
|
|
11
11
|
import { isWsOpen } from './transport.js'
|
|
12
|
+
import { onRemoveSession } from './session.js'
|
|
12
13
|
|
|
13
14
|
export type MonitorDcgchatOpts = {
|
|
14
15
|
config?: ClawdbotConfig
|
|
@@ -142,15 +143,7 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
142
143
|
const { event_type, operation_type } = parsed.content ? parsed.content : ({} as Record<string, any>)
|
|
143
144
|
if (event_type === 'skill') {
|
|
144
145
|
const { skill_url, skill_code, skill_id, bot_token, websocket_trace_id } = parsed.content
|
|
145
|
-
const content = {
|
|
146
|
-
event_type,
|
|
147
|
-
operation_type,
|
|
148
|
-
skill_url,
|
|
149
|
-
skill_code,
|
|
150
|
-
skill_id,
|
|
151
|
-
bot_token,
|
|
152
|
-
websocket_trace_id
|
|
153
|
-
}
|
|
146
|
+
const content = { event_type, operation_type, skill_url, skill_code, skill_id, bot_token, websocket_trace_id }
|
|
154
147
|
if (operation_type === 'install' || operation_type === 'enable' || operation_type === 'update') {
|
|
155
148
|
installSkill({ path: skill_url, code: skill_code }, content)
|
|
156
149
|
} else if (operation_type === 'remove' || operation_type === 'disable') {
|
|
@@ -169,6 +162,11 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
169
162
|
} else if (operation_type === 'run') {
|
|
170
163
|
await onRunCronJob(job_id, message_id)
|
|
171
164
|
}
|
|
165
|
+
} else if (event_type === 'session') {
|
|
166
|
+
const { agent_id, session_id, agent_clone_code } = parsed.content
|
|
167
|
+
if (operation_type === 'remove') {
|
|
168
|
+
await onRemoveSession({ agent_id, session_id, agent_clone_code, account_id: accountId })
|
|
169
|
+
}
|
|
172
170
|
} else {
|
|
173
171
|
dcgLogger(`openclaw_bot_event unknown operation_type: ${operation_type}, ${data.toString()}`)
|
|
174
172
|
}
|
package/src/request/api.ts
CHANGED
|
@@ -40,7 +40,7 @@ export const queryUserTokenByBotToken = async (botToken: string): Promise<string
|
|
|
40
40
|
const response = await post<{ botToken: string }, { token: string }>('/organization/queryUserTokenByBotToken', { botToken })
|
|
41
41
|
|
|
42
42
|
if (!response || !response.data || !response.data.token) {
|
|
43
|
-
dcgLogger('获取绑定的用户信息失败: ' + JSON.stringify(response), 'error')
|
|
43
|
+
dcgLogger('获取绑定的用户信息失败: token:' + botToken + '|' + JSON.stringify(response), 'error')
|
|
44
44
|
return ''
|
|
45
45
|
}
|
|
46
46
|
|
package/src/session.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sendMessageToGateway } from './gateway/socket.js'
|
|
2
|
+
import { getSessionKey } from './utils/global.js'
|
|
3
|
+
import { dcgLogger } from './utils/log.js'
|
|
4
|
+
|
|
5
|
+
interface TSession {
|
|
6
|
+
agent_id: string
|
|
7
|
+
session_id: string
|
|
8
|
+
agent_clone_code?: string
|
|
9
|
+
account_id?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const onRemoveSession = async ({ agent_id, session_id, agent_clone_code, account_id }: TSession) => {
|
|
13
|
+
const sessionKey = getSessionKey({ agent_id, session_id }, account_id)
|
|
14
|
+
if (!session_id) {
|
|
15
|
+
dcgLogger('onRemoveSession: empty session_id', 'error')
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
sendMessageToGateway(JSON.stringify({ method: 'sessions.delete', params: { key: sessionKey, deleteTranscript: true } }))
|
|
19
|
+
}
|
package/src/transport.ts
CHANGED
|
@@ -178,8 +178,8 @@ export function sendError(errorMsg: string, ctx: IMsgParams): boolean {
|
|
|
178
178
|
return wsSend(ctx, { response: `[错误] ${errorMsg}`, state: 'final' })
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
export function sendEventMessage(url: string,
|
|
182
|
-
const ctx =
|
|
181
|
+
export function sendEventMessage(url: string, params: Record<string, string> = {}) {
|
|
182
|
+
const ctx = getParamsDefaults()
|
|
183
183
|
const ws = getWsConnection()
|
|
184
184
|
if (isWsOpen()) {
|
|
185
185
|
ws?.send(
|
|
@@ -187,16 +187,12 @@ export function sendEventMessage(url: string, sessionKey: string) {
|
|
|
187
187
|
messageType: 'openclaw_bot_event',
|
|
188
188
|
source: 'client',
|
|
189
189
|
content: {
|
|
190
|
-
event_type: 'cron',
|
|
191
|
-
operation_type: 'install',
|
|
192
190
|
bot_token: ctx.botToken,
|
|
193
191
|
domain_id: ctx.domainId,
|
|
194
192
|
app_id: ctx.appId,
|
|
195
193
|
oss_url: url,
|
|
196
194
|
bot_id: ctx.botId,
|
|
197
|
-
|
|
198
|
-
session_id: ctx.sessionId,
|
|
199
|
-
message_id: Date.now().toString()
|
|
195
|
+
...params
|
|
200
196
|
}
|
|
201
197
|
})
|
|
202
198
|
)
|
package/src/utils/global.ts
CHANGED
|
@@ -117,7 +117,7 @@ export function clearSentMediaKeys(messageId?: string) {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export const getSessionKey = (content: any, accountId
|
|
120
|
+
export const getSessionKey = (content: any, accountId?: string) => {
|
|
121
121
|
const { real_mobook, agent_id, conversation_id, session_id } = content
|
|
122
122
|
const core = getDcgchatRuntime()
|
|
123
123
|
|
|
@@ -143,3 +143,8 @@ export function getCronMessageId(sk: string): string {
|
|
|
143
143
|
export function removeCronMessageId(sk: string) {
|
|
144
144
|
cronMessageIdMap.delete(sk)
|
|
145
145
|
}
|
|
146
|
+
|
|
147
|
+
export function getInfoBySessionKey(sk: string): { sessionId: string; agentId: string } {
|
|
148
|
+
const sessionInfo = sk.split(':')
|
|
149
|
+
return { sessionId: sessionInfo.at(-1) ?? '', agentId: sessionInfo.at(-2) ?? '' }
|
|
150
|
+
}
|
package/src/utils/params.ts
CHANGED
|
@@ -9,7 +9,7 @@ const paramsMessageMap = new Map<string, IMsgParams>()
|
|
|
9
9
|
|
|
10
10
|
/** 从 OpenClaw 配置读取当前 channel 的基础参数(唯一来源,供 transport / resolve 等复用) */
|
|
11
11
|
export function getParamsDefaults(): IMsgParams {
|
|
12
|
-
const ch = (getOpenClawConfig()?.channels?.[
|
|
12
|
+
const ch = (getOpenClawConfig()?.channels?.["dcgchat"] as DcgchatConfig | undefined) ?? {}
|
|
13
13
|
return {
|
|
14
14
|
userId: Number(ch.userId ?? 0),
|
|
15
15
|
botToken: ch.botToken ?? '',
|