refactor: extend channel plugin boundary

This commit is contained in:
Peter Steinberger
2026-01-20 11:49:31 +00:00
parent 439044068a
commit 9a2bf57e1c
31 changed files with 234 additions and 162 deletions

View File

@@ -264,6 +264,8 @@ export function renderApp(state: AppViewState) {
error: state.cronError,
busy: state.cronBusy,
form: state.cronForm,
channels: state.channelsSnapshot?.channelOrder ?? [],
channelLabels: state.channelsSnapshot?.channelLabels ?? {},
runsJobId: state.cronRunsJobId,
runs: state.cronRuns,
onFormChange: (patch) => (state.cronForm = { ...state.cronForm, ...patch }),

View File

@@ -307,6 +307,7 @@ export async function loadChannelsTab(host: SettingsHost) {
export async function loadCron(host: SettingsHost) {
await Promise.all([
loadChannels(host as unknown as ClawdbotApp, false),
loadCronStatus(host as unknown as ClawdbotApp),
loadCronJobs(host as unknown as ClawdbotApp),
]);

View File

@@ -73,16 +73,7 @@ export function buildCronPayload(form: CronFormState) {
kind: "agentTurn";
message: string;
deliver?: boolean;
channel?:
| "last"
| "whatsapp"
| "telegram"
| "discord"
| "slack"
| "signal"
| "imessage"
| "msteams"
| "bluebubbles";
channel?: string;
to?: string;
timeoutSeconds?: number;
} = { kind: "agentTurn", message };

View File

@@ -2,11 +2,15 @@ export type ChannelsStatusSnapshot = {
ts: number;
channelOrder: string[];
channelLabels: Record<string, string>;
channelDetailLabels?: Record<string, string>;
channelSystemImages?: Record<string, string>;
channels: Record<string, unknown>;
channelAccounts: Record<string, ChannelAccountSnapshot[]>;
channelDefaultAccountId: Record<string, string>;
};
export const CRON_CHANNEL_LAST = "last";
export type ChannelAccountSnapshot = {
accountId: string;
name?: string | null;
@@ -324,16 +328,8 @@ export type CronPayload =
thinking?: string;
timeoutSeconds?: number;
deliver?: boolean;
provider?:
| "last"
| "whatsapp"
| "telegram"
| "discord"
| "slack"
| "signal"
| "imessage"
| "msteams"
| "bluebubbles";
channel?: string;
provider?: string;
to?: string;
bestEffortDeliver?: boolean;
};

View File

@@ -4,6 +4,8 @@ export type ChatQueueItem = {
createdAt: number;
};
export const CRON_CHANNEL_LAST = "last";
export type CronFormState = {
name: string;
description: string;
@@ -20,16 +22,7 @@ export type CronFormState = {
payloadKind: "systemEvent" | "agentTurn";
payloadText: string;
deliver: boolean;
channel:
| "last"
| "whatsapp"
| "telegram"
| "discord"
| "slack"
| "signal"
| "imessage"
| "msteams"
| "bluebubbles";
channel: string;
to: string;
timeoutSeconds: string;
postToMainPrefix: string;

View File

@@ -88,7 +88,7 @@ function resolveChannelOrder(snapshot: ChannelsStatusSnapshot | null): ChannelKe
if (snapshot?.channelOrder?.length) {
return snapshot.channelOrder;
}
return ["whatsapp", "telegram", "discord", "slack", "signal", "imessage", "bluebubbles"];
return ["whatsapp", "telegram", "discord", "slack", "signal", "imessage"];
}
function renderChannel(

View File

@@ -27,6 +27,8 @@ function createProps(overrides: Partial<CronProps> = {}): CronProps {
error: null,
busy: false,
form: { ...DEFAULT_CRON_FORM },
channels: [],
channelLabels: {},
runsJobId: null,
runs: [],
onFormChange: () => undefined,

View File

@@ -17,6 +17,8 @@ export type CronProps = {
error: string | null;
busy: boolean;
form: CronFormState;
channels: string[];
channelLabels?: Record<string, string>;
runsJobId: string | null;
runs: CronRunLogEntry[];
onFormChange: (patch: Partial<CronFormState>) => void;
@@ -28,7 +30,27 @@ export type CronProps = {
onLoadRuns: (jobId: string) => void;
};
function buildChannelOptions(props: CronProps): string[] {
const options = ["last", ...props.channels.filter(Boolean)];
const current = props.form.channel?.trim();
if (current && !options.includes(current)) {
options.push(current);
}
const seen = new Set<string>();
return options.filter((value) => {
if (seen.has(value)) return false;
seen.add(value);
return true;
});
}
function resolveChannelLabel(props: CronProps, channel: string): string {
if (channel === "last") return "last";
return props.channelLabels?.[channel] ?? channel;
}
export function renderCron(props: CronProps) {
const channelOptions = buildChannelOptions(props);
return html`
<section class="grid grid-cols-2">
<div class="card">
@@ -185,21 +207,18 @@ export function renderCron(props: CronProps) {
<label class="field">
<span>Channel</span>
<select
.value=${props.form.channel}
.value=${props.form.channel || "last"}
@change=${(e: Event) =>
props.onFormChange({
channel: (e.target as HTMLSelectElement).value as CronFormState["channel"],
})}
>
<option value="last">Last</option>
<option value="whatsapp">WhatsApp</option>
<option value="telegram">Telegram</option>
<option value="discord">Discord</option>
<option value="slack">Slack</option>
<option value="signal">Signal</option>
<option value="imessage">iMessage</option>
<option value="msteams">MS Teams</option>
<option value="bluebubbles">BlueBubbles</option>
${channelOptions.map(
(channel) =>
html`<option value=${channel}>
${resolveChannelLabel(props, channel)}
</option>`,
)}
</select>
</label>
<label class="field">