Update connections UIs
This commit is contained in:
@@ -23,7 +23,13 @@ import type {
|
||||
SkillStatusReport,
|
||||
StatusSummary,
|
||||
} from "./types";
|
||||
import type { CronFormState, TelegramForm } from "./ui-types";
|
||||
import type {
|
||||
CronFormState,
|
||||
DiscordForm,
|
||||
IMessageForm,
|
||||
SignalForm,
|
||||
TelegramForm,
|
||||
} from "./ui-types";
|
||||
import { renderChat } from "./views/chat";
|
||||
import { renderConfig } from "./views/config";
|
||||
import { renderConnections } from "./views/connections";
|
||||
@@ -34,7 +40,13 @@ import { renderNodes } from "./views/nodes";
|
||||
import { renderOverview } from "./views/overview";
|
||||
import { renderSessions } from "./views/sessions";
|
||||
import { renderSkills } from "./views/skills";
|
||||
import { loadProviders } from "./controllers/connections";
|
||||
import {
|
||||
loadProviders,
|
||||
updateDiscordForm,
|
||||
updateIMessageForm,
|
||||
updateSignalForm,
|
||||
updateTelegramForm,
|
||||
} from "./controllers/connections";
|
||||
import { loadPresence } from "./controllers/presence";
|
||||
import { loadSessions, patchSession } from "./controllers/sessions";
|
||||
import {
|
||||
@@ -95,6 +107,16 @@ export type AppViewState = {
|
||||
telegramSaving: boolean;
|
||||
telegramTokenLocked: boolean;
|
||||
telegramConfigStatus: string | null;
|
||||
discordForm: DiscordForm;
|
||||
discordSaving: boolean;
|
||||
discordTokenLocked: boolean;
|
||||
discordConfigStatus: string | null;
|
||||
signalForm: SignalForm;
|
||||
signalSaving: boolean;
|
||||
signalConfigStatus: string | null;
|
||||
imessageForm: IMessageForm;
|
||||
imessageSaving: boolean;
|
||||
imessageConfigStatus: string | null;
|
||||
presenceLoading: boolean;
|
||||
presenceEntries: PresenceEntry[];
|
||||
presenceError: string | null;
|
||||
@@ -235,12 +257,28 @@ export function renderApp(state: AppViewState) {
|
||||
telegramTokenLocked: state.telegramTokenLocked,
|
||||
telegramSaving: state.telegramSaving,
|
||||
telegramStatus: state.telegramConfigStatus,
|
||||
discordForm: state.discordForm,
|
||||
discordTokenLocked: state.discordTokenLocked,
|
||||
discordSaving: state.discordSaving,
|
||||
discordStatus: state.discordConfigStatus,
|
||||
signalForm: state.signalForm,
|
||||
signalSaving: state.signalSaving,
|
||||
signalStatus: state.signalConfigStatus,
|
||||
imessageForm: state.imessageForm,
|
||||
imessageSaving: state.imessageSaving,
|
||||
imessageStatus: state.imessageConfigStatus,
|
||||
onRefresh: (probe) => loadProviders(state, probe),
|
||||
onWhatsAppStart: (force) => state.handleWhatsAppStart(force),
|
||||
onWhatsAppWait: () => state.handleWhatsAppWait(),
|
||||
onWhatsAppLogout: () => state.handleWhatsAppLogout(),
|
||||
onTelegramChange: (patch) => updateTelegramForm(state, patch),
|
||||
onTelegramSave: () => state.handleTelegramSave(),
|
||||
onDiscordChange: (patch) => updateDiscordForm(state, patch),
|
||||
onDiscordSave: () => state.handleDiscordSave(),
|
||||
onSignalChange: (patch) => updateSignalForm(state, patch),
|
||||
onSignalSave: () => state.handleSignalSave(),
|
||||
onIMessageChange: (patch) => updateIMessageForm(state, patch),
|
||||
onIMessageSave: () => state.handleIMessageSave(),
|
||||
})
|
||||
: nothing}
|
||||
|
||||
|
||||
@@ -26,13 +26,22 @@ import type {
|
||||
SkillStatusReport,
|
||||
StatusSummary,
|
||||
} from "./types";
|
||||
import type { CronFormState, TelegramForm } from "./ui-types";
|
||||
import type {
|
||||
CronFormState,
|
||||
DiscordForm,
|
||||
IMessageForm,
|
||||
SignalForm,
|
||||
TelegramForm,
|
||||
} from "./ui-types";
|
||||
import { loadChatHistory, sendChat, handleChatEvent } from "./controllers/chat";
|
||||
import { loadNodes } from "./controllers/nodes";
|
||||
import { loadConfig } from "./controllers/config";
|
||||
import {
|
||||
loadProviders,
|
||||
logoutWhatsApp,
|
||||
saveDiscordConfig,
|
||||
saveIMessageConfig,
|
||||
saveSignalConfig,
|
||||
saveTelegramConfig,
|
||||
startWhatsAppLogin,
|
||||
waitWhatsAppLogin,
|
||||
@@ -126,6 +135,52 @@ export class ClawdisApp extends LitElement {
|
||||
@state() telegramSaving = false;
|
||||
@state() telegramTokenLocked = false;
|
||||
@state() telegramConfigStatus: string | null = null;
|
||||
@state() discordForm: DiscordForm = {
|
||||
enabled: true,
|
||||
token: "",
|
||||
allowFrom: "",
|
||||
groupEnabled: false,
|
||||
groupChannels: "",
|
||||
mediaMaxMb: "",
|
||||
historyLimit: "",
|
||||
enableReactions: true,
|
||||
slashEnabled: false,
|
||||
slashName: "",
|
||||
slashSessionPrefix: "",
|
||||
slashEphemeral: true,
|
||||
};
|
||||
@state() discordSaving = false;
|
||||
@state() discordTokenLocked = false;
|
||||
@state() discordConfigStatus: string | null = null;
|
||||
@state() signalForm: SignalForm = {
|
||||
enabled: true,
|
||||
account: "",
|
||||
httpUrl: "",
|
||||
httpHost: "",
|
||||
httpPort: "",
|
||||
cliPath: "",
|
||||
autoStart: true,
|
||||
receiveMode: "",
|
||||
ignoreAttachments: false,
|
||||
ignoreStories: false,
|
||||
sendReadReceipts: false,
|
||||
allowFrom: "",
|
||||
mediaMaxMb: "",
|
||||
};
|
||||
@state() signalSaving = false;
|
||||
@state() signalConfigStatus: string | null = null;
|
||||
@state() imessageForm: IMessageForm = {
|
||||
enabled: true,
|
||||
cliPath: "",
|
||||
dbPath: "",
|
||||
service: "auto",
|
||||
region: "",
|
||||
allowFrom: "",
|
||||
includeAttachments: false,
|
||||
mediaMaxMb: "",
|
||||
};
|
||||
@state() imessageSaving = false;
|
||||
@state() imessageConfigStatus: string | null = null;
|
||||
|
||||
@state() presenceLoading = false;
|
||||
@state() presenceEntries: PresenceEntry[] = [];
|
||||
@@ -509,6 +564,24 @@ export class ClawdisApp extends LitElement {
|
||||
await loadProviders(this, true);
|
||||
}
|
||||
|
||||
async handleDiscordSave() {
|
||||
await saveDiscordConfig(this);
|
||||
await loadConfig(this);
|
||||
await loadProviders(this, true);
|
||||
}
|
||||
|
||||
async handleSignalSave() {
|
||||
await saveSignalConfig(this);
|
||||
await loadConfig(this);
|
||||
await loadProviders(this, true);
|
||||
}
|
||||
|
||||
async handleIMessageSave() {
|
||||
await saveIMessageConfig(this);
|
||||
await loadConfig(this);
|
||||
await loadProviders(this, true);
|
||||
}
|
||||
|
||||
render() {
|
||||
return renderApp(this);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { GatewayBrowserClient } from "../gateway";
|
||||
import type { ConfigSnapshot } from "../types";
|
||||
import type { TelegramForm } from "../ui-types";
|
||||
import type { DiscordForm, IMessageForm, SignalForm, TelegramForm } from "../ui-types";
|
||||
|
||||
export type ConfigState = {
|
||||
client: GatewayBrowserClient | null;
|
||||
@@ -13,7 +13,13 @@ export type ConfigState = {
|
||||
configSnapshot: ConfigSnapshot | null;
|
||||
lastError: string | null;
|
||||
telegramForm: TelegramForm;
|
||||
discordForm: DiscordForm;
|
||||
signalForm: SignalForm;
|
||||
imessageForm: IMessageForm;
|
||||
telegramConfigStatus: string | null;
|
||||
discordConfigStatus: string | null;
|
||||
signalConfigStatus: string | null;
|
||||
imessageConfigStatus: string | null;
|
||||
};
|
||||
|
||||
export async function loadConfig(state: ConfigState) {
|
||||
@@ -42,11 +48,18 @@ export function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot
|
||||
|
||||
const config = snapshot.config ?? {};
|
||||
const telegram = (config.telegram ?? {}) as Record<string, unknown>;
|
||||
const discord = (config.discord ?? {}) as Record<string, unknown>;
|
||||
const signal = (config.signal ?? {}) as Record<string, unknown>;
|
||||
const imessage = (config.imessage ?? {}) as Record<string, unknown>;
|
||||
const toList = (value: unknown) =>
|
||||
Array.isArray(value)
|
||||
? value
|
||||
.map((v) => String(v ?? "").trim())
|
||||
.filter((v) => v.length > 0)
|
||||
.join(", ")
|
||||
: "";
|
||||
const allowFrom = Array.isArray(telegram.allowFrom)
|
||||
? (telegram.allowFrom as unknown[])
|
||||
.map((v) => String(v ?? "").trim())
|
||||
.filter((v) => v.length > 0)
|
||||
.join(", ")
|
||||
? toList(telegram.allowFrom)
|
||||
: typeof telegram.allowFrom === "string"
|
||||
? telegram.allowFrom
|
||||
: "";
|
||||
@@ -63,7 +76,77 @@ export function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot
|
||||
webhookPath: typeof telegram.webhookPath === "string" ? telegram.webhookPath : "",
|
||||
};
|
||||
|
||||
state.telegramConfigStatus = snapshot.valid === false ? "Config invalid." : null;
|
||||
const discordDm = (discord.dm ?? {}) as Record<string, unknown>;
|
||||
const slash = (discord.slashCommand ?? {}) as Record<string, unknown>;
|
||||
state.discordForm = {
|
||||
enabled: typeof discord.enabled === "boolean" ? discord.enabled : true,
|
||||
token: typeof discord.token === "string" ? discord.token : "",
|
||||
allowFrom: toList(discordDm.allowFrom),
|
||||
groupEnabled:
|
||||
typeof discordDm.groupEnabled === "boolean" ? discordDm.groupEnabled : false,
|
||||
groupChannels: toList(discordDm.groupChannels),
|
||||
mediaMaxMb:
|
||||
typeof discord.mediaMaxMb === "number" ? String(discord.mediaMaxMb) : "",
|
||||
historyLimit:
|
||||
typeof discord.historyLimit === "number" ? String(discord.historyLimit) : "",
|
||||
enableReactions:
|
||||
typeof discord.enableReactions === "boolean" ? discord.enableReactions : true,
|
||||
slashEnabled: typeof slash.enabled === "boolean" ? slash.enabled : false,
|
||||
slashName: typeof slash.name === "string" ? slash.name : "",
|
||||
slashSessionPrefix:
|
||||
typeof slash.sessionPrefix === "string" ? slash.sessionPrefix : "",
|
||||
slashEphemeral:
|
||||
typeof slash.ephemeral === "boolean" ? slash.ephemeral : true,
|
||||
};
|
||||
|
||||
state.signalForm = {
|
||||
enabled: typeof signal.enabled === "boolean" ? signal.enabled : true,
|
||||
account: typeof signal.account === "string" ? signal.account : "",
|
||||
httpUrl: typeof signal.httpUrl === "string" ? signal.httpUrl : "",
|
||||
httpHost: typeof signal.httpHost === "string" ? signal.httpHost : "",
|
||||
httpPort: typeof signal.httpPort === "number" ? String(signal.httpPort) : "",
|
||||
cliPath: typeof signal.cliPath === "string" ? signal.cliPath : "",
|
||||
autoStart: typeof signal.autoStart === "boolean" ? signal.autoStart : true,
|
||||
receiveMode:
|
||||
signal.receiveMode === "on-start" || signal.receiveMode === "manual"
|
||||
? signal.receiveMode
|
||||
: "",
|
||||
ignoreAttachments:
|
||||
typeof signal.ignoreAttachments === "boolean" ? signal.ignoreAttachments : false,
|
||||
ignoreStories:
|
||||
typeof signal.ignoreStories === "boolean" ? signal.ignoreStories : false,
|
||||
sendReadReceipts:
|
||||
typeof signal.sendReadReceipts === "boolean" ? signal.sendReadReceipts : false,
|
||||
allowFrom: toList(signal.allowFrom),
|
||||
mediaMaxMb:
|
||||
typeof signal.mediaMaxMb === "number" ? String(signal.mediaMaxMb) : "",
|
||||
};
|
||||
|
||||
state.imessageForm = {
|
||||
enabled: typeof imessage.enabled === "boolean" ? imessage.enabled : true,
|
||||
cliPath: typeof imessage.cliPath === "string" ? imessage.cliPath : "",
|
||||
dbPath: typeof imessage.dbPath === "string" ? imessage.dbPath : "",
|
||||
service:
|
||||
imessage.service === "imessage" ||
|
||||
imessage.service === "sms" ||
|
||||
imessage.service === "auto"
|
||||
? imessage.service
|
||||
: "auto",
|
||||
region: typeof imessage.region === "string" ? imessage.region : "",
|
||||
allowFrom: toList(imessage.allowFrom),
|
||||
includeAttachments:
|
||||
typeof imessage.includeAttachments === "boolean"
|
||||
? imessage.includeAttachments
|
||||
: false,
|
||||
mediaMaxMb:
|
||||
typeof imessage.mediaMaxMb === "number" ? String(imessage.mediaMaxMb) : "",
|
||||
};
|
||||
|
||||
const configInvalid = snapshot.valid === false ? "Config invalid." : null;
|
||||
state.telegramConfigStatus = configInvalid;
|
||||
state.discordConfigStatus = configInvalid;
|
||||
state.signalConfigStatus = configInvalid;
|
||||
state.imessageConfigStatus = configInvalid;
|
||||
}
|
||||
|
||||
export async function saveConfig(state: ConfigState) {
|
||||
@@ -79,4 +162,3 @@ export async function saveConfig(state: ConfigState) {
|
||||
state.configSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { GatewayBrowserClient } from "../gateway";
|
||||
import { parseList } from "../format";
|
||||
import type { ConfigSnapshot, ProvidersStatusSnapshot } from "../types";
|
||||
import type { TelegramForm } from "../ui-types";
|
||||
import type { DiscordForm, IMessageForm, SignalForm, TelegramForm } from "../ui-types";
|
||||
|
||||
export type ConnectionsState = {
|
||||
client: GatewayBrowserClient | null;
|
||||
@@ -18,6 +18,16 @@ export type ConnectionsState = {
|
||||
telegramSaving: boolean;
|
||||
telegramTokenLocked: boolean;
|
||||
telegramConfigStatus: string | null;
|
||||
discordForm: DiscordForm;
|
||||
discordSaving: boolean;
|
||||
discordTokenLocked: boolean;
|
||||
discordConfigStatus: string | null;
|
||||
signalForm: SignalForm;
|
||||
signalSaving: boolean;
|
||||
signalConfigStatus: string | null;
|
||||
imessageForm: IMessageForm;
|
||||
imessageSaving: boolean;
|
||||
imessageConfigStatus: string | null;
|
||||
configSnapshot: ConfigSnapshot | null;
|
||||
};
|
||||
|
||||
@@ -34,6 +44,7 @@ export async function loadProviders(state: ConnectionsState, probe: boolean) {
|
||||
state.providersSnapshot = res;
|
||||
state.providersLastSuccess = Date.now();
|
||||
state.telegramTokenLocked = res.telegram.tokenSource === "env";
|
||||
state.discordTokenLocked = res.discord?.tokenSource === "env";
|
||||
} catch (err) {
|
||||
state.providersError = String(err);
|
||||
} finally {
|
||||
@@ -101,6 +112,27 @@ export function updateTelegramForm(
|
||||
state.telegramForm = { ...state.telegramForm, ...patch };
|
||||
}
|
||||
|
||||
export function updateDiscordForm(
|
||||
state: ConnectionsState,
|
||||
patch: Partial<DiscordForm>,
|
||||
) {
|
||||
state.discordForm = { ...state.discordForm, ...patch };
|
||||
}
|
||||
|
||||
export function updateSignalForm(
|
||||
state: ConnectionsState,
|
||||
patch: Partial<SignalForm>,
|
||||
) {
|
||||
state.signalForm = { ...state.signalForm, ...patch };
|
||||
}
|
||||
|
||||
export function updateIMessageForm(
|
||||
state: ConnectionsState,
|
||||
patch: Partial<IMessageForm>,
|
||||
) {
|
||||
state.imessageForm = { ...state.imessageForm, ...patch };
|
||||
}
|
||||
|
||||
export async function saveTelegramConfig(state: ConnectionsState) {
|
||||
if (!state.client || !state.connected) return;
|
||||
if (state.telegramSaving) return;
|
||||
@@ -143,3 +175,243 @@ export async function saveTelegramConfig(state: ConnectionsState) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveDiscordConfig(state: ConnectionsState) {
|
||||
if (!state.client || !state.connected) return;
|
||||
if (state.discordSaving) return;
|
||||
state.discordSaving = true;
|
||||
state.discordConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const discord = { ...(config.discord ?? {}) } as Record<string, unknown>;
|
||||
const form = state.discordForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete discord.enabled;
|
||||
} else {
|
||||
discord.enabled = false;
|
||||
}
|
||||
|
||||
if (!state.discordTokenLocked) {
|
||||
const token = form.token.trim();
|
||||
if (token) discord.token = token;
|
||||
else delete discord.token;
|
||||
}
|
||||
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
const groupChannels = parseList(form.groupChannels);
|
||||
const dm = { ...(discord.dm ?? {}) } as Record<string, unknown>;
|
||||
if (allowFrom.length > 0) dm.allowFrom = allowFrom;
|
||||
else delete dm.allowFrom;
|
||||
if (form.groupEnabled) dm.groupEnabled = true;
|
||||
else delete dm.groupEnabled;
|
||||
if (groupChannels.length > 0) dm.groupChannels = groupChannels;
|
||||
else delete dm.groupChannels;
|
||||
if (Object.keys(dm).length > 0) discord.dm = dm;
|
||||
else delete discord.dm;
|
||||
|
||||
const mediaMaxMb = Number(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
discord.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete discord.mediaMaxMb;
|
||||
}
|
||||
|
||||
const historyLimit = Number(form.historyLimit);
|
||||
if (Number.isFinite(historyLimit) && historyLimit >= 0) {
|
||||
discord.historyLimit = historyLimit;
|
||||
} else {
|
||||
delete discord.historyLimit;
|
||||
}
|
||||
|
||||
if (form.enableReactions) {
|
||||
delete discord.enableReactions;
|
||||
} else {
|
||||
discord.enableReactions = false;
|
||||
}
|
||||
|
||||
const slash = { ...(discord.slashCommand ?? {}) } as Record<string, unknown>;
|
||||
if (form.slashEnabled) {
|
||||
slash.enabled = true;
|
||||
} else {
|
||||
delete slash.enabled;
|
||||
}
|
||||
if (form.slashName.trim()) slash.name = form.slashName.trim();
|
||||
else delete slash.name;
|
||||
if (form.slashSessionPrefix.trim())
|
||||
slash.sessionPrefix = form.slashSessionPrefix.trim();
|
||||
else delete slash.sessionPrefix;
|
||||
if (form.slashEphemeral) {
|
||||
delete slash.ephemeral;
|
||||
} else {
|
||||
slash.ephemeral = false;
|
||||
}
|
||||
if (Object.keys(slash).length > 0) discord.slashCommand = slash;
|
||||
else delete discord.slashCommand;
|
||||
|
||||
if (Object.keys(discord).length > 0) {
|
||||
config.discord = discord;
|
||||
} else {
|
||||
delete config.discord;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
state.discordConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.discordConfigStatus = String(err);
|
||||
} finally {
|
||||
state.discordSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveSignalConfig(state: ConnectionsState) {
|
||||
if (!state.client || !state.connected) return;
|
||||
if (state.signalSaving) return;
|
||||
state.signalSaving = true;
|
||||
state.signalConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const signal = { ...(config.signal ?? {}) } as Record<string, unknown>;
|
||||
const form = state.signalForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete signal.enabled;
|
||||
} else {
|
||||
signal.enabled = false;
|
||||
}
|
||||
|
||||
const account = form.account.trim();
|
||||
if (account) signal.account = account;
|
||||
else delete signal.account;
|
||||
|
||||
const httpUrl = form.httpUrl.trim();
|
||||
if (httpUrl) signal.httpUrl = httpUrl;
|
||||
else delete signal.httpUrl;
|
||||
|
||||
const httpHost = form.httpHost.trim();
|
||||
if (httpHost) signal.httpHost = httpHost;
|
||||
else delete signal.httpHost;
|
||||
|
||||
const httpPort = Number(form.httpPort);
|
||||
if (Number.isFinite(httpPort) && httpPort > 0) {
|
||||
signal.httpPort = httpPort;
|
||||
} else {
|
||||
delete signal.httpPort;
|
||||
}
|
||||
|
||||
const cliPath = form.cliPath.trim();
|
||||
if (cliPath) signal.cliPath = cliPath;
|
||||
else delete signal.cliPath;
|
||||
|
||||
if (form.autoStart) {
|
||||
delete signal.autoStart;
|
||||
} else {
|
||||
signal.autoStart = false;
|
||||
}
|
||||
|
||||
if (form.receiveMode === "on-start" || form.receiveMode === "manual") {
|
||||
signal.receiveMode = form.receiveMode;
|
||||
} else {
|
||||
delete signal.receiveMode;
|
||||
}
|
||||
|
||||
if (form.ignoreAttachments) signal.ignoreAttachments = true;
|
||||
else delete signal.ignoreAttachments;
|
||||
if (form.ignoreStories) signal.ignoreStories = true;
|
||||
else delete signal.ignoreStories;
|
||||
if (form.sendReadReceipts) signal.sendReadReceipts = true;
|
||||
else delete signal.sendReadReceipts;
|
||||
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
if (allowFrom.length > 0) signal.allowFrom = allowFrom;
|
||||
else delete signal.allowFrom;
|
||||
|
||||
const mediaMaxMb = Number(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
signal.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete signal.mediaMaxMb;
|
||||
}
|
||||
|
||||
if (Object.keys(signal).length > 0) {
|
||||
config.signal = signal;
|
||||
} else {
|
||||
delete config.signal;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
state.signalConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.signalConfigStatus = String(err);
|
||||
} finally {
|
||||
state.signalSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveIMessageConfig(state: ConnectionsState) {
|
||||
if (!state.client || !state.connected) return;
|
||||
if (state.imessageSaving) return;
|
||||
state.imessageSaving = true;
|
||||
state.imessageConfigStatus = null;
|
||||
try {
|
||||
const base = state.configSnapshot?.config ?? {};
|
||||
const config = { ...base } as Record<string, unknown>;
|
||||
const imessage = { ...(config.imessage ?? {}) } as Record<string, unknown>;
|
||||
const form = state.imessageForm;
|
||||
|
||||
if (form.enabled) {
|
||||
delete imessage.enabled;
|
||||
} else {
|
||||
imessage.enabled = false;
|
||||
}
|
||||
|
||||
const cliPath = form.cliPath.trim();
|
||||
if (cliPath) imessage.cliPath = cliPath;
|
||||
else delete imessage.cliPath;
|
||||
|
||||
const dbPath = form.dbPath.trim();
|
||||
if (dbPath) imessage.dbPath = dbPath;
|
||||
else delete imessage.dbPath;
|
||||
|
||||
if (form.service === "auto") {
|
||||
delete imessage.service;
|
||||
} else {
|
||||
imessage.service = form.service;
|
||||
}
|
||||
|
||||
const region = form.region.trim();
|
||||
if (region) imessage.region = region;
|
||||
else delete imessage.region;
|
||||
|
||||
const allowFrom = parseList(form.allowFrom);
|
||||
if (allowFrom.length > 0) imessage.allowFrom = allowFrom;
|
||||
else delete imessage.allowFrom;
|
||||
|
||||
if (form.includeAttachments) imessage.includeAttachments = true;
|
||||
else delete imessage.includeAttachments;
|
||||
|
||||
const mediaMaxMb = Number(form.mediaMaxMb);
|
||||
if (Number.isFinite(mediaMaxMb) && mediaMaxMb > 0) {
|
||||
imessage.mediaMaxMb = mediaMaxMb;
|
||||
} else {
|
||||
delete imessage.mediaMaxMb;
|
||||
}
|
||||
|
||||
if (Object.keys(imessage).length > 0) {
|
||||
config.imessage = imessage;
|
||||
} else {
|
||||
delete config.imessage;
|
||||
}
|
||||
|
||||
const raw = `${JSON.stringify(config, null, 2).trimEnd()}\n`;
|
||||
await state.client.request("config.set", { raw });
|
||||
state.imessageConfigStatus = "Saved. Restart gateway if needed.";
|
||||
} catch (err) {
|
||||
state.imessageConfigStatus = String(err);
|
||||
} finally {
|
||||
state.imessageSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ export type ProvidersStatusSnapshot = {
|
||||
ts: number;
|
||||
whatsapp: WhatsAppStatus;
|
||||
telegram: TelegramStatus;
|
||||
discord?: DiscordStatus | null;
|
||||
signal?: SignalStatus | null;
|
||||
imessage?: IMessageStatus | null;
|
||||
};
|
||||
|
||||
export type WhatsAppSelf = {
|
||||
@@ -62,6 +65,66 @@ export type TelegramStatus = {
|
||||
lastProbeAt?: number | null;
|
||||
};
|
||||
|
||||
export type DiscordBot = {
|
||||
id?: string | null;
|
||||
username?: string | null;
|
||||
};
|
||||
|
||||
export type DiscordProbe = {
|
||||
ok: boolean;
|
||||
status?: number | null;
|
||||
error?: string | null;
|
||||
elapsedMs?: number | null;
|
||||
bot?: DiscordBot | null;
|
||||
};
|
||||
|
||||
export type DiscordStatus = {
|
||||
configured: boolean;
|
||||
tokenSource?: string | null;
|
||||
running: boolean;
|
||||
lastStartAt?: number | null;
|
||||
lastStopAt?: number | null;
|
||||
lastError?: string | null;
|
||||
probe?: DiscordProbe | null;
|
||||
lastProbeAt?: number | null;
|
||||
};
|
||||
|
||||
export type SignalProbe = {
|
||||
ok: boolean;
|
||||
status?: number | null;
|
||||
error?: string | null;
|
||||
elapsedMs?: number | null;
|
||||
version?: string | null;
|
||||
};
|
||||
|
||||
export type SignalStatus = {
|
||||
configured: boolean;
|
||||
baseUrl: string;
|
||||
running: boolean;
|
||||
lastStartAt?: number | null;
|
||||
lastStopAt?: number | null;
|
||||
lastError?: string | null;
|
||||
probe?: SignalProbe | null;
|
||||
lastProbeAt?: number | null;
|
||||
};
|
||||
|
||||
export type IMessageProbe = {
|
||||
ok: boolean;
|
||||
error?: string | null;
|
||||
};
|
||||
|
||||
export type IMessageStatus = {
|
||||
configured: boolean;
|
||||
running: boolean;
|
||||
lastStartAt?: number | null;
|
||||
lastStopAt?: number | null;
|
||||
lastError?: string | null;
|
||||
cliPath?: string | null;
|
||||
dbPath?: string | null;
|
||||
probe?: IMessageProbe | null;
|
||||
lastProbeAt?: number | null;
|
||||
};
|
||||
|
||||
export type ConfigSnapshotIssue = {
|
||||
path: string;
|
||||
message: string;
|
||||
|
||||
@@ -8,6 +8,48 @@ export type TelegramForm = {
|
||||
webhookPath: string;
|
||||
};
|
||||
|
||||
export type DiscordForm = {
|
||||
enabled: boolean;
|
||||
token: string;
|
||||
allowFrom: string;
|
||||
groupEnabled: boolean;
|
||||
groupChannels: string;
|
||||
mediaMaxMb: string;
|
||||
historyLimit: string;
|
||||
enableReactions: boolean;
|
||||
slashEnabled: boolean;
|
||||
slashName: string;
|
||||
slashSessionPrefix: string;
|
||||
slashEphemeral: boolean;
|
||||
};
|
||||
|
||||
export type SignalForm = {
|
||||
enabled: boolean;
|
||||
account: string;
|
||||
httpUrl: string;
|
||||
httpHost: string;
|
||||
httpPort: string;
|
||||
cliPath: string;
|
||||
autoStart: boolean;
|
||||
receiveMode: "on-start" | "manual" | "";
|
||||
ignoreAttachments: boolean;
|
||||
ignoreStories: boolean;
|
||||
sendReadReceipts: boolean;
|
||||
allowFrom: string;
|
||||
mediaMaxMb: string;
|
||||
};
|
||||
|
||||
export type IMessageForm = {
|
||||
enabled: boolean;
|
||||
cliPath: string;
|
||||
dbPath: string;
|
||||
service: "auto" | "imessage" | "sms";
|
||||
region: string;
|
||||
allowFrom: string;
|
||||
includeAttachments: boolean;
|
||||
mediaMaxMb: string;
|
||||
};
|
||||
|
||||
export type CronFormState = {
|
||||
name: string;
|
||||
description: string;
|
||||
@@ -28,4 +70,3 @@ export type CronFormState = {
|
||||
timeoutSeconds: string;
|
||||
postToMainPrefix: string;
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -120,7 +120,7 @@ export function renderOverview(props: OverviewProps) {
|
||||
${props.lastError}
|
||||
</div>`
|
||||
: html`<div class="callout" style="margin-top: 14px;">
|
||||
Use Connections to link WhatsApp and Telegram.
|
||||
Use Connections to link WhatsApp, Telegram, Discord, Signal, or iMessage.
|
||||
</div>`}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user