Files
Veskslop-t/src/shared/puppetProtocol.ts
T

125 lines
3.6 KiB
TypeScript

/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2026 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/*
* Vesktop ⇄ Veskslop daemon protocol.
*
* This is the only surface the external daemon can reach. Every action is
* validated by the core before anything touches Discord, and every failure is
* returned as a structured error instead of throwing.
*/
export const enum PuppetAction {
Ping = "ping",
EventsSubscribe = "events.subscribe",
StateGet = "state.get",
VoiceGet = "voice.get",
VoiceOverview = "voice.overview",
VoiceJoin = "voice.join",
VoiceLeave = "voice.leave",
VoiceMove = "voice.move",
VoiceUsers = "voice.users",
VoiceSpeaking = "voice.speaking",
UserStatus = "user.status",
UserVolume = "user.volume",
UserSetVolume = "user.setVolume",
VoiceDevices = "voice.devices",
VoiceSetDevices = "voice.setDevices",
UserSearch = "user.search",
DmList = "dm.list",
MessageSend = "message.send",
MessageRead = "message.read",
MessageFetch = "message.fetch",
ReactionAdd = "reaction.add",
DmSend = "dm.send"
}
export const PuppetActions: readonly PuppetAction[] = [
PuppetAction.Ping,
PuppetAction.EventsSubscribe,
PuppetAction.StateGet,
PuppetAction.VoiceGet,
PuppetAction.VoiceOverview,
PuppetAction.VoiceJoin,
PuppetAction.VoiceLeave,
PuppetAction.VoiceMove,
PuppetAction.VoiceUsers,
PuppetAction.VoiceSpeaking,
PuppetAction.UserStatus,
PuppetAction.UserVolume,
PuppetAction.UserSetVolume,
PuppetAction.VoiceDevices,
PuppetAction.VoiceSetDevices,
PuppetAction.UserSearch,
PuppetAction.DmList,
PuppetAction.MessageSend,
PuppetAction.MessageRead,
PuppetAction.MessageFetch,
PuppetAction.ReactionAdd,
PuppetAction.DmSend
];
export const enum PuppetErrorCode {
InvalidRequest = "INVALID_REQUEST",
PayloadTooLarge = "PAYLOAD_TOO_LARGE",
UnknownAction = "UNKNOWN_ACTION",
CoreUnavailable = "CORE_UNAVAILABLE",
NotLoggedIn = "NOT_LOGGED_IN",
InvalidSnowflake = "INVALID_SNOWFLAKE",
InvalidParam = "INVALID_PARAM",
ContentEmpty = "CONTENT_EMPTY",
ContentTooLong = "CONTENT_TOO_LONG",
ChannelNotFound = "CHANNEL_NOT_FOUND",
NotSendable = "NOT_SENDABLE",
SendFailed = "SEND_FAILED",
ReactionFailed = "REACTION_FAILED",
VoiceFailed = "VOICE_FAILED",
DmUnavailable = "DM_UNAVAILABLE",
NotSupported = "NOT_SUPPORTED",
RateLimited = "RATE_LIMITED",
Internal = "INTERNAL"
}
export interface PuppetError {
code: PuppetErrorCode | string;
message: string;
}
export interface PuppetRequest {
id?: string | number;
action: string;
params?: Record<string, unknown>;
}
export type PuppetResult = { ok: true; result?: unknown } | { ok: false; error: PuppetError };
export type PuppetResponse =
{ id?: string | number; ok: true; result?: unknown } | { id?: string | number; ok: false; error: PuppetError };
export interface PuppetEvent {
type: string;
[key: string]: unknown;
}
/** Hard safety limits applied core-side, regardless of what the daemon asks for. */
export const PUPPET_LIMITS = {
maxContentLength: 2000,
maxReadLimit: 100,
maxFetchLimit: 100,
maxUserSearchResults: 25,
maxDmList: 50,
minActionIntervalMs: 500,
maxActionsPerMinute: 30,
maxRequestBytes: 64 * 1024,
maxChannelsPerGuild: 1000
};
const SNOWFLAKE_RE = /^\d{17,20}$/;
export function isSnowflake(value: unknown): value is string {
return typeof value === "string" && SNOWFLAKE_RE.test(value);
}