Files
BridgeSwarm/native-host/discord/snapshot.js
T
snxraven 693814f46b
CI / Build & Test (push) Failing after 52s
Add Discordjs
2026-09-04 22:10:35 -04:00

227 lines
5.8 KiB
JavaScript

/**
* Serialize discord.js structures for native messaging (JSON, ~1 MB cap).
*/
'use strict';
const PREDICATES = [
'isChatInputCommand',
'isButton',
'isRepliable',
'isStringSelectMenu',
'isUserSelectMenu',
'isRoleSelectMenu',
'isMentionableSelectMenu',
'isChannelSelectMenu',
'isAnySelectMenu',
'isModalSubmit',
'isAutocomplete',
'isContextMenuCommand',
'isUserContextMenuCommand',
'isMessageContextMenuCommand',
'isMessageComponent',
'isCommand',
];
const STUB_METHODS = [
'reply',
'followUp',
'editReply',
'deferReply',
'deferUpdate',
'deleteReply',
'fetchReply',
'showModal',
'update',
'react',
'delete',
'edit',
'send',
'startThread',
'pin',
'unpin',
'fetch',
'setToken',
'put',
'post',
'get',
'patch',
'login',
'destroy',
];
function intern(handles, obj) {
if (!obj || typeof obj !== 'object') return null;
if (obj.__bsHandle && handles.byId.has(obj.__bsHandle)) return obj.__bsHandle;
const id = handles.nextId++;
const handle = 'dj_' + id;
obj.__bsHandle = handle;
handles.byId.set(handle, obj);
handles.meta.set(handle, {
type: (obj.constructor && obj.constructor.name) || 'Object',
});
return handle;
}
function jsonSafe(value, depth) {
if (depth > 5) return undefined;
if (value == null) return value;
const t = typeof value;
if (t === 'string' || t === 'number' || t === 'boolean') return value;
if (t === 'bigint') return value.toString();
if (t === 'function') return undefined;
if (Array.isArray(value)) {
const out = [];
for (let i = 0; i < value.length && i < 50; i++) {
const item = jsonSafe(value[i], depth + 1);
if (item !== undefined) out.push(item);
}
return out;
}
if (t !== 'object') return String(value);
if (typeof value.toJSON === 'function') {
try {
return jsonSafe(value.toJSON(), depth + 1);
} catch (_) {}
}
const out = {};
let n = 0;
for (const key of Object.keys(value)) {
if (n++ > 80) break;
if (key.charAt(0) === '_') continue;
const v = jsonSafe(value[key], depth + 1);
if (v !== undefined) out[key] = v;
}
return out;
}
function snapshotValue(value, handles, depth) {
if (depth > 4) return jsonSafe(value, 0);
if (value == null || typeof value !== 'object') return jsonSafe(value, 0);
if (typeof value.then === 'function') return { _type: 'Promise' };
const ctor = value.constructor && value.constructor.name;
const looksLive =
typeof value.reply === 'function' ||
typeof value.login === 'function' ||
typeof value.setToken === 'function' ||
typeof value.toJSON === 'function' ||
(ctor && ctor !== 'Object' && ctor !== 'Array');
if (!looksLive) return jsonSafe(value, 0);
const handle = intern(handles, value);
let json = {};
if (typeof value.toJSON === 'function') {
try {
json = jsonSafe(value.toJSON(), 0) || {};
} catch (_) {
json = {};
}
} else {
json = jsonSafe(value, 1) || {};
}
const pred = {};
for (const name of PREDICATES) {
if (typeof value[name] === 'function') {
try {
pred[name] = !!value[name]();
} catch (_) {}
}
}
const methods = [];
for (const name of STUB_METHODS) {
if (typeof value[name] === 'function') methods.push(name);
}
const snap = Object.assign({ _handle: handle, _type: ctor || 'Object', _methods: methods }, json, pred);
if (value.user && typeof value.user === 'object') {
snap.user = jsonSafe(value.user, 0);
if (value.user.tag) snap.user.tag = value.user.tag;
if (value.user.id) snap.user.id = value.user.id;
}
if (value.author && typeof value.author === 'object') {
snap.author = jsonSafe(value.author, 0);
}
if (value.channel && typeof value.channel === 'object') {
snap.channel = {
id: value.channel.id,
type: value.channel.type,
name: value.channel.name,
_handle: intern(handles, value.channel),
_type: (value.channel.constructor && value.channel.constructor.name) || 'Channel',
_methods: typeof value.channel.send === 'function' ? ['send'] : [],
};
}
if (value.guild && typeof value.guild === 'object') {
snap.guild = {
id: value.guild.id,
name: value.guild.name,
_handle: intern(handles, value.guild),
_type: 'Guild',
};
}
if (value.commandName != null) snap.commandName = value.commandName;
if (value.customId != null) snap.customId = value.customId;
if (value.content != null) snap.content = value.content;
return snap;
}
function snapshotEventArgs(args, handles) {
const out = [];
for (let i = 0; i < args.length && i < 6; i++) {
const a = args[i];
if (a instanceof Error) {
out.push({ _type: 'Error', message: a.message, code: a.code });
} else {
out.push(snapshotValue(a, handles, 0));
}
}
return out;
}
function serializeConstants(discord) {
const out = { _classes: [] };
if (!discord || typeof discord !== 'object') return out;
for (const key of Object.keys(discord)) {
const v = discord[key];
if (typeof v === 'function') {
out._classes.push(key);
continue;
}
if (v && typeof v === 'object') {
const plain = {};
let n = 0;
for (const k of Object.keys(v)) {
if (n++ > 200) break;
const x = v[k];
if (typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean') {
plain[k] = x;
}
}
if (Object.keys(plain).length) out[key] = plain;
} else if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
out[key] = v;
}
}
return out;
}
function createHandleTable() {
return { nextId: 1, byId: new Map(), meta: new Map() };
}
module.exports = {
PREDICATES,
STUB_METHODS,
intern,
snapshotValue,
snapshotEventArgs,
serializeConstants,
createHandleTable,
jsonSafe,
};