UI: add discord action toggles
This commit is contained in:
committed by
Peter Steinberger
parent
0c38f2df2a
commit
98a1deb129
@@ -334,6 +334,105 @@ struct ConnectionsSettings: View {
|
||||
}
|
||||
}
|
||||
|
||||
Divider().padding(.vertical, 2)
|
||||
|
||||
Text("Tool actions")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 14, verticalSpacing: 10) {
|
||||
GridRow {
|
||||
self.gridLabel("Reactions")
|
||||
Toggle("", isOn: self.$store.discordActionReactions)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Stickers")
|
||||
Toggle("", isOn: self.$store.discordActionStickers)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Polls")
|
||||
Toggle("", isOn: self.$store.discordActionPolls)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Permissions")
|
||||
Toggle("", isOn: self.$store.discordActionPermissions)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Messages")
|
||||
Toggle("", isOn: self.$store.discordActionMessages)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Threads")
|
||||
Toggle("", isOn: self.$store.discordActionThreads)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Pins")
|
||||
Toggle("", isOn: self.$store.discordActionPins)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Search")
|
||||
Toggle("", isOn: self.$store.discordActionSearch)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Member info")
|
||||
Toggle("", isOn: self.$store.discordActionMemberInfo)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Role info")
|
||||
Toggle("", isOn: self.$store.discordActionRoleInfo)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Channel info")
|
||||
Toggle("", isOn: self.$store.discordActionChannelInfo)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Voice status")
|
||||
Toggle("", isOn: self.$store.discordActionVoiceStatus)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Events")
|
||||
Toggle("", isOn: self.$store.discordActionEvents)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Role changes")
|
||||
Toggle("", isOn: self.$store.discordActionRoles)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
GridRow {
|
||||
self.gridLabel("Moderation")
|
||||
Toggle("", isOn: self.$store.discordActionModeration)
|
||||
.labelsHidden()
|
||||
.toggleStyle(.checkbox)
|
||||
}
|
||||
}
|
||||
|
||||
if self.isDiscordTokenLocked {
|
||||
Text("Token set via DISCORD_BOT_TOKEN env; config edits won’t override it.")
|
||||
.font(.caption)
|
||||
|
||||
@@ -174,6 +174,21 @@ final class ConnectionsStore {
|
||||
var discordGroupChannels: String = ""
|
||||
var discordMediaMaxMb: String = ""
|
||||
var discordHistoryLimit: String = ""
|
||||
var discordActionReactions = true
|
||||
var discordActionStickers = true
|
||||
var discordActionPolls = true
|
||||
var discordActionPermissions = true
|
||||
var discordActionMessages = true
|
||||
var discordActionThreads = true
|
||||
var discordActionPins = true
|
||||
var discordActionSearch = true
|
||||
var discordActionMemberInfo = true
|
||||
var discordActionRoleInfo = true
|
||||
var discordActionChannelInfo = true
|
||||
var discordActionVoiceStatus = true
|
||||
var discordActionEvents = true
|
||||
var discordActionRoles = false
|
||||
var discordActionModeration = false
|
||||
var discordSlashEnabled = false
|
||||
var discordSlashName: String = ""
|
||||
var discordSlashSessionPrefix: String = ""
|
||||
@@ -419,6 +434,22 @@ final class ConnectionsStore {
|
||||
} else {
|
||||
self.discordHistoryLimit = ""
|
||||
}
|
||||
let discordActions = discord?["actions"]?.dictionaryValue
|
||||
self.discordActionReactions = discordActions?["reactions"]?.boolValue ?? true
|
||||
self.discordActionStickers = discordActions?["stickers"]?.boolValue ?? true
|
||||
self.discordActionPolls = discordActions?["polls"]?.boolValue ?? true
|
||||
self.discordActionPermissions = discordActions?["permissions"]?.boolValue ?? true
|
||||
self.discordActionMessages = discordActions?["messages"]?.boolValue ?? true
|
||||
self.discordActionThreads = discordActions?["threads"]?.boolValue ?? true
|
||||
self.discordActionPins = discordActions?["pins"]?.boolValue ?? true
|
||||
self.discordActionSearch = discordActions?["search"]?.boolValue ?? true
|
||||
self.discordActionMemberInfo = discordActions?["memberInfo"]?.boolValue ?? true
|
||||
self.discordActionRoleInfo = discordActions?["roleInfo"]?.boolValue ?? true
|
||||
self.discordActionChannelInfo = discordActions?["channelInfo"]?.boolValue ?? true
|
||||
self.discordActionVoiceStatus = discordActions?["voiceStatus"]?.boolValue ?? true
|
||||
self.discordActionEvents = discordActions?["events"]?.boolValue ?? true
|
||||
self.discordActionRoles = discordActions?["roles"]?.boolValue ?? false
|
||||
self.discordActionModeration = discordActions?["moderation"]?.boolValue ?? false
|
||||
let slash = discord?["slashCommand"]?.dictionaryValue
|
||||
self.discordSlashEnabled = slash?["enabled"]?.boolValue ?? false
|
||||
self.discordSlashName = slash?["name"]?.stringValue ?? ""
|
||||
@@ -642,6 +673,35 @@ final class ConnectionsStore {
|
||||
discord.removeValue(forKey: "historyLimit")
|
||||
}
|
||||
|
||||
var actions: [String: Any] = (discord["actions"] as? [String: Any]) ?? [:]
|
||||
func setAction(_ key: String, value: Bool, defaultValue: Bool) {
|
||||
if value == defaultValue {
|
||||
actions.removeValue(forKey: key)
|
||||
} else {
|
||||
actions[key] = value
|
||||
}
|
||||
}
|
||||
setAction("reactions", value: self.discordActionReactions, defaultValue: true)
|
||||
setAction("stickers", value: self.discordActionStickers, defaultValue: true)
|
||||
setAction("polls", value: self.discordActionPolls, defaultValue: true)
|
||||
setAction("permissions", value: self.discordActionPermissions, defaultValue: true)
|
||||
setAction("messages", value: self.discordActionMessages, defaultValue: true)
|
||||
setAction("threads", value: self.discordActionThreads, defaultValue: true)
|
||||
setAction("pins", value: self.discordActionPins, defaultValue: true)
|
||||
setAction("search", value: self.discordActionSearch, defaultValue: true)
|
||||
setAction("memberInfo", value: self.discordActionMemberInfo, defaultValue: true)
|
||||
setAction("roleInfo", value: self.discordActionRoleInfo, defaultValue: true)
|
||||
setAction("channelInfo", value: self.discordActionChannelInfo, defaultValue: true)
|
||||
setAction("voiceStatus", value: self.discordActionVoiceStatus, defaultValue: true)
|
||||
setAction("events", value: self.discordActionEvents, defaultValue: true)
|
||||
setAction("roles", value: self.discordActionRoles, defaultValue: false)
|
||||
setAction("moderation", value: self.discordActionModeration, defaultValue: false)
|
||||
if actions.isEmpty {
|
||||
discord.removeValue(forKey: "actions")
|
||||
} else {
|
||||
discord["actions"] = actions
|
||||
}
|
||||
|
||||
var slash: [String: Any] = (discord["slashCommand"] as? [String: Any]) ?? [:]
|
||||
if self.discordSlashEnabled {
|
||||
slash["enabled"] = true
|
||||
|
||||
@@ -26,12 +26,13 @@ import type {
|
||||
SkillStatusReport,
|
||||
StatusSummary,
|
||||
} from "./types";
|
||||
import type {
|
||||
CronFormState,
|
||||
DiscordForm,
|
||||
IMessageForm,
|
||||
SignalForm,
|
||||
TelegramForm,
|
||||
import {
|
||||
defaultDiscordActions,
|
||||
type CronFormState,
|
||||
type DiscordForm,
|
||||
type IMessageForm,
|
||||
type SignalForm,
|
||||
type TelegramForm,
|
||||
} from "./ui-types";
|
||||
import { loadChatHistory, sendChat, handleChatEvent } from "./controllers/chat";
|
||||
import { loadNodes } from "./controllers/nodes";
|
||||
@@ -143,6 +144,7 @@ export class ClawdisApp extends LitElement {
|
||||
groupChannels: "",
|
||||
mediaMaxMb: "",
|
||||
historyLimit: "",
|
||||
actions: { ...defaultDiscordActions },
|
||||
slashEnabled: false,
|
||||
slashName: "",
|
||||
slashSessionPrefix: "",
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import type { GatewayBrowserClient } from "../gateway";
|
||||
import type { ConfigSnapshot } from "../types";
|
||||
import type { DiscordForm, IMessageForm, SignalForm, TelegramForm } from "../ui-types";
|
||||
import {
|
||||
defaultDiscordActions,
|
||||
type DiscordActionForm,
|
||||
type DiscordForm,
|
||||
type IMessageForm,
|
||||
type SignalForm,
|
||||
type TelegramForm,
|
||||
} from "../ui-types";
|
||||
|
||||
export type ConfigState = {
|
||||
client: GatewayBrowserClient | null;
|
||||
@@ -88,6 +95,11 @@ export function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot
|
||||
|
||||
const discordDm = (discord.dm ?? {}) as Record<string, unknown>;
|
||||
const slash = (discord.slashCommand ?? {}) as Record<string, unknown>;
|
||||
const discordActions = (discord.actions ?? {}) as Record<string, unknown>;
|
||||
const readAction = (key: keyof DiscordActionForm) =>
|
||||
typeof discordActions[key] === "boolean"
|
||||
? (discordActions[key] as boolean)
|
||||
: defaultDiscordActions[key];
|
||||
state.discordForm = {
|
||||
enabled: typeof discord.enabled === "boolean" ? discord.enabled : true,
|
||||
token: typeof discord.token === "string" ? discord.token : "",
|
||||
@@ -99,6 +111,23 @@ export function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot
|
||||
typeof discord.mediaMaxMb === "number" ? String(discord.mediaMaxMb) : "",
|
||||
historyLimit:
|
||||
typeof discord.historyLimit === "number" ? String(discord.historyLimit) : "",
|
||||
actions: {
|
||||
reactions: readAction("reactions"),
|
||||
stickers: readAction("stickers"),
|
||||
polls: readAction("polls"),
|
||||
permissions: readAction("permissions"),
|
||||
messages: readAction("messages"),
|
||||
threads: readAction("threads"),
|
||||
pins: readAction("pins"),
|
||||
search: readAction("search"),
|
||||
memberInfo: readAction("memberInfo"),
|
||||
roleInfo: readAction("roleInfo"),
|
||||
channelInfo: readAction("channelInfo"),
|
||||
voiceStatus: readAction("voiceStatus"),
|
||||
events: readAction("events"),
|
||||
roles: readAction("roles"),
|
||||
moderation: readAction("moderation"),
|
||||
},
|
||||
slashEnabled: typeof slash.enabled === "boolean" ? slash.enabled : false,
|
||||
slashName: typeof slash.name === "string" ? slash.name : "",
|
||||
slashSessionPrefix:
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import type { GatewayBrowserClient } from "../gateway";
|
||||
import { parseList } from "../format";
|
||||
import type { ConfigSnapshot, ProvidersStatusSnapshot } from "../types";
|
||||
import type { DiscordForm, IMessageForm, SignalForm, TelegramForm } from "../ui-types";
|
||||
import {
|
||||
defaultDiscordActions,
|
||||
type DiscordActionForm,
|
||||
type DiscordForm,
|
||||
type IMessageForm,
|
||||
type SignalForm,
|
||||
type TelegramForm,
|
||||
} from "../ui-types";
|
||||
|
||||
export type ConnectionsState = {
|
||||
client: GatewayBrowserClient | null;
|
||||
@@ -116,6 +123,14 @@ export function updateDiscordForm(
|
||||
state: ConnectionsState,
|
||||
patch: Partial<DiscordForm>,
|
||||
) {
|
||||
if (patch.actions) {
|
||||
state.discordForm = {
|
||||
...state.discordForm,
|
||||
...patch,
|
||||
actions: { ...state.discordForm.actions, ...patch.actions },
|
||||
};
|
||||
return;
|
||||
}
|
||||
state.discordForm = { ...state.discordForm, ...patch };
|
||||
}
|
||||
|
||||
@@ -246,6 +261,32 @@ export async function saveDiscordConfig(state: ConnectionsState) {
|
||||
}
|
||||
}
|
||||
|
||||
const actions: Partial<DiscordActionForm> = {};
|
||||
const applyAction = (key: keyof DiscordActionForm) => {
|
||||
const value = form.actions[key];
|
||||
if (value !== defaultDiscordActions[key]) actions[key] = value;
|
||||
};
|
||||
applyAction("reactions");
|
||||
applyAction("stickers");
|
||||
applyAction("polls");
|
||||
applyAction("permissions");
|
||||
applyAction("messages");
|
||||
applyAction("threads");
|
||||
applyAction("pins");
|
||||
applyAction("search");
|
||||
applyAction("memberInfo");
|
||||
applyAction("roleInfo");
|
||||
applyAction("channelInfo");
|
||||
applyAction("voiceStatus");
|
||||
applyAction("events");
|
||||
applyAction("roles");
|
||||
applyAction("moderation");
|
||||
if (Object.keys(actions).length > 0) {
|
||||
discord.actions = actions;
|
||||
} else {
|
||||
delete discord.actions;
|
||||
}
|
||||
|
||||
const slash = { ...(discord.slashCommand ?? {}) } as Record<string, unknown>;
|
||||
if (form.slashEnabled) {
|
||||
slash.enabled = true;
|
||||
|
||||
@@ -16,12 +16,49 @@ export type DiscordForm = {
|
||||
groupChannels: string;
|
||||
mediaMaxMb: string;
|
||||
historyLimit: string;
|
||||
actions: DiscordActionForm;
|
||||
slashEnabled: boolean;
|
||||
slashName: string;
|
||||
slashSessionPrefix: string;
|
||||
slashEphemeral: boolean;
|
||||
};
|
||||
|
||||
export type DiscordActionForm = {
|
||||
reactions: boolean;
|
||||
stickers: boolean;
|
||||
polls: boolean;
|
||||
permissions: boolean;
|
||||
messages: boolean;
|
||||
threads: boolean;
|
||||
pins: boolean;
|
||||
search: boolean;
|
||||
memberInfo: boolean;
|
||||
roleInfo: boolean;
|
||||
channelInfo: boolean;
|
||||
voiceStatus: boolean;
|
||||
events: boolean;
|
||||
roles: boolean;
|
||||
moderation: boolean;
|
||||
};
|
||||
|
||||
export const defaultDiscordActions: DiscordActionForm = {
|
||||
reactions: true,
|
||||
stickers: true,
|
||||
polls: true,
|
||||
permissions: true,
|
||||
messages: true,
|
||||
threads: true,
|
||||
pins: true,
|
||||
search: true,
|
||||
memberInfo: true,
|
||||
roleInfo: true,
|
||||
channelInfo: true,
|
||||
voiceStatus: true,
|
||||
events: true,
|
||||
roles: false,
|
||||
moderation: false,
|
||||
};
|
||||
|
||||
export type SignalForm = {
|
||||
enabled: boolean;
|
||||
account: string;
|
||||
|
||||
@@ -2,7 +2,13 @@ import { html, nothing } from "lit";
|
||||
|
||||
import { formatAgo } from "../format";
|
||||
import type { ProvidersStatusSnapshot } from "../types";
|
||||
import type { DiscordForm, IMessageForm, SignalForm, TelegramForm } from "../ui-types";
|
||||
import type {
|
||||
DiscordActionForm,
|
||||
DiscordForm,
|
||||
IMessageForm,
|
||||
SignalForm,
|
||||
TelegramForm,
|
||||
} from "../ui-types";
|
||||
|
||||
export type ConnectionsProps = {
|
||||
connected: boolean;
|
||||
@@ -48,6 +54,24 @@ export function renderConnections(props: ConnectionsProps) {
|
||||
const discord = props.snapshot?.discord ?? null;
|
||||
const signal = props.snapshot?.signal ?? null;
|
||||
const imessage = props.snapshot?.imessage ?? null;
|
||||
const discordActionOptions: Array<{ key: keyof DiscordActionForm; label: string }> =
|
||||
[
|
||||
{ key: "reactions", label: "Reactions" },
|
||||
{ key: "stickers", label: "Stickers" },
|
||||
{ key: "polls", label: "Polls" },
|
||||
{ key: "permissions", label: "Permissions" },
|
||||
{ key: "messages", label: "Messages" },
|
||||
{ key: "threads", label: "Threads" },
|
||||
{ key: "pins", label: "Pins" },
|
||||
{ key: "search", label: "Search" },
|
||||
{ key: "memberInfo", label: "Member info" },
|
||||
{ key: "roleInfo", label: "Role info" },
|
||||
{ key: "channelInfo", label: "Channel info" },
|
||||
{ key: "voiceStatus", label: "Voice status" },
|
||||
{ key: "events", label: "Events" },
|
||||
{ key: "roles", label: "Role changes" },
|
||||
{ key: "moderation", label: "Moderation" },
|
||||
];
|
||||
const providerOrder: ProviderKey[] = [
|
||||
"whatsapp",
|
||||
"telegram",
|
||||
@@ -572,6 +596,28 @@ function renderProvider(
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="card-sub" style="margin-top: 16px;">Tool actions</div>
|
||||
<div class="form-grid" style="margin-top: 8px;">
|
||||
${discordActionOptions.map(
|
||||
(action) => html`<label class="field">
|
||||
<span>${action.label}</span>
|
||||
<select
|
||||
.value=${props.discordForm.actions[action.key] ? "yes" : "no"}
|
||||
@change=${(e: Event) =>
|
||||
props.onDiscordChange({
|
||||
actions: {
|
||||
...props.discordForm.actions,
|
||||
[action.key]: (e.target as HTMLSelectElement).value === "yes",
|
||||
},
|
||||
})}
|
||||
>
|
||||
<option value="yes">Enabled</option>
|
||||
<option value="no">Disabled</option>
|
||||
</select>
|
||||
</label>`,
|
||||
)}
|
||||
</div>
|
||||
|
||||
${props.discordTokenLocked
|
||||
? html`<div class="callout" style="margin-top: 12px;">
|
||||
DISCORD_BOT_TOKEN is set in the environment. Config edits will not override it.
|
||||
|
||||
Reference in New Issue
Block a user