234 lines
6.3 KiB
TypeScript
234 lines
6.3 KiB
TypeScript
import {
|
|
loadChannels,
|
|
logoutWhatsApp,
|
|
startWhatsAppLogin,
|
|
waitWhatsAppLogin,
|
|
} from "./controllers/channels";
|
|
import { loadConfig, saveConfig } from "./controllers/config";
|
|
import type { ClawdbotApp } from "./app";
|
|
import type { NostrProfile } from "./types";
|
|
import { createNostrProfileFormState } from "./views/channels.nostr-profile-form";
|
|
|
|
export async function handleWhatsAppStart(host: ClawdbotApp, force: boolean) {
|
|
await startWhatsAppLogin(host, force);
|
|
await loadChannels(host, true);
|
|
}
|
|
|
|
export async function handleWhatsAppWait(host: ClawdbotApp) {
|
|
await waitWhatsAppLogin(host);
|
|
await loadChannels(host, true);
|
|
}
|
|
|
|
export async function handleWhatsAppLogout(host: ClawdbotApp) {
|
|
await logoutWhatsApp(host);
|
|
await loadChannels(host, true);
|
|
}
|
|
|
|
export async function handleChannelConfigSave(host: ClawdbotApp) {
|
|
await saveConfig(host);
|
|
await loadConfig(host);
|
|
await loadChannels(host, true);
|
|
}
|
|
|
|
export async function handleChannelConfigReload(host: ClawdbotApp) {
|
|
await loadConfig(host);
|
|
await loadChannels(host, true);
|
|
}
|
|
|
|
function parseValidationErrors(details: unknown): Record<string, string> {
|
|
if (!Array.isArray(details)) return {};
|
|
const errors: Record<string, string> = {};
|
|
for (const entry of details) {
|
|
if (typeof entry !== "string") continue;
|
|
const [rawField, ...rest] = entry.split(":");
|
|
if (!rawField || rest.length === 0) continue;
|
|
const field = rawField.trim();
|
|
const message = rest.join(":").trim();
|
|
if (field && message) errors[field] = message;
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
function resolveNostrAccountId(host: ClawdbotApp): string {
|
|
const accounts = host.channelsSnapshot?.channelAccounts?.nostr ?? [];
|
|
return accounts[0]?.accountId ?? host.nostrProfileAccountId ?? "default";
|
|
}
|
|
|
|
function buildNostrProfileUrl(accountId: string, suffix = ""): string {
|
|
return `/api/channels/nostr/${encodeURIComponent(accountId)}/profile${suffix}`;
|
|
}
|
|
|
|
export function handleNostrProfileEdit(
|
|
host: ClawdbotApp,
|
|
accountId: string,
|
|
profile: NostrProfile | null,
|
|
) {
|
|
host.nostrProfileAccountId = accountId;
|
|
host.nostrProfileFormState = createNostrProfileFormState(profile ?? undefined);
|
|
}
|
|
|
|
export function handleNostrProfileCancel(host: ClawdbotApp) {
|
|
host.nostrProfileFormState = null;
|
|
host.nostrProfileAccountId = null;
|
|
}
|
|
|
|
export function handleNostrProfileFieldChange(
|
|
host: ClawdbotApp,
|
|
field: keyof NostrProfile,
|
|
value: string,
|
|
) {
|
|
const state = host.nostrProfileFormState;
|
|
if (!state) return;
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
values: {
|
|
...state.values,
|
|
[field]: value,
|
|
},
|
|
fieldErrors: {
|
|
...state.fieldErrors,
|
|
[field]: "",
|
|
},
|
|
};
|
|
}
|
|
|
|
export function handleNostrProfileToggleAdvanced(host: ClawdbotApp) {
|
|
const state = host.nostrProfileFormState;
|
|
if (!state) return;
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
showAdvanced: !state.showAdvanced,
|
|
};
|
|
}
|
|
|
|
export async function handleNostrProfileSave(host: ClawdbotApp) {
|
|
const state = host.nostrProfileFormState;
|
|
if (!state || state.saving) return;
|
|
const accountId = resolveNostrAccountId(host);
|
|
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
saving: true,
|
|
error: null,
|
|
success: null,
|
|
fieldErrors: {},
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(buildNostrProfileUrl(accountId), {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(state.values),
|
|
});
|
|
const data = (await response.json().catch(() => null)) as
|
|
| { ok?: boolean; error?: string; details?: unknown; persisted?: boolean }
|
|
| null;
|
|
|
|
if (!response.ok || data?.ok === false || !data) {
|
|
const errorMessage = data?.error ?? `Profile update failed (${response.status})`;
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
saving: false,
|
|
error: errorMessage,
|
|
success: null,
|
|
fieldErrors: parseValidationErrors(data?.details),
|
|
};
|
|
return;
|
|
}
|
|
|
|
if (!data.persisted) {
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
saving: false,
|
|
error: "Profile publish failed on all relays.",
|
|
success: null,
|
|
};
|
|
return;
|
|
}
|
|
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
saving: false,
|
|
error: null,
|
|
success: "Profile published to relays.",
|
|
fieldErrors: {},
|
|
original: { ...state.values },
|
|
};
|
|
await loadChannels(host, true);
|
|
} catch (err) {
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
saving: false,
|
|
error: `Profile update failed: ${String(err)}`,
|
|
success: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
export async function handleNostrProfileImport(host: ClawdbotApp) {
|
|
const state = host.nostrProfileFormState;
|
|
if (!state || state.importing) return;
|
|
const accountId = resolveNostrAccountId(host);
|
|
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
importing: true,
|
|
error: null,
|
|
success: null,
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(buildNostrProfileUrl(accountId, "/import"), {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ autoMerge: true }),
|
|
});
|
|
const data = (await response.json().catch(() => null)) as
|
|
| { ok?: boolean; error?: string; imported?: NostrProfile; merged?: NostrProfile; saved?: boolean }
|
|
| null;
|
|
|
|
if (!response.ok || data?.ok === false || !data) {
|
|
const errorMessage = data?.error ?? `Profile import failed (${response.status})`;
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
importing: false,
|
|
error: errorMessage,
|
|
success: null,
|
|
};
|
|
return;
|
|
}
|
|
|
|
const merged = data.merged ?? data.imported ?? null;
|
|
const nextValues = merged ? { ...state.values, ...merged } : state.values;
|
|
const showAdvanced = Boolean(
|
|
nextValues.banner || nextValues.website || nextValues.nip05 || nextValues.lud16,
|
|
);
|
|
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
importing: false,
|
|
values: nextValues,
|
|
error: null,
|
|
success: data.saved
|
|
? "Profile imported from relays. Review and publish."
|
|
: "Profile imported. Review and publish.",
|
|
showAdvanced,
|
|
};
|
|
|
|
if (data.saved) {
|
|
await loadChannels(host, true);
|
|
}
|
|
} catch (err) {
|
|
host.nostrProfileFormState = {
|
|
...state,
|
|
importing: false,
|
|
error: `Profile import failed: ${String(err)}`,
|
|
success: null,
|
|
};
|
|
}
|
|
}
|