mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 20:14:44 +00:00
fix(qqbot): preserve framework command source
This commit is contained in:
committed by
Peter Steinberger
parent
2a6239084f
commit
877eae9b58
@@ -49,6 +49,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Active Memory: support concrete `plugins.entries.active-memory.config.toolsAllow` recall tool names for custom memory plugins while keeping the built-in memory-core default on `memory_search`/`memory_get` and preserving `memory_recall` automatically for `plugins.slots.memory: "memory-lancedb"`.
|
||||
- Telegram: share the grammY API throttler across polling and ad hoc send clients for the same bot token, so visible draft previews and CLI sends use one quota gate. Thanks @anagnorisis2peripeteia.
|
||||
- Telegram/Feishu: honor configured per-agent and global `reasoningDefault` values when deciding whether channel reasoning previews should stream or stay hidden, addressing the preview-default part of #73182. Thanks @anagnorisis2peripeteia.
|
||||
- QQBot: mark recognized framework slash commands as text-command turns before reply dispatch so `/models`, `/status`, and `/new` responses stay visible in QQ Bot C2C conversations. Fixes #79310. Thanks @rollingshmily.
|
||||
- Docker: run the runtime image under `tini` so long-lived containers reap orphaned child processes and forward signals correctly. (#77885) Thanks @VintageAyu.
|
||||
- Logging/redaction: redact quoted HTTP client secret fields and auth/cookie headers in shared log and formatted error output. Related #71211 and #65623. (#75033) Thanks @liaoandi.
|
||||
- Gateway/SDK: document and stabilize the task ledger RPC surface for `tasks.list`, `tasks.get`, and `tasks.cancel`, including generated Swift model typing for optional task summaries. Thanks @BunsDev.
|
||||
|
||||
@@ -98,6 +98,7 @@ function makeInbound(overrides: Partial<InboundContext> = {}): InboundContext {
|
||||
|
||||
function makeRuntime(params: {
|
||||
onFinalize?: (ctx: Record<string, unknown>) => void;
|
||||
isControlCommandMessage?: (text?: string, cfg?: unknown) => boolean;
|
||||
onDeliver?: (
|
||||
deliver: (
|
||||
payload: { text?: string; audioAsVoice?: boolean },
|
||||
@@ -164,6 +165,9 @@ function makeRuntime(params: {
|
||||
text: {
|
||||
chunkMarkdownText: (text: string) => [text],
|
||||
},
|
||||
commands: {
|
||||
isControlCommandMessage: params.isControlCommandMessage ?? (() => false),
|
||||
},
|
||||
},
|
||||
tts: {
|
||||
textToSpeech: vi.fn(async () => ({
|
||||
@@ -228,4 +232,40 @@ describe("dispatchOutbound", () => {
|
||||
);
|
||||
expect(sendTextMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("marks recognized C2C framework slash commands as text commands", async () => {
|
||||
let finalized: Record<string, unknown> | undefined;
|
||||
const runtime = makeRuntime({
|
||||
isControlCommandMessage: (text) => text === "/models",
|
||||
onFinalize: (ctx) => (finalized = ctx),
|
||||
});
|
||||
|
||||
await dispatchOutbound(
|
||||
makeInbound({
|
||||
event: {
|
||||
type: "c2c",
|
||||
senderId: "user-openid",
|
||||
messageId: "msg-models",
|
||||
content: "/models",
|
||||
timestamp: "2026-04-25T00:00:00.000Z",
|
||||
},
|
||||
parsedContent: "/models",
|
||||
userContent: "/models",
|
||||
userMessage: "/models",
|
||||
agentBody: "/models",
|
||||
body: "/models",
|
||||
commandAuthorized: true,
|
||||
}),
|
||||
{ runtime, cfg: { commands: { text: true } }, account },
|
||||
);
|
||||
|
||||
expect(finalized).toMatchObject({
|
||||
CommandBody: "/models",
|
||||
CommandAuthorized: true,
|
||||
CommandSource: "text",
|
||||
Provider: "qqbot",
|
||||
Surface: "qqbot",
|
||||
ChatType: "direct",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ export async function dispatchOutbound(
|
||||
const sendErrorMessage = (errorText: string) => sendErrorToTarget(replyCtx, errorText);
|
||||
|
||||
// ---- Build ctxPayload ----
|
||||
const ctxPayload = buildCtxPayload(inbound, runtime);
|
||||
const ctxPayload = buildCtxPayload(inbound, runtime, cfg);
|
||||
|
||||
// ---- Deliver state ----
|
||||
let hasResponse = false;
|
||||
@@ -512,11 +512,25 @@ export async function dispatchOutbound(
|
||||
|
||||
// ============ ctxPayload builder ============
|
||||
|
||||
function resolveCommandSource(
|
||||
inbound: InboundContext,
|
||||
runtime: GatewayPluginRuntime,
|
||||
cfg: unknown,
|
||||
): "text" | undefined {
|
||||
const commandBody = inbound.event.content;
|
||||
if (!runtime.channel.commands?.isControlCommandMessage?.(commandBody, cfg)) {
|
||||
return undefined;
|
||||
}
|
||||
return "text";
|
||||
}
|
||||
|
||||
function buildCtxPayload(
|
||||
inbound: InboundContext,
|
||||
runtime: GatewayPluginRuntime,
|
||||
cfg: unknown,
|
||||
): FinalizedMsgContext {
|
||||
const { event } = inbound;
|
||||
const commandSource = resolveCommandSource(inbound, runtime, cfg);
|
||||
return runtime.channel.reply.finalizeInboundContext({
|
||||
Body: inbound.body,
|
||||
BodyForAgent: inbound.agentBody,
|
||||
@@ -546,6 +560,7 @@ function buildCtxPayload(
|
||||
QQVoiceAsrReferTexts: inbound.uniqueVoiceAsrReferTexts,
|
||||
QQVoiceInputStrategy: "prefer_audio_stt_then_asr_fallback",
|
||||
CommandAuthorized: inbound.commandAuthorized,
|
||||
...(commandSource ? { CommandSource: commandSource } : {}),
|
||||
...(inbound.voiceMediaTypes.length > 0
|
||||
? {
|
||||
MediaTypes: inbound.voiceMediaTypes,
|
||||
|
||||
@@ -42,6 +42,9 @@ export interface GatewayPluginRuntime {
|
||||
peer: { kind: "group" | "direct"; id: string };
|
||||
}) => { sessionKey: string; accountId: string; agentId?: string };
|
||||
};
|
||||
commands?: {
|
||||
isControlCommandMessage?: (text?: string, cfg?: unknown) => boolean;
|
||||
};
|
||||
reply: {
|
||||
dispatchReplyWithBufferedBlockDispatcher: (params: unknown) => Promise<unknown>;
|
||||
resolveEffectiveMessagesConfig: (
|
||||
|
||||
Reference in New Issue
Block a user