125 lines
3.6 KiB
TypeScript
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);
|
|
}
|