227 lines
5.8 KiB
JavaScript
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,
|
|
};
|