Files
cours/.obsidian/plugins/themed-discord-rpc/main.js

1567 lines
51 KiB
JavaScript

/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// (disabled):register-scheme
var require_register_scheme = __commonJS({
"(disabled):register-scheme"() {
}
});
// node_modules/discord-rpc/src/util.js
var require_util = __commonJS({
"node_modules/discord-rpc/src/util.js"(exports, module2) {
"use strict";
var register;
try {
const { app } = require("electron");
register = app.setAsDefaultProtocolClient.bind(app);
} catch (err) {
try {
register = require_register_scheme();
} catch (e) {
}
}
if (typeof register !== "function") {
register = () => false;
}
function pid() {
if (typeof process !== "undefined") {
return process.pid;
}
return null;
}
var uuid4122 = () => {
let uuid = "";
for (let i = 0; i < 32; i += 1) {
if (i === 8 || i === 12 || i === 16 || i === 20) {
uuid += "-";
}
let n;
if (i === 12) {
n = 4;
} else {
const random = Math.random() * 16 | 0;
if (i === 16) {
n = random & 3 | 0;
} else {
n = random;
}
}
uuid += n.toString(16);
}
return uuid;
};
module2.exports = {
pid,
register,
uuid: uuid4122
};
}
});
// node_modules/node-fetch/browser.js
var require_browser = __commonJS({
"node_modules/node-fetch/browser.js"(exports, module2) {
"use strict";
var getGlobal = function() {
if (typeof self !== "undefined") {
return self;
}
if (typeof window !== "undefined") {
return window;
}
if (typeof global !== "undefined") {
return global;
}
throw new Error("unable to locate global object");
};
var globalObject = getGlobal();
module2.exports = exports = globalObject.fetch;
if (globalObject.fetch) {
exports.default = globalObject.fetch.bind(globalObject);
}
exports.Headers = globalObject.Headers;
exports.Request = globalObject.Request;
exports.Response = globalObject.Response;
}
});
// node_modules/discord-rpc/src/transports/ipc.js
var require_ipc = __commonJS({
"node_modules/discord-rpc/src/transports/ipc.js"(exports, module2) {
"use strict";
var net = require("net");
var EventEmitter = require("events");
var fetch = require_browser();
var { uuid } = require_util();
var OPCodes = {
HANDSHAKE: 0,
FRAME: 1,
CLOSE: 2,
PING: 3,
PONG: 4
};
function getIPCPath(id) {
if (process.platform === "win32") {
return `\\\\?\\pipe\\discord-ipc-${id}`;
}
const { env: { XDG_RUNTIME_DIR, TMPDIR, TMP, TEMP } } = process;
const prefix = XDG_RUNTIME_DIR || TMPDIR || TMP || TEMP || "/tmp";
return `${prefix.replace(/\/$/, "")}/discord-ipc-${id}`;
}
function getIPC(id = 0) {
return new Promise((resolve, reject) => {
const path = getIPCPath(id);
const onerror = () => {
if (id < 10) {
resolve(getIPC(id + 1));
} else {
reject(new Error("Could not connect"));
}
};
const sock = net.createConnection(path, () => {
sock.removeListener("error", onerror);
resolve(sock);
});
sock.once("error", onerror);
});
}
async function findEndpoint(tries = 0) {
if (tries > 30) {
throw new Error("Could not find endpoint");
}
const endpoint = `http://127.0.0.1:${6463 + tries % 10}`;
try {
const r = await fetch(endpoint);
if (r.status === 404) {
return endpoint;
}
return findEndpoint(tries + 1);
} catch (e) {
return findEndpoint(tries + 1);
}
}
function encode(op, data) {
data = JSON.stringify(data);
const len = Buffer.byteLength(data);
const packet = Buffer.alloc(8 + len);
packet.writeInt32LE(op, 0);
packet.writeInt32LE(len, 4);
packet.write(data, 8, len);
return packet;
}
var working = {
full: "",
op: void 0
};
function decode(socket, callback) {
const packet = socket.read();
if (!packet) {
return;
}
let { op } = working;
let raw;
if (working.full === "") {
op = working.op = packet.readInt32LE(0);
const len = packet.readInt32LE(4);
raw = packet.slice(8, len + 8);
} else {
raw = packet.toString();
}
try {
const data = JSON.parse(working.full + raw);
callback({ op, data });
working.full = "";
working.op = void 0;
} catch (err) {
working.full += raw;
}
decode(socket, callback);
}
var IPCTransport = class extends EventEmitter {
constructor(client) {
super();
this.client = client;
this.socket = null;
}
async connect() {
const socket = this.socket = await getIPC();
socket.on("close", this.onClose.bind(this));
socket.on("error", this.onClose.bind(this));
this.emit("open");
socket.write(encode(OPCodes.HANDSHAKE, {
v: 1,
client_id: this.client.clientId
}));
socket.pause();
socket.on("readable", () => {
decode(socket, ({ op, data }) => {
switch (op) {
case OPCodes.PING:
this.send(data, OPCodes.PONG);
break;
case OPCodes.FRAME:
if (!data) {
return;
}
if (data.cmd === "AUTHORIZE" && data.evt !== "ERROR") {
findEndpoint().then((endpoint) => {
this.client.request.endpoint = endpoint;
}).catch((e) => {
this.client.emit("error", e);
});
}
this.emit("message", data);
break;
case OPCodes.CLOSE:
this.emit("close", data);
break;
default:
break;
}
});
});
}
onClose(e) {
this.emit("close", e);
}
send(data, op = OPCodes.FRAME) {
this.socket.write(encode(op, data));
}
async close() {
return new Promise((r) => {
this.once("close", r);
this.send({}, OPCodes.CLOSE);
this.socket.end();
});
}
ping() {
this.send(uuid(), OPCodes.PING);
}
};
module2.exports = IPCTransport;
module2.exports.encode = encode;
module2.exports.decode = decode;
}
});
// node_modules/discord-rpc/src/constants.js
var require_constants = __commonJS({
"node_modules/discord-rpc/src/constants.js"(exports) {
"use strict";
function keyMirror(arr) {
const tmp = {};
for (const value of arr) {
tmp[value] = value;
}
return tmp;
}
exports.browser = typeof window !== "undefined";
exports.RPCCommands = keyMirror([
"DISPATCH",
"AUTHORIZE",
"AUTHENTICATE",
"GET_GUILD",
"GET_GUILDS",
"GET_CHANNEL",
"GET_CHANNELS",
"CREATE_CHANNEL_INVITE",
"GET_RELATIONSHIPS",
"GET_USER",
"SUBSCRIBE",
"UNSUBSCRIBE",
"SET_USER_VOICE_SETTINGS",
"SET_USER_VOICE_SETTINGS_2",
"SELECT_VOICE_CHANNEL",
"GET_SELECTED_VOICE_CHANNEL",
"SELECT_TEXT_CHANNEL",
"GET_VOICE_SETTINGS",
"SET_VOICE_SETTINGS_2",
"SET_VOICE_SETTINGS",
"CAPTURE_SHORTCUT",
"SET_ACTIVITY",
"SEND_ACTIVITY_JOIN_INVITE",
"CLOSE_ACTIVITY_JOIN_REQUEST",
"ACTIVITY_INVITE_USER",
"ACCEPT_ACTIVITY_INVITE",
"INVITE_BROWSER",
"DEEP_LINK",
"CONNECTIONS_CALLBACK",
"BRAINTREE_POPUP_BRIDGE_CALLBACK",
"GIFT_CODE_BROWSER",
"GUILD_TEMPLATE_BROWSER",
"OVERLAY",
"BROWSER_HANDOFF",
"SET_CERTIFIED_DEVICES",
"GET_IMAGE",
"CREATE_LOBBY",
"UPDATE_LOBBY",
"DELETE_LOBBY",
"UPDATE_LOBBY_MEMBER",
"CONNECT_TO_LOBBY",
"DISCONNECT_FROM_LOBBY",
"SEND_TO_LOBBY",
"SEARCH_LOBBIES",
"CONNECT_TO_LOBBY_VOICE",
"DISCONNECT_FROM_LOBBY_VOICE",
"SET_OVERLAY_LOCKED",
"OPEN_OVERLAY_ACTIVITY_INVITE",
"OPEN_OVERLAY_GUILD_INVITE",
"OPEN_OVERLAY_VOICE_SETTINGS",
"VALIDATE_APPLICATION",
"GET_ENTITLEMENT_TICKET",
"GET_APPLICATION_TICKET",
"START_PURCHASE",
"GET_SKUS",
"GET_ENTITLEMENTS",
"GET_NETWORKING_CONFIG",
"NETWORKING_SYSTEM_METRICS",
"NETWORKING_PEER_METRICS",
"NETWORKING_CREATE_TOKEN",
"SET_USER_ACHIEVEMENT",
"GET_USER_ACHIEVEMENTS"
]);
exports.RPCEvents = keyMirror([
"CURRENT_USER_UPDATE",
"GUILD_STATUS",
"GUILD_CREATE",
"CHANNEL_CREATE",
"RELATIONSHIP_UPDATE",
"VOICE_CHANNEL_SELECT",
"VOICE_STATE_CREATE",
"VOICE_STATE_DELETE",
"VOICE_STATE_UPDATE",
"VOICE_SETTINGS_UPDATE",
"VOICE_SETTINGS_UPDATE_2",
"VOICE_CONNECTION_STATUS",
"SPEAKING_START",
"SPEAKING_STOP",
"GAME_JOIN",
"GAME_SPECTATE",
"ACTIVITY_JOIN",
"ACTIVITY_JOIN_REQUEST",
"ACTIVITY_SPECTATE",
"ACTIVITY_INVITE",
"NOTIFICATION_CREATE",
"MESSAGE_CREATE",
"MESSAGE_UPDATE",
"MESSAGE_DELETE",
"LOBBY_DELETE",
"LOBBY_UPDATE",
"LOBBY_MEMBER_CONNECT",
"LOBBY_MEMBER_DISCONNECT",
"LOBBY_MEMBER_UPDATE",
"LOBBY_MESSAGE",
"CAPTURE_SHORTCUT_CHANGE",
"OVERLAY",
"OVERLAY_UPDATE",
"ENTITLEMENT_CREATE",
"ENTITLEMENT_DELETE",
"USER_ACHIEVEMENT_UPDATE",
"READY",
"ERROR"
]);
exports.RPCErrors = {
CAPTURE_SHORTCUT_ALREADY_LISTENING: 5004,
GET_GUILD_TIMED_OUT: 5002,
INVALID_ACTIVITY_JOIN_REQUEST: 4012,
INVALID_ACTIVITY_SECRET: 5005,
INVALID_CHANNEL: 4005,
INVALID_CLIENTID: 4007,
INVALID_COMMAND: 4002,
INVALID_ENTITLEMENT: 4015,
INVALID_EVENT: 4004,
INVALID_GIFT_CODE: 4016,
INVALID_GUILD: 4003,
INVALID_INVITE: 4011,
INVALID_LOBBY: 4013,
INVALID_LOBBY_SECRET: 4014,
INVALID_ORIGIN: 4008,
INVALID_PAYLOAD: 4e3,
INVALID_PERMISSIONS: 4006,
INVALID_TOKEN: 4009,
INVALID_USER: 4010,
LOBBY_FULL: 5007,
NO_ELIGIBLE_ACTIVITY: 5006,
OAUTH2_ERROR: 5e3,
PURCHASE_CANCELED: 5008,
PURCHASE_ERROR: 5009,
RATE_LIMITED: 5011,
SELECT_CHANNEL_TIMED_OUT: 5001,
SELECT_VOICE_FORCE_REQUIRED: 5003,
SERVICE_UNAVAILABLE: 1001,
TRANSACTION_ABORTED: 1002,
UNAUTHORIZED_FOR_ACHIEVEMENT: 5010,
UNKNOWN_ERROR: 1e3
};
exports.RPCCloseCodes = {
CLOSE_NORMAL: 1e3,
CLOSE_UNSUPPORTED: 1003,
CLOSE_ABNORMAL: 1006,
INVALID_CLIENTID: 4e3,
INVALID_ORIGIN: 4001,
RATELIMITED: 4002,
TOKEN_REVOKED: 4003,
INVALID_VERSION: 4004,
INVALID_ENCODING: 4005
};
exports.LobbyTypes = {
PRIVATE: 1,
PUBLIC: 2
};
exports.RelationshipTypes = {
NONE: 0,
FRIEND: 1,
BLOCKED: 2,
PENDING_INCOMING: 3,
PENDING_OUTGOING: 4,
IMPLICIT: 5
};
}
});
// (disabled):node_modules/ws/browser.js
var require_browser2 = __commonJS({
"(disabled):node_modules/ws/browser.js"() {
}
});
// node_modules/discord-rpc/src/transports/websocket.js
var require_websocket = __commonJS({
"node_modules/discord-rpc/src/transports/websocket.js"(exports, module2) {
"use strict";
var EventEmitter = require("events");
var { browser } = require_constants();
var WebSocket = browser ? window.WebSocket : require_browser2();
var pack = (d) => JSON.stringify(d);
var unpack = (s) => JSON.parse(s);
var WebSocketTransport = class extends EventEmitter {
constructor(client) {
super();
this.client = client;
this.ws = null;
this.tries = 0;
}
async connect() {
const port = 6463 + this.tries % 10;
this.tries += 1;
this.ws = new WebSocket(
`ws://127.0.0.1:${port}/?v=1&client_id=${this.client.clientId}`,
browser ? void 0 : { origin: this.client.options.origin }
);
this.ws.onopen = this.onOpen.bind(this);
this.ws.onclose = this.onClose.bind(this);
this.ws.onerror = this.onError.bind(this);
this.ws.onmessage = this.onMessage.bind(this);
}
onOpen() {
this.emit("open");
}
onClose(event) {
if (!event.wasClean) {
return;
}
this.emit("close", event);
}
onError(event) {
try {
this.ws.close();
} catch (e) {
}
if (this.tries > 20) {
this.emit("error", event.error);
} else {
setTimeout(() => {
this.connect();
}, 250);
}
}
onMessage(event) {
this.emit("message", unpack(event.data));
}
send(data) {
this.ws.send(pack(data));
}
ping() {
}
// eslint-disable-line no-empty-function
close() {
return new Promise((r) => {
this.once("close", r);
this.ws.close();
});
}
};
module2.exports = WebSocketTransport;
}
});
// node_modules/discord-rpc/src/transports/index.js
var require_transports = __commonJS({
"node_modules/discord-rpc/src/transports/index.js"(exports, module2) {
"use strict";
module2.exports = {
ipc: require_ipc(),
websocket: require_websocket()
};
}
});
// node_modules/discord-rpc/src/client.js
var require_client = __commonJS({
"node_modules/discord-rpc/src/client.js"(exports, module2) {
"use strict";
var EventEmitter = require("events");
var { setTimeout: setTimeout2, clearTimeout } = require("timers");
var fetch = require_browser();
var transports = require_transports();
var { RPCCommands, RPCEvents, RelationshipTypes } = require_constants();
var { pid: getPid, uuid } = require_util();
function subKey(event, args) {
return `${event}${JSON.stringify(args)}`;
}
var RPCClient = class extends EventEmitter {
/**
* @param {RPCClientOptions} [options] Options for the client.
* You must provide a transport
*/
constructor(options = {}) {
super();
this.options = options;
this.accessToken = null;
this.clientId = null;
this.application = null;
this.user = null;
const Transport = transports[options.transport];
if (!Transport) {
throw new TypeError("RPC_INVALID_TRANSPORT", options.transport);
}
this.fetch = (method, path, { data, query } = {}) => fetch(`${this.fetch.endpoint}${path}${query ? new URLSearchParams(query) : ""}`, {
method,
body: data,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
}).then(async (r) => {
const body = await r.json();
if (!r.ok) {
const e = new Error(r.status);
e.body = body;
throw e;
}
return body;
});
this.fetch.endpoint = "https://discord.com/api";
this.transport = new Transport(this);
this.transport.on("message", this._onRpcMessage.bind(this));
this._expecting = /* @__PURE__ */ new Map();
this._connectPromise = void 0;
}
/**
* Search and connect to RPC
*/
connect(clientId) {
if (this._connectPromise) {
return this._connectPromise;
}
this._connectPromise = new Promise((resolve, reject) => {
this.clientId = clientId;
const timeout = setTimeout2(() => reject(new Error("RPC_CONNECTION_TIMEOUT")), 1e4);
timeout.unref();
this.once("connected", () => {
clearTimeout(timeout);
resolve(this);
});
this.transport.once("close", () => {
this._expecting.forEach((e) => {
e.reject(new Error("connection closed"));
});
this.emit("disconnected");
reject(new Error("connection closed"));
});
this.transport.connect().catch(reject);
});
return this._connectPromise;
}
/**
* @typedef {RPCLoginOptions}
* @param {string} clientId Client ID
* @param {string} [clientSecret] Client secret
* @param {string} [accessToken] Access token
* @param {string} [rpcToken] RPC token
* @param {string} [tokenEndpoint] Token endpoint
* @param {string[]} [scopes] Scopes to authorize with
*/
/**
* Performs authentication flow. Automatically calls Client#connect if needed.
* @param {RPCLoginOptions} options Options for authentication.
* At least one property must be provided to perform login.
* @example client.login({ clientId: '1234567', clientSecret: 'abcdef123' });
* @returns {Promise<RPCClient>}
*/
async login(options = {}) {
let { clientId, accessToken } = options;
await this.connect(clientId);
if (!options.scopes) {
this.emit("ready");
return this;
}
if (!accessToken) {
accessToken = await this.authorize(options);
}
return this.authenticate(accessToken);
}
/**
* Request
* @param {string} cmd Command
* @param {Object} [args={}] Arguments
* @param {string} [evt] Event
* @returns {Promise}
* @private
*/
request(cmd, args, evt) {
return new Promise((resolve, reject) => {
const nonce = uuid();
this.transport.send({ cmd, args, evt, nonce });
this._expecting.set(nonce, { resolve, reject });
});
}
/**
* Message handler
* @param {Object} message message
* @private
*/
_onRpcMessage(message) {
if (message.cmd === RPCCommands.DISPATCH && message.evt === RPCEvents.READY) {
if (message.data.user) {
this.user = message.data.user;
}
this.emit("connected");
} else if (this._expecting.has(message.nonce)) {
const { resolve, reject } = this._expecting.get(message.nonce);
if (message.evt === "ERROR") {
const e = new Error(message.data.message);
e.code = message.data.code;
e.data = message.data;
reject(e);
} else {
resolve(message.data);
}
this._expecting.delete(message.nonce);
} else {
this.emit(message.evt, message.data);
}
}
/**
* Authorize
* @param {Object} options options
* @returns {Promise}
* @private
*/
async authorize({ scopes, clientSecret, rpcToken, redirectUri, prompt } = {}) {
if (clientSecret && rpcToken === true) {
const body = await this.fetch("POST", "/oauth2/token/rpc", {
data: new URLSearchParams({
client_id: this.clientId,
client_secret: clientSecret
})
});
rpcToken = body.rpc_token;
}
const { code } = await this.request("AUTHORIZE", {
scopes,
client_id: this.clientId,
prompt,
rpc_token: rpcToken
});
const response = await this.fetch("POST", "/oauth2/token", {
data: new URLSearchParams({
client_id: this.clientId,
client_secret: clientSecret,
code,
grant_type: "authorization_code",
redirect_uri: redirectUri
})
});
return response.access_token;
}
/**
* Authenticate
* @param {string} accessToken access token
* @returns {Promise}
* @private
*/
authenticate(accessToken) {
return this.request("AUTHENTICATE", { access_token: accessToken }).then(({ application, user }) => {
this.accessToken = accessToken;
this.application = application;
this.user = user;
this.emit("ready");
return this;
});
}
/**
* Fetch a guild
* @param {Snowflake} id Guild ID
* @param {number} [timeout] Timeout request
* @returns {Promise<Guild>}
*/
getGuild(id, timeout) {
return this.request(RPCCommands.GET_GUILD, { guild_id: id, timeout });
}
/**
* Fetch all guilds
* @param {number} [timeout] Timeout request
* @returns {Promise<Collection<Snowflake, Guild>>}
*/
getGuilds(timeout) {
return this.request(RPCCommands.GET_GUILDS, { timeout });
}
/**
* Get a channel
* @param {Snowflake} id Channel ID
* @param {number} [timeout] Timeout request
* @returns {Promise<Channel>}
*/
getChannel(id, timeout) {
return this.request(RPCCommands.GET_CHANNEL, { channel_id: id, timeout });
}
/**
* Get all channels
* @param {Snowflake} [id] Guild ID
* @param {number} [timeout] Timeout request
* @returns {Promise<Collection<Snowflake, Channel>>}
*/
async getChannels(id, timeout) {
const { channels } = await this.request(RPCCommands.GET_CHANNELS, {
timeout,
guild_id: id
});
return channels;
}
/**
* @typedef {CertifiedDevice}
* @prop {string} type One of `AUDIO_INPUT`, `AUDIO_OUTPUT`, `VIDEO_INPUT`
* @prop {string} uuid This device's Windows UUID
* @prop {object} vendor Vendor information
* @prop {string} vendor.name Vendor's name
* @prop {string} vendor.url Vendor's url
* @prop {object} model Model information
* @prop {string} model.name Model's name
* @prop {string} model.url Model's url
* @prop {string[]} related Array of related product's Windows UUIDs
* @prop {boolean} echoCancellation If the device has echo cancellation
* @prop {boolean} noiseSuppression If the device has noise suppression
* @prop {boolean} automaticGainControl If the device has automatic gain control
* @prop {boolean} hardwareMute If the device has a hardware mute
*/
/**
* Tell discord which devices are certified
* @param {CertifiedDevice[]} devices Certified devices to send to discord
* @returns {Promise}
*/
setCertifiedDevices(devices) {
return this.request(RPCCommands.SET_CERTIFIED_DEVICES, {
devices: devices.map((d) => ({
type: d.type,
id: d.uuid,
vendor: d.vendor,
model: d.model,
related: d.related,
echo_cancellation: d.echoCancellation,
noise_suppression: d.noiseSuppression,
automatic_gain_control: d.automaticGainControl,
hardware_mute: d.hardwareMute
}))
});
}
/**
* @typedef {UserVoiceSettings}
* @prop {Snowflake} id ID of the user these settings apply to
* @prop {?Object} [pan] Pan settings, an object with `left` and `right` set between
* 0.0 and 1.0, inclusive
* @prop {?number} [volume=100] The volume
* @prop {bool} [mute] If the user is muted
*/
/**
* Set the voice settings for a user, by id
* @param {Snowflake} id ID of the user to set
* @param {UserVoiceSettings} settings Settings
* @returns {Promise}
*/
setUserVoiceSettings(id, settings) {
return this.request(RPCCommands.SET_USER_VOICE_SETTINGS, {
user_id: id,
pan: settings.pan,
mute: settings.mute,
volume: settings.volume
});
}
/**
* Move the user to a voice channel
* @param {Snowflake} id ID of the voice channel
* @param {Object} [options] Options
* @param {number} [options.timeout] Timeout for the command
* @param {boolean} [options.force] Force this move. This should only be done if you
* have explicit permission from the user.
* @returns {Promise}
*/
selectVoiceChannel(id, { timeout, force = false } = {}) {
return this.request(RPCCommands.SELECT_VOICE_CHANNEL, { channel_id: id, timeout, force });
}
/**
* Move the user to a text channel
* @param {Snowflake} id ID of the voice channel
* @param {Object} [options] Options
* @param {number} [options.timeout] Timeout for the command
* have explicit permission from the user.
* @returns {Promise}
*/
selectTextChannel(id, { timeout } = {}) {
return this.request(RPCCommands.SELECT_TEXT_CHANNEL, { channel_id: id, timeout });
}
/**
* Get current voice settings
* @returns {Promise}
*/
getVoiceSettings() {
return this.request(RPCCommands.GET_VOICE_SETTINGS).then((s) => ({
automaticGainControl: s.automatic_gain_control,
echoCancellation: s.echo_cancellation,
noiseSuppression: s.noise_suppression,
qos: s.qos,
silenceWarning: s.silence_warning,
deaf: s.deaf,
mute: s.mute,
input: {
availableDevices: s.input.available_devices,
device: s.input.device_id,
volume: s.input.volume
},
output: {
availableDevices: s.output.available_devices,
device: s.output.device_id,
volume: s.output.volume
},
mode: {
type: s.mode.type,
autoThreshold: s.mode.auto_threshold,
threshold: s.mode.threshold,
shortcut: s.mode.shortcut,
delay: s.mode.delay
}
}));
}
/**
* Set current voice settings, overriding the current settings until this session disconnects.
* This also locks the settings for any other rpc sessions which may be connected.
* @param {Object} args Settings
* @returns {Promise}
*/
setVoiceSettings(args) {
return this.request(RPCCommands.SET_VOICE_SETTINGS, {
automatic_gain_control: args.automaticGainControl,
echo_cancellation: args.echoCancellation,
noise_suppression: args.noiseSuppression,
qos: args.qos,
silence_warning: args.silenceWarning,
deaf: args.deaf,
mute: args.mute,
input: args.input ? {
device_id: args.input.device,
volume: args.input.volume
} : void 0,
output: args.output ? {
device_id: args.output.device,
volume: args.output.volume
} : void 0,
mode: args.mode ? {
type: args.mode.type,
auto_threshold: args.mode.autoThreshold,
threshold: args.mode.threshold,
shortcut: args.mode.shortcut,
delay: args.mode.delay
} : void 0
});
}
/**
* Capture a shortcut using the client
* The callback takes (key, stop) where `stop` is a function that will stop capturing.
* This `stop` function must be called before disconnecting or else the user will have
* to restart their client.
* @param {Function} callback Callback handling keys
* @returns {Promise<Function>}
*/
captureShortcut(callback) {
const subid = subKey(RPCEvents.CAPTURE_SHORTCUT_CHANGE);
const stop = () => {
this._subscriptions.delete(subid);
return this.request(RPCCommands.CAPTURE_SHORTCUT, { action: "STOP" });
};
this._subscriptions.set(subid, ({ shortcut }) => {
callback(shortcut, stop);
});
return this.request(RPCCommands.CAPTURE_SHORTCUT, { action: "START" }).then(() => stop);
}
/**
* Sets the presence for the logged in user.
* @param {object} args The rich presence to pass.
* @param {number} [pid] The application's process ID. Defaults to the executing process' PID.
* @returns {Promise}
*/
setActivity(args = {}, pid = getPid()) {
let timestamps;
let assets;
let party;
let secrets;
if (args.startTimestamp || args.endTimestamp) {
timestamps = {
start: args.startTimestamp,
end: args.endTimestamp
};
if (timestamps.start instanceof Date) {
timestamps.start = Math.round(timestamps.start.getTime());
}
if (timestamps.end instanceof Date) {
timestamps.end = Math.round(timestamps.end.getTime());
}
if (timestamps.start > 2147483647e3) {
throw new RangeError("timestamps.start must fit into a unix timestamp");
}
if (timestamps.end > 2147483647e3) {
throw new RangeError("timestamps.end must fit into a unix timestamp");
}
}
if (args.largeImageKey || args.largeImageText || args.smallImageKey || args.smallImageText) {
assets = {
large_image: args.largeImageKey,
large_text: args.largeImageText,
small_image: args.smallImageKey,
small_text: args.smallImageText
};
}
if (args.partySize || args.partyId || args.partyMax) {
party = { id: args.partyId };
if (args.partySize || args.partyMax) {
party.size = [args.partySize, args.partyMax];
}
}
if (args.matchSecret || args.joinSecret || args.spectateSecret) {
secrets = {
match: args.matchSecret,
join: args.joinSecret,
spectate: args.spectateSecret
};
}
return this.request(RPCCommands.SET_ACTIVITY, {
pid,
activity: {
state: args.state,
details: args.details,
timestamps,
assets,
party,
secrets,
buttons: args.buttons,
instance: !!args.instance
}
});
}
/**
* Clears the currently set presence, if any. This will hide the "Playing X" message
* displayed below the user's name.
* @param {number} [pid] The application's process ID. Defaults to the executing process' PID.
* @returns {Promise}
*/
clearActivity(pid = getPid()) {
return this.request(RPCCommands.SET_ACTIVITY, {
pid
});
}
/**
* Invite a user to join the game the RPC user is currently playing
* @param {User} user The user to invite
* @returns {Promise}
*/
sendJoinInvite(user) {
return this.request(RPCCommands.SEND_ACTIVITY_JOIN_INVITE, {
user_id: user.id || user
});
}
/**
* Request to join the game the user is playing
* @param {User} user The user whose game you want to request to join
* @returns {Promise}
*/
sendJoinRequest(user) {
return this.request(RPCCommands.SEND_ACTIVITY_JOIN_REQUEST, {
user_id: user.id || user
});
}
/**
* Reject a join request from a user
* @param {User} user The user whose request you wish to reject
* @returns {Promise}
*/
closeJoinRequest(user) {
return this.request(RPCCommands.CLOSE_ACTIVITY_JOIN_REQUEST, {
user_id: user.id || user
});
}
createLobby(type, capacity, metadata) {
return this.request(RPCCommands.CREATE_LOBBY, {
type,
capacity,
metadata
});
}
updateLobby(lobby, { type, owner, capacity, metadata } = {}) {
return this.request(RPCCommands.UPDATE_LOBBY, {
id: lobby.id || lobby,
type,
owner_id: owner && owner.id || owner,
capacity,
metadata
});
}
deleteLobby(lobby) {
return this.request(RPCCommands.DELETE_LOBBY, {
id: lobby.id || lobby
});
}
connectToLobby(id, secret) {
return this.request(RPCCommands.CONNECT_TO_LOBBY, {
id,
secret
});
}
sendToLobby(lobby, data) {
return this.request(RPCCommands.SEND_TO_LOBBY, {
id: lobby.id || lobby,
data
});
}
disconnectFromLobby(lobby) {
return this.request(RPCCommands.DISCONNECT_FROM_LOBBY, {
id: lobby.id || lobby
});
}
updateLobbyMember(lobby, user, metadata) {
return this.request(RPCCommands.UPDATE_LOBBY_MEMBER, {
lobby_id: lobby.id || lobby,
user_id: user.id || user,
metadata
});
}
getRelationships() {
const types = Object.keys(RelationshipTypes);
return this.request(RPCCommands.GET_RELATIONSHIPS).then((o) => o.relationships.map((r) => ({
...r,
type: types[r.type]
})));
}
/**
* Subscribe to an event
* @param {string} event Name of event e.g. `MESSAGE_CREATE`
* @param {Object} [args] Args for event e.g. `{ channel_id: '1234' }`
* @returns {Promise<Object>}
*/
async subscribe(event, args) {
await this.request(RPCCommands.SUBSCRIBE, args, event);
return {
unsubscribe: () => this.request(RPCCommands.UNSUBSCRIBE, args, event)
};
}
/**
* Destroy the client
*/
async destroy() {
await this.transport.close();
}
};
module2.exports = RPCClient;
}
});
// node_modules/discord-rpc/src/index.js
var require_src = __commonJS({
"node_modules/discord-rpc/src/index.js"(exports, module2) {
"use strict";
var util = require_util();
module2.exports = {
Client: require_client(),
register(id) {
return util.register(`discord-${id}`);
}
};
}
});
// src/main.ts
var main_exports = {};
__export(main_exports, {
default: () => ObsidianDiscordRPC
});
module.exports = __toCommonJS(main_exports);
var import_discord_rpc = __toESM(require_src());
var import_obsidian4 = require("obsidian");
// src/logger.ts
var import_obsidian = require("obsidian");
var Logger = class {
constructor(plugin) {
this.plugin = plugin;
}
log(message, showPopups) {
if (showPopups) {
new import_obsidian.Notice(message);
}
console.log(`discordrpc: ${message}`);
}
logIgnoreNoNotice(message) {
new import_obsidian.Notice(message);
console.log(`discordrpc: ${message}`);
}
};
// src/settings/settings.ts
var DiscordRPCSettings = class {
constructor() {
this.showVaultName = true;
this.showCurrentFileName = true;
this.showConnectionTimer = false;
this.showPopups = true;
this.customVaultName = "";
this.showFileExtension = false;
this.useLoadedTime = false;
this.connectOnStart = true;
this.autoHideStatusBar = true;
this.privacyMode = false;
this.themeStyle = "default-new-dark" /* Default_new_dark */;
}
};
// src/settings/settings-tab.ts
var import_obsidian2 = require("obsidian");
var DiscordRPCSettingsTab = class extends import_obsidian2.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
this.logger = new Logger(plugin);
}
display() {
let { containerEl } = this;
const plugin = this.plugin;
containerEl.empty();
new import_obsidian2.Setting(containerEl).setName("Vault name").setHeading();
new import_obsidian2.Setting(containerEl).setName("Privacy mode").setDesc("Enable this to hide the name of the vault and Hide file names").addToggle(
(boolean) => boolean.setValue(plugin.settings.privacyMode).onChange((value) => {
plugin.settings.privacyMode = value;
plugin.saveData(plugin.settings);
plugin.setActivity("", "", "");
})
);
new import_obsidian2.Setting(containerEl).setName("Show vault name").setDesc(
"Enable this to show the name of the vault you are working with."
).addToggle(
(boolean) => boolean.setValue(plugin.settings.showVaultName).onChange((value) => {
plugin.settings.showVaultName = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
})
);
new import_obsidian2.Setting(containerEl).setName("Set custom vault name").setDesc(
"Change the vault name shown publicly. Leave blank to use your actual vault name."
).addText(
(text) => text.setValue(plugin.settings.customVaultName).onChange((value) => {
plugin.settings.customVaultName = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
})
);
new import_obsidian2.Setting(containerEl).setName("File name").setHeading();
new import_obsidian2.Setting(containerEl).setName("Show current file name").setDesc("Enable this to show the name of the file you are working on.").addToggle(
(boolean) => boolean.setValue(plugin.settings.showCurrentFileName).onChange((value) => {
plugin.settings.showCurrentFileName = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
})
);
new import_obsidian2.Setting(containerEl).setName("Show file extension").setDesc("Enable this to show file extension.").addToggle(
(boolean) => boolean.setValue(plugin.settings.showFileExtension).onChange((value) => {
plugin.settings.showFileExtension = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
})
);
new import_obsidian2.Setting(containerEl).setName("Time tracking").setHeading();
new import_obsidian2.Setting(containerEl).setName("Use obsidian total time").setDesc(
"Enable to use the total time you have been using Obsidian, instead of the time spent editing a single file."
).addToggle((boolean) => {
boolean.setValue(plugin.settings.useLoadedTime).onChange((value) => {
plugin.settings.useLoadedTime = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
});
});
new import_obsidian2.Setting(containerEl).setName("Status bar").setHeading();
new import_obsidian2.Setting(containerEl).setName("Automatically hide status bar").setDesc(
"Automatically hide status bar after successfully connecting to Discord."
).addToggle((boolean) => {
boolean.setValue(plugin.settings.autoHideStatusBar).onChange((value) => {
plugin.settings.autoHideStatusBar = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
});
});
new import_obsidian2.Setting(containerEl).setName("Show connected time").setDesc(
"Show time spent editing file or time connected to Discord in the status bar."
).addToggle((boolean) => {
boolean.setValue(plugin.settings.showConnectionTimer).onChange((value) => {
plugin.settings.showConnectionTimer = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
plugin.statusBar.displayState(plugin.getState(), plugin.settings.autoHideStatusBar);
});
});
new import_obsidian2.Setting(containerEl).setName("Startup behavior").setHeading();
new import_obsidian2.Setting(containerEl).setName("Automatically connect to Discord").setDesc(
"Automatically connect to Discord on startup. You can always click the status bar to manually connect."
).addToggle((boolean) => {
boolean.setValue(plugin.settings.connectOnStart).onChange((value) => {
plugin.settings.connectOnStart = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
});
});
new import_obsidian2.Setting(containerEl).setName("Notices").setHeading();
new import_obsidian2.Setting(containerEl).setName("Show notices").setDesc("Enable this to show connection Notices.").addToggle(
(boolean) => boolean.setValue(plugin.settings.showPopups).onChange((value) => {
plugin.settings.showPopups = value;
plugin.saveData(plugin.settings);
plugin.setActivity(
this.app.vault.getName(),
plugin.currentFile.basename,
plugin.currentFile.extension
);
})
);
new import_obsidian2.Setting(containerEl).setName("Theme style").setHeading();
new import_obsidian2.Setting(containerEl).setName("Theme style").setDesc("Choose the theme style for Discord Rich Presence").addDropdown((dropdown) => dropdown.addOption("default-old-dark" /* Default_dark */, "Default Dark (Old)").addOption("default-old-light" /* Default_light */, "Default Light (Old)").addOption("default-new-dark" /* Default_new_dark */, "Default Dark (New)").addOption("default-new-light" /* Default_new_light */, "Default Light (New)").addOption("latte" /* Catppuccin_Latte */, "Catppuccin Latte").addOption("frappe" /* Catppuccin_Frappe */, "Catppuccin Frappe").addOption("macchiato" /* Catppuccin_Macchiato */, "Catppuccin Macchiato").addOption("mocha" /* Catppuccin_Mocha */, "Catppuccin Mocha").addOption("cyberglow-dark" /* Cyberglow_Dark */, "Cyberglow Dark").addOption("cyberglow-light" /* Cyberglow_Light */, "Cyberglow Light").addOption("tokyo-night-dark" /* Tokyo_night_Dark */, "Tokyo Night Dark").addOption("tokyo-night-light" /* Tokyo_night_Light */, "Tokyo Night Light").setValue(plugin.settings.themeStyle).onChange(async (value) => {
var _a, _b, _c, _d;
plugin.settings.themeStyle = value;
await plugin.saveData(plugin.settings);
if (plugin.getState() === 0 /* connected */) {
await plugin.setActivity(
plugin.app.vault.getName(),
(_b = (_a = plugin.currentFile) == null ? void 0 : _a.basename) != null ? _b : "...",
(_d = (_c = plugin.currentFile) == null ? void 0 : _c.extension) != null ? _d : ""
);
}
}));
}
};
// src/status-bar.ts
var import_obsidian3 = require("obsidian");
var StatusBar = class _StatusBar {
constructor(statusBarEl) {
this.statusBarEl = statusBarEl;
}
displayState(state, autoHide) {
switch (state) {
case 0 /* connected */:
this.displayConnected(autoHide ? 1e4 : 0);
break;
case 1 /* connecting */:
this.statusBarEl.setText(`Connecting to Discord...`);
break;
case 2 /* disconnected */:
this.statusBarEl.setText(`\u{1F5D8} Reconnect to Discord`);
break;
}
}
displayTimer(loadedTime) {
this.statusBarEl.setText(`\u{1F30D} ${_StatusBar.millisecsToString((/* @__PURE__ */ new Date()).getTime() - loadedTime.getTime())}`);
}
displayConnected(timeout) {
this.statusBarEl.setText(`\u2713 Connected to Discord`);
if (timeout && timeout > 0) {
window.setTimeout(() => {
this.statusBarEl.setText("");
}, timeout);
} else {
window.setTimeout(() => {
this.statusBarEl.setText(`Discord RPC`);
}, 5e3);
}
}
/* Returns [HH:]mm:ss on the stopwatch
https://github.com/grassbl8d/flexible-pomo-obsidian/blob/ae037e3710866863c5397f42af06c5540a807b10/src/timer.ts#L475
*/
static millisecsToString(millisecs) {
let formattedCountDown;
if (millisecs >= 60 * 60 * 1e3) {
formattedCountDown = import_obsidian3.moment.utc(millisecs).format("HH:mm:ss");
} else {
formattedCountDown = import_obsidian3.moment.utc(millisecs).format("mm:ss");
}
return formattedCountDown.toString();
}
};
// src/main.ts
var ObsidianDiscordRPC = class extends import_obsidian4.Plugin {
constructor() {
super(...arguments);
this.logger = new Logger();
}
setState(state) {
this.state = state;
}
getState() {
return this.state;
}
getApp() {
return this.app;
}
getPluginManifest() {
return this.manifest;
}
async onload() {
let statusBarEl = this.addStatusBarItem();
this.statusBar = new StatusBar(statusBarEl);
this.settings = await this.loadData() || new DiscordRPCSettings();
this.registerEvent(
this.app.workspace.on("file-open", this.onFileOpen, this)
);
this.registerInterval(window.setInterval(async () => {
if (this.settings.showConnectionTimer && this.getState() == 0 /* connected */) {
this.statusBar.displayTimer(this.settings.useLoadedTime ? this.loadedTime : this.lastSetTime);
}
}, 500));
this.registerDomEvent(statusBarEl, "click", async () => {
if (this.getState() == 2 /* disconnected */) {
await this.connectDiscord();
} else if (this.getState() == 0 /* connected */) {
await this.disconnectDiscord();
}
});
this.addSettingTab(new DiscordRPCSettingsTab(this.app, this));
this.addCommand({
id: "reconnect-discord",
name: "Reconnect to Discord",
callback: async () => await this.connectDiscord()
});
this.addCommand({
id: "disconnect-discord",
name: "Disconnect from Discord",
callback: async () => await this.disconnectDiscord()
});
if (this.settings.connectOnStart) {
await this.connectDiscord();
const activeLeaf = this.app.workspace.activeLeaf;
const files = this.app.vault.getMarkdownFiles();
if (activeLeaf) {
const displayText = activeLeaf.getDisplayText();
files.forEach((file) => {
if (file.basename === displayText) {
this.onFileOpen(file);
}
});
}
} else {
this.setState(2 /* disconnected */);
this.statusBar.displayState(
this.getState(),
this.settings.autoHideStatusBar
);
}
}
async onFileOpen(file) {
this.currentFile = file;
if (this.getState() === 0 /* connected */) {
await this.setActivity(
this.app.vault.getName(),
file.basename,
file.extension
);
}
}
async onunload() {
await this.saveData(this.settings);
this.rpc.clearActivity();
this.rpc.destroy();
}
async connectDiscord() {
this.loadedTime = /* @__PURE__ */ new Date();
this.lastSetTime = /* @__PURE__ */ new Date();
this.rpc = new import_discord_rpc.Client({
transport: "ipc"
});
this.setState(1 /* connecting */);
this.statusBar.displayState(
this.getState(),
this.settings.autoHideStatusBar
);
this.rpc.once("ready", () => {
this.setState(0 /* connected */);
this.statusBar.displayState(
this.getState(),
this.settings.autoHideStatusBar
);
this.logger.log("Connected to Discord", this.settings.showPopups);
});
try {
await this.rpc.login({
clientId: "1352970439684657152"
});
await this.setActivity(this.app.vault.getName(), "...", "");
} catch (error) {
this.setState(2 /* disconnected */);
this.statusBar.displayState(
this.getState(),
this.settings.autoHideStatusBar
);
this.logger.log("Failed to connect to Discord", this.settings.showPopups);
}
}
async disconnectDiscord() {
this.rpc.clearActivity();
this.rpc.destroy();
this.setState(2 /* disconnected */);
this.statusBar.displayState(
this.getState(),
this.settings.autoHideStatusBar
);
this.logger.log("Disconnected from Discord", this.settings.showPopups);
}
async setActivity(vaultName, fileName, fileExtension) {
if (this.getState() === 0 /* connected */) {
let vault;
if (this.settings.customVaultName === "") {
vault = vaultName;
} else {
vault = this.settings.customVaultName;
}
let file;
if (this.settings.showFileExtension) {
file = fileName + "." + fileExtension;
} else {
file = fileName;
}
let date;
if (this.settings.useLoadedTime) {
date = this.loadedTime;
} else {
date = /* @__PURE__ */ new Date();
}
this.lastSetTime = date;
if (this.settings.privacyMode) {
await this.rpc.setActivity({
details: `Editing Notes`,
state: `Working in a Vault`,
startTimestamp: date,
largeImageKey: this.settings.themeStyle,
largeImageText: "no info just privacy mode"
});
} else if (this.settings.showVaultName && this.settings.showCurrentFileName) {
await this.rpc.setActivity({
details: `Editing ${file}`,
state: `Vault: ${vault}`,
startTimestamp: date,
largeImageKey: this.settings.themeStyle,
largeImageText: "I'm thinking!"
});
} else if (this.settings.showVaultName) {
await this.rpc.setActivity({
state: `Vault: ${vault}`,
startTimestamp: date,
largeImageKey: this.settings.themeStyle,
largeImageText: "Obsidian"
});
} else if (this.settings.showCurrentFileName) {
await this.rpc.setActivity({
details: `Editing ${file}`,
startTimestamp: date,
largeImageKey: this.settings.themeStyle,
largeImageText: "I'm thinking!"
});
} else {
await this.rpc.setActivity({
startTimestamp: date,
largeImageKey: this.settings.themeStyle,
largeImageText: "Obsidian"
});
}
}
}
};
/* nosourcemap */