mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-12 02:06:02 +00:00
* fix telegram plugin callback routing * Add Telegram callback proof coverage * Fix proof script lint * Fix Telegram proof CI regressions * test: fix telegram callback proof CI regressions * fix: retire registry-owned callbacks * fix: retire registry-owned callbacks --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
150 lines
5.3 KiB
TypeScript
150 lines
5.3 KiB
TypeScript
// Maintains interactive plugin registry entries discovered from manifests.
|
|
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
|
|
import {
|
|
normalizePluginInteractiveNamespace,
|
|
resolvePluginInteractiveMatch,
|
|
toPluginInteractiveRegistryKey,
|
|
validatePluginInteractiveNamespace,
|
|
} from "./interactive-shared.js";
|
|
import {
|
|
clearPluginInteractiveHandlerRegistrationsState,
|
|
clearPluginInteractiveHandlersState,
|
|
getPluginInteractiveHandlersState,
|
|
type RegisteredInteractiveHandler,
|
|
} from "./interactive-state.js";
|
|
import type { PluginInteractiveHandlerRegistration } from "./types.js";
|
|
|
|
/** Registration result for plugin interactive namespace handlers. */
|
|
export type InteractiveRegistrationResult = {
|
|
ok: boolean;
|
|
error?: string;
|
|
};
|
|
|
|
/** Resolves a channel payload to a registered plugin interactive namespace handler. */
|
|
export function resolvePluginInteractiveNamespaceMatch(
|
|
channel: string,
|
|
data: string,
|
|
): { registration: RegisteredInteractiveHandler; namespace: string; payload: string } | null {
|
|
return resolvePluginInteractiveMatch({
|
|
interactiveHandlers: getPluginInteractiveHandlersState(),
|
|
channel,
|
|
data,
|
|
});
|
|
}
|
|
|
|
/** Resolves a handler from registry-owned registrations without changing global state. */
|
|
export function resolvePluginInteractiveRegistrationsMatch(
|
|
registrations: readonly RegisteredInteractiveHandler[],
|
|
channel: string,
|
|
data: string,
|
|
): { registration: RegisteredInteractiveHandler; namespace: string; payload: string } | null {
|
|
return resolvePluginInteractiveMatch({
|
|
interactiveHandlers: {
|
|
get: (key) =>
|
|
registrations.find(
|
|
(registration) =>
|
|
toPluginInteractiveRegistryKey(registration.channel, registration.namespace) === key,
|
|
),
|
|
},
|
|
channel,
|
|
data,
|
|
});
|
|
}
|
|
|
|
/** Registers one plugin interactive namespace for a channel. */
|
|
function registerPluginInteractiveHandlerWithOptions(
|
|
pluginId: string,
|
|
registration: PluginInteractiveHandlerRegistration,
|
|
opts?: { pluginName?: string; pluginRoot?: string; registryOwned?: true },
|
|
): InteractiveRegistrationResult {
|
|
const interactiveHandlers = getPluginInteractiveHandlersState();
|
|
const namespace = normalizePluginInteractiveNamespace(registration.namespace);
|
|
const validationError = validatePluginInteractiveNamespace(namespace);
|
|
if (validationError) {
|
|
return { ok: false, error: validationError };
|
|
}
|
|
const key = toPluginInteractiveRegistryKey(registration.channel, namespace);
|
|
const existing = interactiveHandlers.get(key);
|
|
if (existing) {
|
|
return {
|
|
ok: false,
|
|
error: `Interactive handler namespace "${namespace}" already registered by plugin "${existing.pluginId}"`,
|
|
};
|
|
}
|
|
interactiveHandlers.set(key, {
|
|
...registration,
|
|
namespace,
|
|
channel: normalizeOptionalLowercaseString(registration.channel) ?? "",
|
|
pluginId,
|
|
pluginName: opts?.pluginName,
|
|
pluginRoot: opts?.pluginRoot,
|
|
registryOwned: opts?.registryOwned,
|
|
});
|
|
return { ok: true };
|
|
}
|
|
|
|
/** Registers one process-global interactive handler. */
|
|
export function registerPluginInteractiveHandler(
|
|
pluginId: string,
|
|
registration: PluginInteractiveHandlerRegistration,
|
|
opts?: { pluginName?: string; pluginRoot?: string },
|
|
): InteractiveRegistrationResult {
|
|
return registerPluginInteractiveHandlerWithOptions(pluginId, registration, opts);
|
|
}
|
|
|
|
/** Registers one handler whose lifetime follows its owning plugin registry. */
|
|
export function registerRegistryPluginInteractiveHandler(
|
|
pluginId: string,
|
|
registration: PluginInteractiveHandlerRegistration,
|
|
opts?: { pluginName?: string; pluginRoot?: string },
|
|
): InteractiveRegistrationResult {
|
|
return registerPluginInteractiveHandlerWithOptions(pluginId, registration, {
|
|
...opts,
|
|
registryOwned: true,
|
|
});
|
|
}
|
|
|
|
/** Clears all active plugin interactive handlers. */
|
|
export function clearPluginInteractiveHandlers(): void {
|
|
clearPluginInteractiveHandlersState();
|
|
}
|
|
|
|
/** Clears stored plugin interactive handler registrations. */
|
|
export function clearPluginInteractiveHandlerRegistrations(): void {
|
|
clearPluginInteractiveHandlerRegistrationsState();
|
|
}
|
|
|
|
/** Clears active interactive handlers owned by one plugin. */
|
|
export function clearPluginInteractiveHandlersForPlugin(pluginId: string): void {
|
|
const interactiveHandlers = getPluginInteractiveHandlersState();
|
|
for (const [key, value] of interactiveHandlers.entries()) {
|
|
if (value.pluginId === pluginId) {
|
|
interactiveHandlers.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Lists active plugin interactive handlers. */
|
|
export function listPluginInteractiveHandlers(): RegisteredInteractiveHandler[] {
|
|
return Array.from(getPluginInteractiveHandlersState().values());
|
|
}
|
|
|
|
/** Restores active plugin interactive handlers from a saved registry snapshot. */
|
|
export function restorePluginInteractiveHandlers(
|
|
registrations: readonly RegisteredInteractiveHandler[],
|
|
): void {
|
|
clearPluginInteractiveHandlerRegistrations();
|
|
const interactiveHandlers = getPluginInteractiveHandlersState();
|
|
for (const registration of registrations) {
|
|
const namespace = normalizePluginInteractiveNamespace(registration.namespace);
|
|
if (!namespace) {
|
|
continue;
|
|
}
|
|
interactiveHandlers.set(toPluginInteractiveRegistryKey(registration.channel, namespace), {
|
|
...registration,
|
|
namespace,
|
|
channel: normalizeOptionalLowercaseString(registration.channel) ?? "",
|
|
});
|
|
}
|
|
}
|