mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 09:52:25 +00:00
Fix/telegram writeback admin scope gate (#54561)
* fix(telegram): require operator.admin for legacy target writeback persistence * Address claude feedback * Update extensions/telegram/src/target-writeback.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Remove stray brace * Add updated docs * Add missing test file, address codex concerns * Fix test formatting error * Address comments, fix tests --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
@@ -127,7 +127,7 @@
|
||||
"exportName": "ChannelConfiguredBindingConversationRef",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 553,
|
||||
"line": 554,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -136,7 +136,7 @@
|
||||
"exportName": "ChannelConfiguredBindingMatch",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 558,
|
||||
"line": 559,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -145,7 +145,7 @@
|
||||
"exportName": "ChannelConfiguredBindingProvider",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 562,
|
||||
"line": 563,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -154,7 +154,7 @@
|
||||
"exportName": "ChannelGatewayContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 238,
|
||||
"line": 239,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1044,7 +1044,7 @@
|
||||
"exportName": "BaseProbeResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 558,
|
||||
"line": 559,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
@@ -1053,7 +1053,7 @@
|
||||
"exportName": "BaseTokenResolution",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 564,
|
||||
"line": 565,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
@@ -1518,7 +1518,7 @@
|
||||
"exportName": "BaseProbeResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 558,
|
||||
"line": 559,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
@@ -1527,7 +1527,7 @@
|
||||
"exportName": "BaseTokenResolution",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 564,
|
||||
"line": 565,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
@@ -1581,7 +1581,7 @@
|
||||
"exportName": "ChannelAllowlistAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 497,
|
||||
"line": 498,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1590,7 +1590,7 @@
|
||||
"exportName": "ChannelAuthAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 362,
|
||||
"line": 363,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1635,7 +1635,7 @@
|
||||
"exportName": "ChannelCommandAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 444,
|
||||
"line": 445,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1653,7 +1653,7 @@
|
||||
"exportName": "ChannelConfiguredBindingConversationRef",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 553,
|
||||
"line": 554,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1662,7 +1662,7 @@
|
||||
"exportName": "ChannelConfiguredBindingMatch",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 558,
|
||||
"line": 559,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1671,7 +1671,7 @@
|
||||
"exportName": "ChannelConfiguredBindingProvider",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 562,
|
||||
"line": 563,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1680,7 +1680,7 @@
|
||||
"exportName": "ChannelDirectoryAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 406,
|
||||
"line": 407,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1707,7 +1707,7 @@
|
||||
"exportName": "ChannelElevatedAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 437,
|
||||
"line": 438,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1716,7 +1716,7 @@
|
||||
"exportName": "ChannelExecApprovalAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 463,
|
||||
"line": 464,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1743,7 +1743,7 @@
|
||||
"exportName": "ChannelGatewayAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 346,
|
||||
"line": 347,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1752,7 +1752,7 @@
|
||||
"exportName": "ChannelGatewayContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 238,
|
||||
"line": 239,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1779,7 +1779,7 @@
|
||||
"exportName": "ChannelHeartbeatAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 372,
|
||||
"line": 373,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1806,7 +1806,7 @@
|
||||
"exportName": "ChannelLifecycleAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 449,
|
||||
"line": 450,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1815,7 +1815,7 @@
|
||||
"exportName": "ChannelLoginWithQrStartResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 317,
|
||||
"line": 318,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1824,7 +1824,7 @@
|
||||
"exportName": "ChannelLoginWithQrWaitResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 322,
|
||||
"line": 323,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1833,7 +1833,7 @@
|
||||
"exportName": "ChannelLogoutContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 327,
|
||||
"line": 328,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1842,7 +1842,7 @@
|
||||
"exportName": "ChannelLogoutResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 311,
|
||||
"line": 312,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1950,7 +1950,7 @@
|
||||
"exportName": "ChannelOutboundAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 154,
|
||||
"line": 155,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -1977,7 +1977,7 @@
|
||||
"exportName": "ChannelPairingAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 335,
|
||||
"line": 336,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -2013,7 +2013,7 @@
|
||||
"exportName": "ChannelResolveKind",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 417,
|
||||
"line": 418,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -2022,7 +2022,7 @@
|
||||
"exportName": "ChannelResolverAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 427,
|
||||
"line": 428,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -2031,7 +2031,7 @@
|
||||
"exportName": "ChannelResolveResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 419,
|
||||
"line": 420,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -2040,7 +2040,7 @@
|
||||
"exportName": "ChannelSecurityAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 575,
|
||||
"line": 576,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -2085,7 +2085,7 @@
|
||||
"exportName": "ChannelStatusAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 184,
|
||||
"line": 185,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
@@ -4884,7 +4884,7 @@
|
||||
"exportName": "ChannelGatewayContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 238,
|
||||
"line": 239,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
{"declaration":"export type ChannelAgentToolFactory = ChannelAgentToolFactory;","entrypoint":"index","exportName":"ChannelAgentToolFactory","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":23,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"index","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":230,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelConfigSchema = ChannelConfigSchema;","entrypoint":"index","exportName":"ChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":48,"sourcePath":"src/channels/plugins/types.plugin.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"index","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":553,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"index","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"index","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":562,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"index","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":554,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"index","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"index","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":563,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelId = ChannelId;","entrypoint":"index","exportName":"ChannelId","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"index","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":516,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"index","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
@@ -113,8 +113,8 @@
|
||||
{"declaration":"export const MarkdownConfigSchema: z.ZodOptional<z.ZodObject<{ tables: z.ZodOptional<z.ZodEnum<{ off: \"off\"; bullets: \"bullets\"; code: \"code\"; }>>; }, z.core.$strict>>;","entrypoint":"channel-config-schema","exportName":"MarkdownConfigSchema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","kind":"const","recordType":"export","sourceLine":371,"sourcePath":"src/config/zod-schema.core.ts"}
|
||||
{"declaration":"export const ToolPolicySchema: z.ZodOptional<z.ZodObject<{ allow: z.ZodOptional<z.ZodArray<z.ZodString>>; alsoAllow: z.ZodOptional<z.ZodArray<z.ZodString>>; deny: z.ZodOptional<z.ZodArray<z.ZodString>>; }, z.core.$strict>>;","entrypoint":"channel-config-schema","exportName":"ToolPolicySchema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","kind":"const","recordType":"export","sourceLine":253,"sourcePath":"src/config/zod-schema.agent-runtime.ts"}
|
||||
{"category":"channel","entrypoint":"channel-contract","importSpecifier":"openclaw/plugin-sdk/channel-contract","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/channel-contract.ts"}
|
||||
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-contract","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-contract","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":564,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-contract","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-contract","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":565,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"channel-contract","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAgentTool = ChannelAgentTool;","entrypoint":"channel-contract","exportName":"ChannelAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":18,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelGroupContext = ChannelGroupContext;","entrypoint":"channel-contract","exportName":"ChannelGroupContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":216,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
@@ -165,43 +165,43 @@
|
||||
{"declaration":"export function waitUntilAbort(signal?: AbortSignal | undefined, onAbort?: (() => void | Promise<void>) | undefined): Promise<void>;","entrypoint":"channel-runtime","exportName":"waitUntilAbort","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/plugin-sdk/channel-lifecycle.ts"}
|
||||
{"declaration":"export const CHANNEL_MESSAGE_ACTION_NAMES: readonly [\"send\", \"broadcast\", \"poll\", \"poll-vote\", \"react\", \"reactions\", \"read\", \"edit\", \"unsend\", \"reply\", \"sendWithEffect\", \"renameGroup\", \"setGroupIcon\", \"addParticipant\", \"removeParticipant\", \"leaveGroup\", \"sendAttachment\", \"delete\", \"pin\", \"unpin\", \"list-pins\", \"permissions\", \"thread-create\", \"thread-list\", \"thread-reply\", \"search\", \"sticker\", \"sticker-search\", \"member-info\", \"role-info\", \"emoji-list\", \"emoji-upload\", \"sticker-upload\", \"role-add\", \"role-remove\", \"channel-info\", \"channel-list\", \"channel-create\", \"channel-edit\", \"channel-delete\", \"channel-move\", \"category-create\", \"category-edit\", \"category-delete\", \"topic-create\", \"topic-edit\", \"voice-status\", \"event-list\", \"event-create\", \"timeout\", \"kick\", \"ban\", \"set-profile\", \"set-presence\", \"set-profile\", \"download-file\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_ACTION_NAMES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-action-names.ts"}
|
||||
{"declaration":"export const CHANNEL_MESSAGE_CAPABILITIES: readonly [\"interactive\", \"buttons\", \"cards\", \"components\", \"blocks\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_CAPABILITIES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-capabilities.ts"}
|
||||
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-runtime","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-runtime","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":564,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-runtime","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-runtime","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":565,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"channel-runtime","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAccountState = ChannelAccountState;","entrypoint":"channel-runtime","exportName":"ChannelAccountState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":108,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAgentPromptAdapter = ChannelAgentPromptAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAgentPromptAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":463,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAgentTool = ChannelAgentTool;","entrypoint":"channel-runtime","exportName":"ChannelAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":18,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAgentToolFactory = ChannelAgentToolFactory;","entrypoint":"channel-runtime","exportName":"ChannelAgentToolFactory","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":23,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAllowlistAdapter = ChannelAllowlistAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAllowlistAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":497,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelAuthAdapter = ChannelAuthAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAuthAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":362,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelAllowlistAdapter = ChannelAllowlistAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAllowlistAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":498,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelAuthAdapter = ChannelAuthAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAuthAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":363,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"channel-runtime","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":230,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelCapabilitiesDiagnostics = ChannelCapabilitiesDiagnostics;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDiagnostics","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelCapabilitiesDisplayLine = ChannelCapabilitiesDisplayLine;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDisplayLine","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":42,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelCapabilitiesDisplayTone = ChannelCapabilitiesDisplayTone;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDisplayTone","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":40,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelCommandAdapter = ChannelCommandAdapter;","entrypoint":"channel-runtime","exportName":"ChannelCommandAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":444,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelCommandAdapter = ChannelCommandAdapter;","entrypoint":"channel-runtime","exportName":"ChannelCommandAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":445,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfigAdapter = ChannelConfigAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelConfigAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":91,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":553,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":562,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":406,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":554,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":563,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":407,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelDirectoryEntry = ChannelDirectoryEntry;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryEntry","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":469,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelDirectoryEntryKind = ChannelDirectoryEntryKind;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryEntryKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":467,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelElevatedAdapter = ChannelElevatedAdapter;","entrypoint":"channel-runtime","exportName":"ChannelElevatedAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":437,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelExecApprovalAdapter = ChannelExecApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":463,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelElevatedAdapter = ChannelElevatedAdapter;","entrypoint":"channel-runtime","exportName":"ChannelElevatedAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":438,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelExecApprovalAdapter = ChannelExecApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":464,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelExecApprovalForwardTarget = ChannelExecApprovalForwardTarget;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalForwardTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelExecApprovalInitiatingSurfaceState = ChannelExecApprovalInitiatingSurfaceState;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalInitiatingSurfaceState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":27,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":346,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":347,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGroupAdapter = ChannelGroupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelGroupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGroupContext = ChannelGroupContext;","entrypoint":"channel-runtime","exportName":"ChannelGroupContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":216,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelHeartbeatAdapter = ChannelHeartbeatAdapter;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":372,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelHeartbeatAdapter = ChannelHeartbeatAdapter;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":373,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelHeartbeatDeps = ChannelHeartbeatDeps;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatDeps","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":116,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelId = ChannelId;","entrypoint":"channel-runtime","exportName":"ChannelId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelLifecycleAdapter = ChannelLifecycleAdapter;","entrypoint":"channel-runtime","exportName":"ChannelLifecycleAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":449,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLoginWithQrStartResult = ChannelLoginWithQrStartResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrStartResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":317,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLoginWithQrWaitResult = ChannelLoginWithQrWaitResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrWaitResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":322,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":327,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLogoutResult = ChannelLogoutResult;","entrypoint":"channel-runtime","exportName":"ChannelLogoutResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":311,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLifecycleAdapter = ChannelLifecycleAdapter;","entrypoint":"channel-runtime","exportName":"ChannelLifecycleAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":450,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLoginWithQrStartResult = ChannelLoginWithQrStartResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrStartResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":318,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLoginWithQrWaitResult = ChannelLoginWithQrWaitResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrWaitResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":323,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":328,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLogoutResult = ChannelLogoutResult;","entrypoint":"channel-runtime","exportName":"ChannelLogoutResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":312,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelLogSink = ChannelLogSink;","entrypoint":"channel-runtime","exportName":"ChannelLogSink","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":209,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelMentionAdapter = ChannelMentionAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMentionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":260,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":516,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
@@ -213,22 +213,22 @@
|
||||
{"declaration":"export type ChannelMessageToolSchemaContribution = ChannelMessageToolSchemaContribution;","entrypoint":"channel-runtime","exportName":"ChannelMessageToolSchemaContribution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":51,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelMeta = ChannelMeta;","entrypoint":"channel-runtime","exportName":"ChannelMeta","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelOutboundAdapter = ChannelOutboundAdapter;","entrypoint":"channel-runtime","exportName":"ChannelOutboundAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":154,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelOutboundAdapter = ChannelOutboundAdapter;","entrypoint":"channel-runtime","exportName":"ChannelOutboundAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":155,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelOutboundContext = ChannelOutboundContext;","entrypoint":"channel-runtime","exportName":"ChannelOutboundContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":128,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelOutboundTargetMode = ChannelOutboundTargetMode;","entrypoint":"channel-runtime","exportName":"ChannelOutboundTargetMode","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":15,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":335,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":336,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":55,"sourcePath":"src/channels/plugins/types.plugin.ts"}
|
||||
{"declaration":"export type ChannelPollContext = ChannelPollContext;","entrypoint":"channel-runtime","exportName":"ChannelPollContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":547,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelPollResult = ChannelPollResult;","entrypoint":"channel-runtime","exportName":"ChannelPollResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":538,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelResolveKind = ChannelResolveKind;","entrypoint":"channel-runtime","exportName":"ChannelResolveKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":417,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelResolverAdapter = ChannelResolverAdapter;","entrypoint":"channel-runtime","exportName":"ChannelResolverAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":427,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelResolveResult = ChannelResolveResult;","entrypoint":"channel-runtime","exportName":"ChannelResolveResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":419,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":575,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelResolveKind = ChannelResolveKind;","entrypoint":"channel-runtime","exportName":"ChannelResolveKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":418,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelResolverAdapter = ChannelResolverAdapter;","entrypoint":"channel-runtime","exportName":"ChannelResolverAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":428,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelResolveResult = ChannelResolveResult;","entrypoint":"channel-runtime","exportName":"ChannelResolveResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":420,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":576,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelSecurityContext = ChannelSecurityContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":254,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelSecurityDmPolicy = ChannelSecurityDmPolicy;","entrypoint":"channel-runtime","exportName":"ChannelSecurityDmPolicy","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":245,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelSetupInput = ChannelSetupInput;","entrypoint":"channel-runtime","exportName":"ChannelSetupInput","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":63,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":184,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":185,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"channel-runtime","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelStreamingAdapter = ChannelStreamingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelStreamingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":279,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelStructuredComponents = ChannelStructuredComponents;","entrypoint":"channel-runtime","exportName":"ChannelStructuredComponents","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":288,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
@@ -537,7 +537,7 @@
|
||||
{"declaration":"export function removeAckReactionAfterReply(params: { removeAfterReply: boolean; ackReactionPromise: Promise<boolean> | null; ackReactionValue: string | null; remove: () => Promise<void>; onError?: ((err: unknown) => void) | undefined; }): void;","entrypoint":"testing","exportName":"removeAckReactionAfterReply","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":81,"sourcePath":"src/channels/ack-reactions.ts"}
|
||||
{"declaration":"export function shouldAckReaction(params: AckReactionGateParams): boolean;","entrypoint":"testing","exportName":"shouldAckReaction","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/channels/ack-reactions.ts"}
|
||||
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"testing","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"}
|
||||
{"declaration":"export type MockFn = MockFn<T>;","entrypoint":"testing","exportName":"MockFn","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":5,"sourcePath":"src/test-utils/vitest-mock-fn.ts"}
|
||||
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"testing","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
|
||||
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"testing","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
|
||||
|
||||
@@ -88,6 +88,7 @@ function buildTelegramSendOptions(params: {
|
||||
threadId?: string | number | null;
|
||||
silent?: boolean | null;
|
||||
forceDocument?: boolean | null;
|
||||
gatewayClientScopes?: readonly string[] | null;
|
||||
}): TelegramSendOptions {
|
||||
return {
|
||||
verbose: false,
|
||||
@@ -99,6 +100,9 @@ function buildTelegramSendOptions(params: {
|
||||
accountId: params.accountId ?? undefined,
|
||||
silent: params.silent ?? undefined,
|
||||
forceDocument: params.forceDocument ?? undefined,
|
||||
...(Array.isArray(params.gatewayClientScopes)
|
||||
? { gatewayClientScopes: [...params.gatewayClientScopes] }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,6 +117,7 @@ async function sendTelegramOutbound(params: {
|
||||
replyToId?: string | null;
|
||||
threadId?: string | number | null;
|
||||
silent?: boolean | null;
|
||||
gatewayClientScopes?: readonly string[] | null;
|
||||
}) {
|
||||
const send =
|
||||
resolveOutboundSendDep<TelegramSendFn>(params.deps, "telegram") ??
|
||||
@@ -128,6 +133,7 @@ async function sendTelegramOutbound(params: {
|
||||
replyToId: params.replyToId,
|
||||
threadId: params.threadId,
|
||||
silent: params.silent,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -710,6 +716,7 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
threadId,
|
||||
silent,
|
||||
forceDocument,
|
||||
gatewayClientScopes,
|
||||
}) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<TelegramSendFn>(deps, "telegram") ??
|
||||
@@ -726,6 +733,7 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
threadId,
|
||||
silent,
|
||||
forceDocument,
|
||||
gatewayClientScopes,
|
||||
}),
|
||||
});
|
||||
return attachChannelToResult("telegram", result);
|
||||
@@ -733,7 +741,17 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
},
|
||||
attachedResults: {
|
||||
channel: "telegram",
|
||||
sendText: async ({ cfg, to, text, accountId, deps, replyToId, threadId, silent }) =>
|
||||
sendText: async ({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
accountId,
|
||||
deps,
|
||||
replyToId,
|
||||
threadId,
|
||||
silent,
|
||||
gatewayClientScopes,
|
||||
}) =>
|
||||
await sendTelegramOutbound({
|
||||
cfg,
|
||||
to,
|
||||
@@ -743,6 +761,7 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
replyToId,
|
||||
threadId,
|
||||
silent,
|
||||
gatewayClientScopes,
|
||||
}),
|
||||
sendMedia: async ({
|
||||
cfg,
|
||||
@@ -755,6 +774,7 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
replyToId,
|
||||
threadId,
|
||||
silent,
|
||||
gatewayClientScopes,
|
||||
}) =>
|
||||
await sendTelegramOutbound({
|
||||
cfg,
|
||||
@@ -767,14 +787,25 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
replyToId,
|
||||
threadId,
|
||||
silent,
|
||||
gatewayClientScopes,
|
||||
}),
|
||||
sendPoll: async ({ cfg, to, poll, accountId, threadId, silent, isAnonymous }) =>
|
||||
sendPoll: async ({
|
||||
cfg,
|
||||
to,
|
||||
poll,
|
||||
accountId,
|
||||
threadId,
|
||||
silent,
|
||||
isAnonymous,
|
||||
gatewayClientScopes,
|
||||
}) =>
|
||||
await getTelegramRuntime().channel.telegram.sendPollTelegram(to, poll, {
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
messageThreadId: parseTelegramThreadId(threadId),
|
||||
silent: silent ?? undefined,
|
||||
isAnonymous: isAnonymous ?? undefined,
|
||||
gatewayClientScopes,
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -30,6 +30,7 @@ function resolveTelegramSendContext(params: {
|
||||
accountId?: string | null;
|
||||
replyToId?: string | null;
|
||||
threadId?: string | number | null;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
}): {
|
||||
send: TelegramSendFn;
|
||||
baseOpts: {
|
||||
@@ -39,6 +40,7 @@ function resolveTelegramSendContext(params: {
|
||||
messageThreadId?: number;
|
||||
replyToMessageId?: number;
|
||||
accountId?: string;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
} {
|
||||
const send =
|
||||
@@ -52,6 +54,7 @@ function resolveTelegramSendContext(params: {
|
||||
messageThreadId: parseTelegramThreadId(params.threadId),
|
||||
replyToMessageId: parseTelegramReplyToMessageId(params.replyToId),
|
||||
accountId: params.accountId ?? undefined,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -111,13 +114,23 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||
typeof fallbackLimit === "number" ? Math.min(fallbackLimit, 4096) : 4096,
|
||||
...createAttachedChannelResultAdapter({
|
||||
channel: "telegram",
|
||||
sendText: async ({ cfg, to, text, accountId, deps, replyToId, threadId }) => {
|
||||
sendText: async ({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
accountId,
|
||||
deps,
|
||||
replyToId,
|
||||
threadId,
|
||||
gatewayClientScopes,
|
||||
}) => {
|
||||
const { send, baseOpts } = resolveTelegramSendContext({
|
||||
cfg,
|
||||
deps,
|
||||
accountId,
|
||||
replyToId,
|
||||
threadId,
|
||||
gatewayClientScopes,
|
||||
});
|
||||
return await send(to, text, {
|
||||
...baseOpts,
|
||||
@@ -134,6 +147,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||
replyToId,
|
||||
threadId,
|
||||
forceDocument,
|
||||
gatewayClientScopes,
|
||||
}) => {
|
||||
const { send, baseOpts } = resolveTelegramSendContext({
|
||||
cfg,
|
||||
@@ -141,6 +155,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||
accountId,
|
||||
replyToId,
|
||||
threadId,
|
||||
gatewayClientScopes,
|
||||
});
|
||||
return await send(to, text, {
|
||||
...baseOpts,
|
||||
@@ -160,6 +175,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||
replyToId,
|
||||
threadId,
|
||||
forceDocument,
|
||||
gatewayClientScopes,
|
||||
}) => {
|
||||
const { send, baseOpts } = resolveTelegramSendContext({
|
||||
cfg,
|
||||
@@ -167,6 +183,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||
accountId,
|
||||
replyToId,
|
||||
threadId,
|
||||
gatewayClientScopes,
|
||||
});
|
||||
const result = await sendTelegramPayloadMessages({
|
||||
send,
|
||||
|
||||
@@ -596,6 +596,7 @@ describe("sendMessageTelegram", () => {
|
||||
await sendMessageTelegram("https://t.me/mychannel", "hi", {
|
||||
token: "tok",
|
||||
api,
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
});
|
||||
|
||||
expect(getChat).toHaveBeenCalledWith("@mychannel");
|
||||
@@ -606,6 +607,7 @@ describe("sendMessageTelegram", () => {
|
||||
expect.objectContaining({
|
||||
rawTarget: "https://t.me/mychannel",
|
||||
resolvedChatId: "-100123",
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -2117,6 +2119,32 @@ describe("editMessageTelegram", () => {
|
||||
});
|
||||
|
||||
describe("sendPollTelegram", () => {
|
||||
it("propagates gateway client scopes when resolving legacy poll targets", async () => {
|
||||
const api = {
|
||||
getChat: vi.fn(async () => ({ id: -100321 })),
|
||||
sendPoll: vi.fn(async () => ({ message_id: 123, chat: { id: 555 }, poll: { id: "p1" } })),
|
||||
};
|
||||
|
||||
await sendPollTelegram(
|
||||
"https://t.me/mychannel",
|
||||
{ question: " Q ", options: [" A ", "B "] },
|
||||
{
|
||||
token: "t",
|
||||
api: api as unknown as Bot["api"],
|
||||
gatewayClientScopes: ["operator.admin"],
|
||||
},
|
||||
);
|
||||
|
||||
expect(api.getChat).toHaveBeenCalledWith("@mychannel");
|
||||
expect(maybePersistResolvedTelegramTarget).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
rawTarget: "https://t.me/mychannel",
|
||||
resolvedChatId: "-100321",
|
||||
gatewayClientScopes: ["operator.admin"],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("maps durationSeconds to open_period", async () => {
|
||||
const api = {
|
||||
sendPoll: vi.fn(async () => ({ message_id: 123, chat: { id: 555 }, poll: { id: "p1" } })),
|
||||
|
||||
@@ -65,6 +65,7 @@ type TelegramSendOpts = {
|
||||
verbose?: boolean;
|
||||
mediaUrl?: string;
|
||||
mediaLocalRoots?: readonly string[];
|
||||
gatewayClientScopes?: readonly string[];
|
||||
maxBytes?: number;
|
||||
api?: TelegramApiOverride;
|
||||
retry?: RetryConfig;
|
||||
@@ -315,6 +316,7 @@ async function resolveAndPersistChatId(params: {
|
||||
lookupTarget: string;
|
||||
persistTarget: string;
|
||||
verbose?: boolean;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
}): Promise<string> {
|
||||
const chatId = await resolveChatId(params.lookupTarget, {
|
||||
api: params.api,
|
||||
@@ -325,6 +327,7 @@ async function resolveAndPersistChatId(params: {
|
||||
rawTarget: params.persistTarget,
|
||||
resolvedChatId: chatId,
|
||||
verbose: params.verbose,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
});
|
||||
return chatId;
|
||||
}
|
||||
@@ -632,6 +635,7 @@ export async function sendMessageTelegram(
|
||||
lookupTarget: target.chatId,
|
||||
persistTarget: to,
|
||||
verbose: opts.verbose,
|
||||
gatewayClientScopes: opts.gatewayClientScopes,
|
||||
});
|
||||
const mediaUrl = opts.mediaUrl?.trim();
|
||||
const mediaMaxBytes =
|
||||
@@ -1555,6 +1559,7 @@ type TelegramPollOpts = {
|
||||
verbose?: boolean;
|
||||
api?: TelegramApiOverride;
|
||||
retry?: RetryConfig;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
/** Message ID to reply to (for threading) */
|
||||
replyToMessageId?: number;
|
||||
/** Forum topic thread ID (for forum supergroups) */
|
||||
@@ -1584,6 +1589,7 @@ export async function sendPollTelegram(
|
||||
lookupTarget: target.chatId,
|
||||
persistTarget: to,
|
||||
verbose: opts.verbose,
|
||||
gatewayClientScopes: opts.gatewayClientScopes,
|
||||
});
|
||||
|
||||
// Normalize the poll input (validates question, options, maxSelections)
|
||||
|
||||
216
extensions/telegram/src/target-writeback.test.ts
Normal file
216
extensions/telegram/src/target-writeback.test.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
|
||||
const readConfigFileSnapshotForWrite = vi.fn();
|
||||
const writeConfigFile = vi.fn();
|
||||
const loadCronStore = vi.fn();
|
||||
const resolveCronStorePath = vi.fn();
|
||||
const saveCronStore = vi.fn();
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
readConfigFileSnapshotForWrite,
|
||||
writeConfigFile,
|
||||
loadCronStore,
|
||||
resolveCronStorePath,
|
||||
saveCronStore,
|
||||
};
|
||||
});
|
||||
|
||||
describe("maybePersistResolvedTelegramTarget", () => {
|
||||
let maybePersistResolvedTelegramTarget: typeof import("./target-writeback.js").maybePersistResolvedTelegramTarget;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ maybePersistResolvedTelegramTarget } = await import("./target-writeback.js"));
|
||||
readConfigFileSnapshotForWrite.mockReset();
|
||||
writeConfigFile.mockReset();
|
||||
loadCronStore.mockReset();
|
||||
resolveCronStorePath.mockReset();
|
||||
saveCronStore.mockReset();
|
||||
resolveCronStorePath.mockReturnValue("/tmp/cron/jobs.json");
|
||||
});
|
||||
|
||||
it("skips writeback when target is already numeric", async () => {
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: {} as OpenClawConfig,
|
||||
rawTarget: "-100123",
|
||||
resolvedChatId: "-100123",
|
||||
});
|
||||
|
||||
expect(readConfigFileSnapshotForWrite).not.toHaveBeenCalled();
|
||||
expect(loadCronStore).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips config and cron writeback for gateway callers missing operator.admin", async () => {
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: {
|
||||
cron: { store: "/tmp/cron/jobs.json" },
|
||||
} as OpenClawConfig,
|
||||
rawTarget: "t.me/mychannel",
|
||||
resolvedChatId: "-100123",
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
});
|
||||
|
||||
expect(readConfigFileSnapshotForWrite).not.toHaveBeenCalled();
|
||||
expect(writeConfigFile).not.toHaveBeenCalled();
|
||||
expect(loadCronStore).not.toHaveBeenCalled();
|
||||
expect(saveCronStore).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips config and cron writeback for gateway callers with an empty scope set", async () => {
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: {
|
||||
cron: { store: "/tmp/cron/jobs.json" },
|
||||
} as OpenClawConfig,
|
||||
rawTarget: "t.me/mychannel",
|
||||
resolvedChatId: "-100123",
|
||||
gatewayClientScopes: [],
|
||||
});
|
||||
|
||||
expect(readConfigFileSnapshotForWrite).not.toHaveBeenCalled();
|
||||
expect(writeConfigFile).not.toHaveBeenCalled();
|
||||
expect(loadCronStore).not.toHaveBeenCalled();
|
||||
expect(saveCronStore).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("writes back matching config and cron targets for gateway callers with operator.admin", async () => {
|
||||
readConfigFileSnapshotForWrite.mockResolvedValue({
|
||||
snapshot: {
|
||||
config: {
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "t.me/mychannel",
|
||||
accounts: {
|
||||
alerts: {
|
||||
defaultTo: "@mychannel",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
writeOptions: { expectedConfigPath: "/tmp/openclaw.json" },
|
||||
});
|
||||
loadCronStore.mockResolvedValue({
|
||||
version: 1,
|
||||
jobs: [
|
||||
{ id: "a", delivery: { channel: "telegram", to: "https://t.me/mychannel" } },
|
||||
{ id: "b", delivery: { channel: "slack", to: "C123" } },
|
||||
],
|
||||
});
|
||||
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: {
|
||||
cron: { store: "/tmp/cron/jobs.json" },
|
||||
} as OpenClawConfig,
|
||||
rawTarget: "t.me/mychannel",
|
||||
resolvedChatId: "-100123",
|
||||
gatewayClientScopes: ["operator.admin"],
|
||||
});
|
||||
|
||||
expect(writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "-100123",
|
||||
accounts: {
|
||||
alerts: {
|
||||
defaultTo: "-100123",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expect.objectContaining({ expectedConfigPath: "/tmp/openclaw.json" }),
|
||||
);
|
||||
expect(saveCronStore).toHaveBeenCalledTimes(1);
|
||||
expect(saveCronStore).toHaveBeenCalledWith(
|
||||
"/tmp/cron/jobs.json",
|
||||
expect.objectContaining({
|
||||
jobs: [
|
||||
{ id: "a", delivery: { channel: "telegram", to: "-100123" } },
|
||||
{ id: "b", delivery: { channel: "slack", to: "C123" } },
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves topic suffix style in writeback target", async () => {
|
||||
readConfigFileSnapshotForWrite.mockResolvedValue({
|
||||
snapshot: {
|
||||
config: {
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "t.me/mychannel:topic:9",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
writeOptions: {},
|
||||
});
|
||||
loadCronStore.mockResolvedValue({ version: 1, jobs: [] });
|
||||
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: {} as OpenClawConfig,
|
||||
rawTarget: "t.me/mychannel:topic:9",
|
||||
resolvedChatId: "-100123",
|
||||
});
|
||||
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "-100123:topic:9",
|
||||
},
|
||||
},
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("matches username targets case-insensitively", async () => {
|
||||
readConfigFileSnapshotForWrite.mockResolvedValue({
|
||||
snapshot: {
|
||||
config: {
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "https://t.me/mychannel",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
writeOptions: {},
|
||||
});
|
||||
loadCronStore.mockResolvedValue({
|
||||
version: 1,
|
||||
jobs: [{ id: "a", delivery: { channel: "telegram", to: "https://t.me/mychannel" } }],
|
||||
});
|
||||
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: {} as OpenClawConfig,
|
||||
rawTarget: "@MyChannel",
|
||||
resolvedChatId: "-100123",
|
||||
});
|
||||
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "-100123",
|
||||
},
|
||||
},
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(saveCronStore).toHaveBeenCalledWith(
|
||||
"/tmp/cron/jobs.json",
|
||||
expect.objectContaining({
|
||||
jobs: [{ id: "a", delivery: { channel: "telegram", to: "-100123" } }],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from "./targets.js";
|
||||
|
||||
const writebackLogger = createSubsystemLogger("telegram/target-writeback");
|
||||
const TELEGRAM_ADMIN_SCOPE = "operator.admin";
|
||||
|
||||
function asObjectRecord(value: unknown): Record<string, unknown> | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
@@ -141,6 +142,7 @@ export async function maybePersistResolvedTelegramTarget(params: {
|
||||
rawTarget: string;
|
||||
resolvedChatId: string;
|
||||
verbose?: boolean;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
}): Promise<void> {
|
||||
const raw = params.rawTarget.trim();
|
||||
if (!raw) {
|
||||
@@ -154,6 +156,15 @@ export async function maybePersistResolvedTelegramTarget(params: {
|
||||
return;
|
||||
}
|
||||
const { matchKey, resolvedTarget } = rewrite;
|
||||
if (
|
||||
Array.isArray(params.gatewayClientScopes) &&
|
||||
!params.gatewayClientScopes.includes(TELEGRAM_ADMIN_SCOPE)
|
||||
) {
|
||||
writebackLogger.warn(
|
||||
`skipping Telegram target writeback for ${raw} because gateway caller is missing ${TELEGRAM_ADMIN_SCOPE}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { snapshot, writeOptions } = await readConfigFileSnapshotForWrite();
|
||||
|
||||
@@ -141,6 +141,7 @@ export type ChannelOutboundContext = {
|
||||
identity?: OutboundIdentity;
|
||||
deps?: OutboundSendDeps;
|
||||
silent?: boolean;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
|
||||
export type ChannelOutboundPayloadContext = ChannelOutboundContext & {
|
||||
|
||||
@@ -552,6 +552,7 @@ export type ChannelPollContext = {
|
||||
threadId?: string | null;
|
||||
silent?: boolean;
|
||||
isAnonymous?: boolean;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
|
||||
/** Minimal base for all channel probe results. Channel-specific probes extend this. */
|
||||
|
||||
@@ -101,26 +101,40 @@ const makeContext = (): GatewayRequestContext =>
|
||||
}) as unknown as GatewayRequestContext;
|
||||
|
||||
async function runSend(params: Record<string, unknown>) {
|
||||
return await runSendWithClient(params);
|
||||
}
|
||||
|
||||
async function runSendWithClient(
|
||||
params: Record<string, unknown>,
|
||||
client?: { connect?: { scopes?: string[] } } | null,
|
||||
) {
|
||||
const respond = vi.fn();
|
||||
await sendHandlers.send({
|
||||
params: params as never,
|
||||
respond,
|
||||
context: makeContext(),
|
||||
req: { type: "req", id: "1", method: "send" },
|
||||
client: null,
|
||||
client: (client ?? null) as never,
|
||||
isWebchatConnect: () => false,
|
||||
});
|
||||
return { respond };
|
||||
}
|
||||
|
||||
async function runPoll(params: Record<string, unknown>) {
|
||||
return await runPollWithClient(params);
|
||||
}
|
||||
|
||||
async function runPollWithClient(
|
||||
params: Record<string, unknown>,
|
||||
client?: { connect?: { scopes?: string[] } } | null,
|
||||
) {
|
||||
const respond = vi.fn();
|
||||
await sendHandlers.poll({
|
||||
params: params as never,
|
||||
respond,
|
||||
context: makeContext(),
|
||||
req: { type: "req", id: "1", method: "poll" },
|
||||
client: null,
|
||||
client: (client ?? null) as never,
|
||||
isWebchatConnect: () => false,
|
||||
});
|
||||
return { respond };
|
||||
@@ -185,6 +199,48 @@ describe("gateway send mirroring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards gateway client scopes into outbound delivery", async () => {
|
||||
mockDeliverySuccess("m-telegram-scope");
|
||||
|
||||
await runSendWithClient(
|
||||
{
|
||||
to: "https://t.me/mychannel",
|
||||
message: "hi",
|
||||
channel: "telegram",
|
||||
idempotencyKey: "idem-telegram-scope",
|
||||
},
|
||||
{ connect: { scopes: ["operator.write"] } },
|
||||
);
|
||||
|
||||
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards an empty gateway scope array into outbound delivery", async () => {
|
||||
mockDeliverySuccess("m-telegram-empty-scope");
|
||||
|
||||
await runSendWithClient(
|
||||
{
|
||||
to: "https://t.me/mychannel",
|
||||
message: "hi",
|
||||
channel: "telegram",
|
||||
idempotencyKey: "idem-telegram-empty-scope",
|
||||
},
|
||||
{ connect: { scopes: [] } },
|
||||
);
|
||||
|
||||
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
gatewayClientScopes: [],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects empty sends when neither text nor media is present", async () => {
|
||||
const { respond } = await runSend({
|
||||
to: "channel:C1",
|
||||
@@ -268,6 +324,48 @@ describe("gateway send mirroring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards gateway client scopes into outbound poll delivery", async () => {
|
||||
await runPollWithClient(
|
||||
{
|
||||
to: "https://t.me/mychannel",
|
||||
question: "Q?",
|
||||
options: ["A", "B"],
|
||||
channel: "telegram",
|
||||
idempotencyKey: "idem-poll-scope",
|
||||
},
|
||||
{ connect: { scopes: ["operator.admin"] } },
|
||||
);
|
||||
|
||||
expect(mocks.sendPoll).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: expect.any(Object),
|
||||
to: "resolved",
|
||||
gatewayClientScopes: ["operator.admin"],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards an empty gateway scope array into outbound poll delivery", async () => {
|
||||
await runPollWithClient(
|
||||
{
|
||||
to: "https://t.me/mychannel",
|
||||
question: "Q?",
|
||||
options: ["A", "B"],
|
||||
channel: "telegram",
|
||||
idempotencyKey: "idem-poll-empty-scope",
|
||||
},
|
||||
{ connect: { scopes: [] } },
|
||||
);
|
||||
|
||||
expect(mocks.sendPoll).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: expect.any(Object),
|
||||
to: "resolved",
|
||||
gatewayClientScopes: [],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("auto-picks the single configured channel for poll", async () => {
|
||||
const { respond } = await runPoll({
|
||||
to: "x",
|
||||
|
||||
@@ -89,7 +89,7 @@ async function resolveRequestedChannel(params: {
|
||||
}
|
||||
|
||||
export const sendHandlers: GatewayRequestHandlers = {
|
||||
send: async ({ params, respond, context }) => {
|
||||
send: async ({ params, respond, context, client }) => {
|
||||
const p = params;
|
||||
if (!validateSendParams(p)) {
|
||||
respond(
|
||||
@@ -263,6 +263,7 @@ export const sendHandlers: GatewayRequestHandlers = {
|
||||
gifPlayback: request.gifPlayback,
|
||||
threadId: threadId ?? null,
|
||||
deps: outboundDeps,
|
||||
gatewayClientScopes: client?.connect?.scopes ?? [],
|
||||
mirror: providedSessionKey
|
||||
? {
|
||||
sessionKey: providedSessionKey,
|
||||
@@ -332,7 +333,7 @@ export const sendHandlers: GatewayRequestHandlers = {
|
||||
inflightMap.delete(dedupeKey);
|
||||
}
|
||||
},
|
||||
poll: async ({ params, respond, context }) => {
|
||||
poll: async ({ params, respond, context, client }) => {
|
||||
const p = params;
|
||||
if (!validatePollParams(p)) {
|
||||
respond(
|
||||
@@ -444,6 +445,7 @@ export const sendHandlers: GatewayRequestHandlers = {
|
||||
threadId,
|
||||
silent: request.silent,
|
||||
isAnonymous: request.isAnonymous,
|
||||
gatewayClientScopes: client?.connect?.scopes ?? [],
|
||||
});
|
||||
const payload: Record<string, unknown> = {
|
||||
runId: idem,
|
||||
|
||||
296
src/gateway/server.send-telegram-target-writeback-scope.test.ts
Normal file
296
src/gateway/server.send-telegram-target-writeback-scope.test.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
sendMessageTelegram,
|
||||
sendPollTelegram,
|
||||
type TelegramApiOverride,
|
||||
} from "../../extensions/telegram/src/send.js";
|
||||
import {
|
||||
clearConfigCache,
|
||||
loadConfig,
|
||||
writeConfigFile,
|
||||
type OpenClawConfig,
|
||||
} from "../config/config.js";
|
||||
import { loadCronStore, saveCronStore } from "../cron/store.js";
|
||||
import type { CronStoreFile } from "../cron/types.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import {
|
||||
getActivePluginRegistry,
|
||||
releasePinnedPluginChannelRegistry,
|
||||
setActivePluginRegistry,
|
||||
} from "../plugins/runtime.js";
|
||||
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { connectOk, installGatewayTestHooks, rpcReq } from "./test-helpers.js";
|
||||
import { withServer } from "./test-with-server.js";
|
||||
|
||||
installGatewayTestHooks({ scope: "suite" });
|
||||
|
||||
type TelegramGetChat = NonNullable<TelegramApiOverride["getChat"]>;
|
||||
type TelegramSendMessage = NonNullable<TelegramApiOverride["sendMessage"]>;
|
||||
type TelegramSendPoll = NonNullable<TelegramApiOverride["sendPoll"]>;
|
||||
|
||||
function createCronStore(): CronStoreFile {
|
||||
const now = Date.now();
|
||||
return {
|
||||
version: 1,
|
||||
jobs: [
|
||||
{
|
||||
id: "telegram-writeback-job",
|
||||
name: "Telegram writeback job",
|
||||
enabled: true,
|
||||
createdAtMs: now,
|
||||
updatedAtMs: now,
|
||||
schedule: { kind: "every", everyMs: 60_000 },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "tick" },
|
||||
state: {},
|
||||
delivery: {
|
||||
mode: "announce",
|
||||
channel: "telegram",
|
||||
to: "@mychannel",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
async function withTelegramGatewayWritebackFixture(
|
||||
run: (params: {
|
||||
cronStorePath: string;
|
||||
getChatMock: ReturnType<typeof vi.fn>;
|
||||
sendMessageMock: ReturnType<typeof vi.fn>;
|
||||
sendPollMock: ReturnType<typeof vi.fn>;
|
||||
installTelegramTestPlugin: () => void;
|
||||
}) => Promise<void>,
|
||||
): Promise<void> {
|
||||
const previousRegistry = getActivePluginRegistry() ?? createEmptyPluginRegistry();
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-telegram-writeback-"));
|
||||
const cronStorePath = path.join(tempDir, "cron", "jobs.json");
|
||||
const getChatMock = vi.fn();
|
||||
const sendMessageMock = vi.fn();
|
||||
const sendPollMock = vi.fn();
|
||||
const getChat: TelegramGetChat = async (...args) => {
|
||||
getChatMock(...args);
|
||||
return { id: -100321 } as unknown as Awaited<ReturnType<TelegramGetChat>>;
|
||||
};
|
||||
const sendMessage: TelegramSendMessage = async (...args) => {
|
||||
sendMessageMock(...args);
|
||||
return {
|
||||
message_id: 17,
|
||||
date: 1,
|
||||
chat: { id: "-100321" },
|
||||
} as unknown as Awaited<ReturnType<TelegramSendMessage>>;
|
||||
};
|
||||
const sendPoll: TelegramSendPoll = async (...args) => {
|
||||
sendPollMock(...args);
|
||||
return {
|
||||
message_id: 19,
|
||||
date: 1,
|
||||
chat: { id: "-100321" },
|
||||
poll: { id: "poll-1" },
|
||||
} as unknown as Awaited<ReturnType<TelegramSendPoll>>;
|
||||
};
|
||||
|
||||
const installTelegramTestPlugin = () => {
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "telegram",
|
||||
source: "test",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "telegram",
|
||||
label: "Telegram",
|
||||
outbound: {
|
||||
deliveryMode: "direct",
|
||||
sendText: async ({ cfg, to, text, accountId, gatewayClientScopes }) =>
|
||||
({
|
||||
channel: "telegram",
|
||||
...(await sendMessageTelegram(to, text, {
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
gatewayClientScopes,
|
||||
token: "123:abc",
|
||||
api: {
|
||||
getChat,
|
||||
sendMessage,
|
||||
},
|
||||
})),
|
||||
}),
|
||||
sendPoll: async ({ cfg, to, poll, accountId, gatewayClientScopes, threadId }) =>
|
||||
({
|
||||
channel: "telegram",
|
||||
...(await sendPollTelegram(to, poll, {
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
gatewayClientScopes,
|
||||
messageThreadId:
|
||||
typeof threadId === "number" && Number.isFinite(threadId)
|
||||
? Math.trunc(threadId)
|
||||
: undefined,
|
||||
token: "123:abc",
|
||||
api: {
|
||||
getChat,
|
||||
sendPoll,
|
||||
},
|
||||
})),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
]),
|
||||
"telegram-target-writeback-scope",
|
||||
);
|
||||
};
|
||||
|
||||
installTelegramTestPlugin();
|
||||
|
||||
try {
|
||||
await saveCronStore(cronStorePath, createCronStore());
|
||||
clearConfigCache();
|
||||
await writeConfigFile({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: "gpt-5.4",
|
||||
workspace: path.join(process.env.HOME ?? ".", "openclaw"),
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
telegram: {
|
||||
botToken: "123:abc",
|
||||
defaultTo: "https://t.me/mychannel",
|
||||
},
|
||||
},
|
||||
cron: {
|
||||
store: cronStorePath,
|
||||
},
|
||||
} satisfies OpenClawConfig);
|
||||
clearConfigCache();
|
||||
|
||||
await run({
|
||||
cronStorePath,
|
||||
getChatMock,
|
||||
sendMessageMock,
|
||||
sendPollMock,
|
||||
installTelegramTestPlugin,
|
||||
});
|
||||
} finally {
|
||||
setActivePluginRegistry(previousRegistry);
|
||||
clearConfigCache();
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe("gateway Telegram target writeback scope enforcement", () => {
|
||||
it("allows operator.write delivery but skips config and cron persistence", async () => {
|
||||
await withTelegramGatewayWritebackFixture(async (params) => {
|
||||
const { cronStorePath, getChatMock, sendMessageMock } = params;
|
||||
await withServer(async (ws) => {
|
||||
await connectOk(ws, { token: "secret", scopes: ["operator.write"] });
|
||||
|
||||
const current = await rpcReq<{ hash?: string }>(ws, "config.get", {});
|
||||
expect(current.ok).toBe(true);
|
||||
expect(typeof current.payload?.hash).toBe("string");
|
||||
|
||||
const directPatch = await rpcReq(ws, "config.patch", {
|
||||
raw: JSON.stringify({
|
||||
channels: {
|
||||
telegram: {
|
||||
defaultTo: "-100321",
|
||||
},
|
||||
},
|
||||
}),
|
||||
baseHash: current.payload?.hash,
|
||||
});
|
||||
expect(directPatch.ok).toBe(false);
|
||||
expect(directPatch.error?.message).toBe("missing scope: operator.admin");
|
||||
|
||||
const viaSend = await rpcReq(ws, "send", {
|
||||
to: "https://t.me/mychannel",
|
||||
message: "hello from send scope test",
|
||||
channel: "telegram",
|
||||
sessionKey: "main",
|
||||
idempotencyKey: "idem-send-telegram-target-writeback-operator-write",
|
||||
});
|
||||
expect(viaSend.ok).toBe(true);
|
||||
|
||||
clearConfigCache();
|
||||
const stored = loadConfig();
|
||||
const cronStore = await loadCronStore(cronStorePath);
|
||||
|
||||
expect(stored.channels?.telegram?.defaultTo).toBe("https://t.me/mychannel");
|
||||
expect(cronStore.jobs[0]?.delivery?.to).toBe("@mychannel");
|
||||
expect(getChatMock).toHaveBeenCalledWith("@mychannel");
|
||||
expect(sendMessageMock).toHaveBeenCalledWith("-100321", "hello from send scope test", {
|
||||
parse_mode: "HTML",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("persists config and cron rewrites for operator.admin delivery", async () => {
|
||||
await withTelegramGatewayWritebackFixture(async (params) => {
|
||||
const { cronStorePath, getChatMock, sendMessageMock } = params;
|
||||
await withServer(async (ws) => {
|
||||
await connectOk(ws, { token: "secret", scopes: ["operator.write", "operator.admin"] });
|
||||
|
||||
const viaSend = await rpcReq(ws, "send", {
|
||||
to: "https://t.me/mychannel",
|
||||
message: "hello from admin scope test",
|
||||
channel: "telegram",
|
||||
sessionKey: "main",
|
||||
idempotencyKey: "idem-send-telegram-target-writeback-operator-admin",
|
||||
});
|
||||
expect(viaSend.ok).toBe(true);
|
||||
|
||||
clearConfigCache();
|
||||
const stored = loadConfig();
|
||||
const cronStore = await loadCronStore(cronStorePath);
|
||||
|
||||
expect(stored.channels?.telegram?.defaultTo).toBe("-100321");
|
||||
expect(cronStore.jobs[0]?.delivery?.to).toBe("-100321");
|
||||
expect(getChatMock).toHaveBeenCalledWith("@mychannel");
|
||||
expect(sendMessageMock).toHaveBeenCalledWith("-100321", "hello from admin scope test", {
|
||||
parse_mode: "HTML",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("allows operator.write poll delivery but skips config and cron persistence", async () => {
|
||||
await withTelegramGatewayWritebackFixture(async (params) => {
|
||||
const { cronStorePath, getChatMock, sendPollMock, installTelegramTestPlugin } = params;
|
||||
await withServer(async (ws) => {
|
||||
releasePinnedPluginChannelRegistry();
|
||||
installTelegramTestPlugin();
|
||||
await connectOk(ws, { token: "secret", scopes: ["operator.write"] });
|
||||
|
||||
const viaPoll = await rpcReq(ws, "poll", {
|
||||
to: "https://t.me/mychannel",
|
||||
question: "Which one?",
|
||||
options: ["A", "B"],
|
||||
channel: "telegram",
|
||||
idempotencyKey: "idem-poll-telegram-target-writeback-operator-write",
|
||||
});
|
||||
if (!viaPoll.ok) {
|
||||
throw new Error(`poll failed: ${viaPoll.error?.message ?? "unknown error"}`);
|
||||
}
|
||||
expect(viaPoll.ok).toBe(true);
|
||||
|
||||
clearConfigCache();
|
||||
const stored = loadConfig();
|
||||
const cronStore = await loadCronStore(cronStorePath);
|
||||
|
||||
expect(stored.channels?.telegram?.defaultTo).toBe("https://t.me/mychannel");
|
||||
expect(cronStore.jobs[0]?.delivery?.to).toBe("@mychannel");
|
||||
expect(getChatMock).toHaveBeenCalledWith("@mychannel");
|
||||
expect(sendPollMock).toHaveBeenCalledWith("-100321", "Which one?", ["A", "B"], {
|
||||
allows_multiple_answers: false,
|
||||
is_anonymous: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -130,6 +130,7 @@ type ChannelHandlerParams = {
|
||||
forceDocument?: boolean;
|
||||
silent?: boolean;
|
||||
mediaLocalRoots?: readonly string[];
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
|
||||
// Channel docking: outbound delivery delegates to plugin.outbound adapters.
|
||||
@@ -250,6 +251,7 @@ function createChannelOutboundContextBase(
|
||||
deps: params.deps,
|
||||
silent: params.silent,
|
||||
mediaLocalRoots: params.mediaLocalRoots,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -275,6 +277,7 @@ type DeliverOutboundPayloadsCoreParams = {
|
||||
session?: OutboundSessionContext;
|
||||
mirror?: DeliveryMirror;
|
||||
silent?: boolean;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
|
||||
function collectPayloadMediaSources(payloads: ReplyPayload[]): string[] {
|
||||
@@ -508,6 +511,7 @@ export async function deliverOutboundPayloads(
|
||||
forceDocument: params.forceDocument,
|
||||
silent: params.silent,
|
||||
mirror: params.mirror,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
}).catch(() => null); // Best-effort — don't block delivery if queue write fails.
|
||||
|
||||
// Wrap onError to detect partial failures under bestEffort mode.
|
||||
@@ -576,6 +580,7 @@ async function deliverOutboundPayloadsCore(
|
||||
forceDocument: params.forceDocument,
|
||||
silent: params.silent,
|
||||
mediaLocalRoots,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
});
|
||||
const configuredTextLimit = handler.chunker
|
||||
? resolveTextChunkLimit(cfg, channel, accountId, {
|
||||
|
||||
@@ -75,6 +75,7 @@ function buildRecoveryDeliverParams(entry: QueuedDelivery, cfg: OpenClawConfig)
|
||||
forceDocument: entry.forceDocument,
|
||||
silent: entry.silent,
|
||||
mirror: entry.mirror,
|
||||
gatewayClientScopes: entry.gatewayClientScopes,
|
||||
skipQueue: true, // Prevent re-enqueueing during recovery.
|
||||
} satisfies Parameters<DeliverFn>[0];
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ export type QueuedDeliveryPayload = {
|
||||
forceDocument?: boolean;
|
||||
silent?: boolean;
|
||||
mirror?: OutboundMirror;
|
||||
/** Gateway caller scopes at enqueue time, preserved for recovery replay. */
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
|
||||
export interface QueuedDelivery extends QueuedDeliveryPayload {
|
||||
@@ -142,6 +144,7 @@ export async function enqueueDelivery(
|
||||
forceDocument: params.forceDocument,
|
||||
silent: params.silent,
|
||||
mirror: params.mirror,
|
||||
gatewayClientScopes: params.gatewayClientScopes,
|
||||
retryCount: 0,
|
||||
});
|
||||
return id;
|
||||
|
||||
@@ -125,6 +125,7 @@ describe("delivery-queue recovery", () => {
|
||||
bestEffort: true,
|
||||
gifPlayback: true,
|
||||
silent: true,
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
mirror: {
|
||||
sessionKey: "agent:main:main",
|
||||
text: "a",
|
||||
@@ -142,6 +143,7 @@ describe("delivery-queue recovery", () => {
|
||||
bestEffort: true,
|
||||
gifPlayback: true,
|
||||
silent: true,
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
mirror: {
|
||||
sessionKey: "agent:main:main",
|
||||
text: "a",
|
||||
|
||||
@@ -23,6 +23,7 @@ describe("delivery-queue storage", () => {
|
||||
bestEffort: true,
|
||||
gifPlayback: true,
|
||||
silent: true,
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
mirror: {
|
||||
sessionKey: "agent:main:main",
|
||||
text: "hello",
|
||||
@@ -45,6 +46,7 @@ describe("delivery-queue storage", () => {
|
||||
bestEffort: true,
|
||||
gifPlayback: true,
|
||||
silent: true,
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
mirror: {
|
||||
sessionKey: "agent:main:main",
|
||||
text: "hello",
|
||||
@@ -157,6 +159,21 @@ describe("delivery-queue storage", () => {
|
||||
expect(await loadPendingDeliveries(tmpDir())).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("persists gateway caller scopes for replay", async () => {
|
||||
const id = await enqueueDelivery(
|
||||
{
|
||||
channel: "telegram",
|
||||
to: "2",
|
||||
payloads: [{ text: "b" }],
|
||||
gatewayClientScopes: ["operator.write"],
|
||||
},
|
||||
tmpDir(),
|
||||
);
|
||||
|
||||
const entry = readQueuedEntry(tmpDir(), id);
|
||||
expect(entry.gatewayClientScopes).toEqual(["operator.write"]);
|
||||
});
|
||||
|
||||
it("backfills lastAttemptAt for legacy retry entries during load", async () => {
|
||||
const id = await enqueueDelivery(
|
||||
{ channel: "whatsapp", to: "+1", payloads: [{ text: "legacy" }] },
|
||||
|
||||
Reference in New Issue
Block a user