big refactor to keep motivation
This commit is contained in:
parent
8f89815106
commit
8bb2ad3012
46 changed files with 3949 additions and 4766 deletions
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"bracketSpacing": false,
|
"bracketSpacing": false,
|
||||||
"endOfLine": "lf"
|
"endOfLine": "lf",
|
||||||
|
"printWidth": 120
|
||||||
}
|
}
|
||||||
|
|
97
src/index.js
97
src/index.js
|
@ -1,5 +1,4 @@
|
||||||
const Dysnomia = require("@projectdysnomia/dysnomia");
|
const {Client, Collection, Channel, Permission} = require("@projectdysnomia/dysnomia");
|
||||||
const logger = require("./lib/logger.js");
|
|
||||||
const fs = require("node:fs");
|
const fs = require("node:fs");
|
||||||
const {resolve} = require("node:path");
|
const {resolve} = require("node:path");
|
||||||
const sqlite3 = require("sqlite3");
|
const sqlite3 = require("sqlite3");
|
||||||
|
@ -8,22 +7,25 @@ const {instead, before} = require("spitroast");
|
||||||
const config = require("../config.json");
|
const config = require("../config.json");
|
||||||
const apikeys = require("../apikeys.json");
|
const apikeys = require("../apikeys.json");
|
||||||
|
|
||||||
|
const logger = require("./lib/logger.js");
|
||||||
const events = require("./lib/events.js");
|
const events = require("./lib/events.js");
|
||||||
const timer = require("./lib/timer.js");
|
const timer = require("./lib/timer.js");
|
||||||
const Command = require("./lib/command.js");
|
const Command = require("./lib/command.js");
|
||||||
const InteractionCommand = require("./lib/interactionCommand.js");
|
const InteractionCommand = require("./lib/interactionCommand.js");
|
||||||
|
|
||||||
const bot = new Dysnomia.Client(config.token, {
|
const {APIEndpoints, Intents, ApplicationCommandTypes, GatewayOPCodes} = require("./util/dconstants.js");
|
||||||
|
|
||||||
|
const bot = new Client(config.token, {
|
||||||
defaultImageFormat: "png",
|
defaultImageFormat: "png",
|
||||||
defaultImageSize: 1024,
|
defaultImageSize: 1024,
|
||||||
gateway: {
|
gateway: {
|
||||||
intents: Object.values(Dysnomia.Constants.Intents),
|
intents: Object.values(Intents),
|
||||||
},
|
},
|
||||||
restMode: true,
|
restMode: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const commands = new Dysnomia.Collection();
|
const commands = new Collection();
|
||||||
const interactionCommands = new Dysnomia.Collection();
|
const interactionCommands = new Collection();
|
||||||
|
|
||||||
const database = new sqlite3.Database(resolve(__dirname, "..", "database.db"));
|
const database = new sqlite3.Database(resolve(__dirname, "..", "database.db"));
|
||||||
|
|
||||||
|
@ -33,9 +35,7 @@ function registerCommand(cmdObj) {
|
||||||
const aliases = cmdObj.getAliases();
|
const aliases = cmdObj.getAliases();
|
||||||
logger.info(
|
logger.info(
|
||||||
"hf:cmd",
|
"hf:cmd",
|
||||||
`Registered command '${cmdObj.name}'${
|
`Registered command '${cmdObj.name}'${aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : ""}`
|
||||||
aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : ""
|
|
||||||
}`
|
|
||||||
);
|
);
|
||||||
} else if (cmdObj instanceof InteractionCommand) {
|
} else if (cmdObj instanceof InteractionCommand) {
|
||||||
interactionCommands.set(cmdObj.name, cmdObj);
|
interactionCommands.set(cmdObj.name, cmdObj);
|
||||||
|
@ -55,57 +55,51 @@ global.hf = {
|
||||||
database,
|
database,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {formatUsername} = require("./lib/utils.js");
|
const {formatUsername} = require("./util/misc.js");
|
||||||
const CommandDispatcher = require("./lib/commandDispatcher.js");
|
const CommandDispatcher = require("./lib/commandDispatcher.js");
|
||||||
const {InteractionDispatcher} = require("./lib/interactionDispatcher.js");
|
const {InteractionDispatcher} = require("./lib/interactionDispatcher.js");
|
||||||
|
const {hasFlag} = require("./lib/guildData.js");
|
||||||
|
|
||||||
for (const file of fs.readdirSync(resolve(__dirname, "modules"))) {
|
for (const file of fs.readdirSync(resolve(__dirname, "modules"))) {
|
||||||
require(resolve(__dirname, "modules", file));
|
try {
|
||||||
logger.info("hf:modules", `Loaded module: '${file}'`);
|
require(resolve(__dirname, "modules", file));
|
||||||
|
logger.info("hf:modules", `Loaded module: "${file}"`);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("hf:modules", `Failed to load "${file}": ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bot.on("messageCreate", async (msg) => {
|
bot.on("messageCreate", async (msg) => {
|
||||||
try {
|
try {
|
||||||
// fix DMs cause of gateway v8 changes
|
// fix DMs cause of gateway v8 changes
|
||||||
if (
|
if (!(msg.channel instanceof Channel) && msg.author.id != bot.user.id && !msg.guildID) {
|
||||||
!(msg.channel instanceof Dysnomia.Channel) &&
|
|
||||||
msg.author.id != bot.user.id &&
|
|
||||||
!msg.guildID
|
|
||||||
) {
|
|
||||||
const newChannel = await bot.getDMChannel(msg.author.id);
|
const newChannel = await bot.getDMChannel(msg.author.id);
|
||||||
if (msg.channel.id == newChannel.id) msg.channel = newChannel;
|
if (msg.channel.id == newChannel.id) msg.channel = newChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(msg.channel instanceof Dysnomia.Channel)) return;
|
if (!(msg.channel instanceof Channel)) return;
|
||||||
|
if (msg.author.bot && msg.guildID) {
|
||||||
|
const canBot = await hasFlag(msg.guildID, "replyToBots");
|
||||||
|
if (!canBot) return;
|
||||||
|
}
|
||||||
|
|
||||||
await CommandDispatcher(msg);
|
await CommandDispatcher(msg);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const stack = (err?.stack ?? err.message).split("\n");
|
const stack = (err?.stack ?? err.message).split("\n");
|
||||||
const error = stack.shift();
|
const error = stack.shift();
|
||||||
logger.error(
|
logger.error("hf:main", `Failed to dispatch command: ${error}\n\t${stack.join("\n\t")}`);
|
||||||
"hf:main",
|
|
||||||
`Failed to dispatch command: ${error}\n\t${stack.join("\n\t")}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bot.on("messageUpdate", async (msg, oldMsg) => {
|
bot.on("messageUpdate", async (msg, oldMsg) => {
|
||||||
try {
|
try {
|
||||||
const oneDay = Date.now() - 86400000;
|
const oneDay = Date.now() - 86400000;
|
||||||
if (
|
if (msg.timestamp > oneDay && !msg.hasRan && oldMsg && oldMsg.content !== msg.content) {
|
||||||
msg.timestamp > oneDay &&
|
|
||||||
!msg.hasRan &&
|
|
||||||
oldMsg &&
|
|
||||||
oldMsg.content !== msg.content
|
|
||||||
) {
|
|
||||||
await CommandDispatcher(msg);
|
await CommandDispatcher(msg);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const stack = (err?.stack ?? err.message).split("\n");
|
const stack = (err?.stack ?? err.message).split("\n");
|
||||||
const error = stack.shift();
|
const error = stack.shift();
|
||||||
logger.error(
|
logger.error("hf:main", `Failed to dispatch command update: ${error}\n\t${stack.join("\n\t")}`);
|
||||||
"hf:main",
|
|
||||||
`Failed to dispatch command update: ${error}\n\t${stack.join("\n\t")}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
||||||
|
@ -114,7 +108,7 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let channel = msg.channel;
|
let channel = msg.channel;
|
||||||
if (!(channel instanceof Dysnomia.Channel)) {
|
if (!(channel instanceof Channel)) {
|
||||||
const newChannel = hf.bot.getChannel(channel.id);
|
const newChannel = hf.bot.getChannel(channel.id);
|
||||||
if (newChannel) {
|
if (newChannel) {
|
||||||
channel = newChannel;
|
channel = newChannel;
|
||||||
|
@ -137,10 +131,7 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const stack = (err?.stack ?? err.message).split("\n");
|
const stack = (err?.stack ?? err.message).split("\n");
|
||||||
const error = stack.shift();
|
const error = stack.shift();
|
||||||
logger.error(
|
logger.error("hf:main", `Failed to self-delete message: ${error}\n\t${stack.join("\n\t")}`);
|
||||||
"hf:main",
|
|
||||||
`Failed to self-delete message: ${error}\n\t${stack.join("\n\t")}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bot.on("interactionCreate", async (interaction) => {
|
bot.on("interactionCreate", async (interaction) => {
|
||||||
|
@ -149,21 +140,13 @@ bot.on("interactionCreate", async (interaction) => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const stack = (err?.stack ?? err.message).split("\n");
|
const stack = (err?.stack ?? err.message).split("\n");
|
||||||
const error = stack.shift();
|
const error = stack.shift();
|
||||||
logger.error(
|
logger.error("hf:main", `Failed to dispatch interaction command: ${error}\n\t${stack.join("\n\t")}`);
|
||||||
"hf:main",
|
|
||||||
`Failed to dispatch interaction command: ${error}\n\t${stack.join(
|
|
||||||
"\n\t"
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.once("ready", async () => {
|
bot.once("ready", async () => {
|
||||||
logger.info("hf:main", "Connected to Discord.");
|
logger.info("hf:main", "Connected to Discord.");
|
||||||
logger.info(
|
logger.info("hf:main", `Logged in as: ${formatUsername(bot.user)} (${bot.user.id})`);
|
||||||
"hf:main",
|
|
||||||
`Logged in as: ${formatUsername(bot.user)} (${bot.user.id})`
|
|
||||||
);
|
|
||||||
|
|
||||||
const channel = await bot.getDMChannel(config.owner_id);
|
const channel = await bot.getDMChannel(config.owner_id);
|
||||||
if (channel) {
|
if (channel) {
|
||||||
|
@ -202,25 +185,18 @@ bot.once("ready", async () => {
|
||||||
formattedCommand.contexts.push(1, 2);
|
formattedCommand.contexts.push(1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.type === Dysnomia.Constants.ApplicationCommandTypes.CHAT_INPUT)
|
if (command.type === ApplicationCommandTypes.CHAT_INPUT)
|
||||||
formattedCommand.name = formattedCommand.name.toLowerCase();
|
formattedCommand.name = formattedCommand.name.toLowerCase();
|
||||||
|
|
||||||
if (command.permissions !== undefined) {
|
if (command.permissions !== undefined) {
|
||||||
formattedCommand.default_member_permissions =
|
formattedCommand.default_member_permissions =
|
||||||
command.permissions instanceof Dysnomia.Permission
|
command.permissions instanceof Permission ? String(command.permissions.allow) : String(command.permissions);
|
||||||
? String(command.permissions.allow)
|
|
||||||
: String(command.permissions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commands.push(formattedCommand);
|
commands.push(formattedCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
await bot.requestHandler.request(
|
await bot.requestHandler.request("PUT", APIEndpoints.COMMANDS(bot.application.id), true, commands);
|
||||||
"PUT",
|
|
||||||
`/applications/${bot.application.id}/commands`,
|
|
||||||
true,
|
|
||||||
commands
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.on("error", (err) => {
|
bot.on("error", (err) => {
|
||||||
|
@ -243,10 +219,7 @@ bot.on("shardReady", (id) => {
|
||||||
logger.verbose("hf:shard", `Shard ${id} ready`);
|
logger.verbose("hf:shard", `Shard ${id} ready`);
|
||||||
});
|
});
|
||||||
bot.on("unknown", (packet, id) => {
|
bot.on("unknown", (packet, id) => {
|
||||||
logger.verbose(
|
logger.verbose("hf:main", `Shard ${id} caught unknown packet: ${JSON.stringify(packet)}`);
|
||||||
"hf:main",
|
|
||||||
`Shard ${id} caught unknown packet: ${JSON.stringify(packet)}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
instead("spawn", bot.shards, function (args, orig) {
|
instead("spawn", bot.shards, function (args, orig) {
|
||||||
|
@ -254,7 +227,7 @@ instead("spawn", bot.shards, function (args, orig) {
|
||||||
const shard = this.get(args[0]);
|
const shard = this.get(args[0]);
|
||||||
if (shard) {
|
if (shard) {
|
||||||
before("sendWS", shard.__proto__, function ([op, _data]) {
|
before("sendWS", shard.__proto__, function ([op, _data]) {
|
||||||
if (op === Dysnomia.Constants.GatewayOPCodes.IDENTIFY) {
|
if (op === GatewayOPCodes.IDENTIFY) {
|
||||||
_data.properties.browser = "Discord Embedded";
|
_data.properties.browser = "Discord Embedded";
|
||||||
delete _data.properties.device;
|
delete _data.properties.device;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const logger = require("./logger.js");
|
const logger = require("./logger.js");
|
||||||
const {pastelize, getTopColor} = require("./utils.js");
|
const {pastelize, getTopColor} = require("../util/misc.js");
|
||||||
|
|
||||||
function convertIfApplicable(val) {
|
function convertIfApplicable(val) {
|
||||||
if (isNaN(val)) {
|
if (isNaN(val)) {
|
||||||
|
@ -27,10 +27,7 @@ function parseAsArgv(args) {
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
const arg = args.shift();
|
const arg = args.shift();
|
||||||
const equalsIndex = arg.charAt(0) === "-" ? arg.indexOf("=") : -1;
|
const equalsIndex = arg.charAt(0) === "-" ? arg.indexOf("=") : -1;
|
||||||
const argName =
|
const argName = equalsIndex === -1 ? removeStartHyphens(arg) : removeStartHyphens(arg.slice(0, equalsIndex));
|
||||||
equalsIndex === -1
|
|
||||||
? removeStartHyphens(arg)
|
|
||||||
: removeStartHyphens(arg.slice(0, equalsIndex));
|
|
||||||
|
|
||||||
if (equalsIndex !== -1) {
|
if (equalsIndex !== -1) {
|
||||||
optional[argName] = convertIfApplicable(arg.slice(equalsIndex + 1));
|
optional[argName] = convertIfApplicable(arg.slice(equalsIndex + 1));
|
||||||
|
@ -176,9 +173,7 @@ async function CommandDispatcher(msg) {
|
||||||
}
|
}
|
||||||
if (response.embeds) {
|
if (response.embeds) {
|
||||||
for (const embed of response.embeds) {
|
for (const embed of response.embeds) {
|
||||||
embed.color =
|
embed.color = embed.color || getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id));
|
||||||
embed.color ||
|
|
||||||
getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response.reaction) {
|
if (response.reaction) {
|
||||||
|
@ -186,17 +181,14 @@ async function CommandDispatcher(msg) {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const outMessage = await msg.channel.createMessage(
|
const outMessage = await msg.channel.createMessage(
|
||||||
Object.assign(
|
Object.assign(typeof response === "string" ? {content: response} : response, {
|
||||||
typeof response === "string" ? {content: response} : response,
|
allowedMentions: {
|
||||||
{
|
repliedUser: false,
|
||||||
allowedMentions: {
|
},
|
||||||
repliedUser: false,
|
messageReference: {
|
||||||
},
|
messageID: msg.id,
|
||||||
messageReference: {
|
},
|
||||||
messageID: msg.id,
|
})
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
if (response.addReactions) {
|
if (response.addReactions) {
|
||||||
for (const index in response.addReactions) {
|
for (const index in response.addReactions) {
|
||||||
|
|
|
@ -17,10 +17,7 @@ function add(event, identifier, callback) {
|
||||||
try {
|
try {
|
||||||
callback.apply(this, arguments);
|
callback.apply(this, arguments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error("hf:event", `Event "${event}:${identifier}" failed: ${error}`);
|
||||||
"hf:event",
|
|
||||||
`Event "${event}:${identifier}" failed: ${error}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
hf.database.run(
|
hf.database.run("CREATE TABLE IF NOT EXISTS guild_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID");
|
||||||
"CREATE TABLE IF NOT EXISTS guild_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID"
|
|
||||||
);
|
|
||||||
|
|
||||||
function setGuildData(id, key, value) {
|
function setGuildData(id, key, value) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ const {getGuildData, setGuildData} = require("./guildData.js");
|
||||||
|
|
||||||
const flags = Object.freeze({
|
const flags = Object.freeze({
|
||||||
codePreviews: 1 << 0,
|
codePreviews: 1 << 0,
|
||||||
tweetUnrolling: 1 << 1,
|
replyToBots: 1 << 1,
|
||||||
fedimbed: 1 << 2,
|
fedimbed: 1 << 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,9 +18,7 @@ async function getFlags(guildId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hasFlag(guildId, key) {
|
async function hasFlag(guildId, key) {
|
||||||
return (
|
return ((await getGuildData(guildId, "settings_flags", 0)) & flags[key]) !== 0;
|
||||||
((await getGuildData(guildId, "settings_flags", 0)) & flags[key]) !== 0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enableFlag(guildId, key) {
|
async function enableFlag(guildId, key) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
const {ApplicationCommandTypes, ApplicationCommandOptionTypes} =
|
const {ApplicationCommandTypes, ApplicationCommandOptionTypes} = require("../util/dconstants.js");
|
||||||
require("@projectdysnomia/dysnomia").Constants;
|
|
||||||
|
|
||||||
class InteractionCommand {
|
class InteractionCommand {
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
|
@ -18,6 +17,10 @@ class InteractionCommand {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOption(interaction, key) {
|
||||||
|
return interaction.data.options?.find((o) => o.name === key)?.value ?? this.options[key].default;
|
||||||
|
}
|
||||||
|
|
||||||
callback() {
|
callback() {
|
||||||
return "Callback not overwritten.";
|
return "Callback not overwritten.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
const logger = require("./logger.js");
|
const logger = require("./logger.js");
|
||||||
const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants;
|
const {MessageFlags} = require("../util/dconstants.js");
|
||||||
const {pastelize, getTopColor} = require("./utils.js");
|
const {pastelize, getTopColor} = require("../util/misc.js");
|
||||||
|
|
||||||
function getOption(interaction, command, key) {
|
|
||||||
return (
|
|
||||||
interaction.data.options?.find((o) => o.name === key)?.value ??
|
|
||||||
command.options[key].default
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runCommand(interaction, command) {
|
async function runCommand(interaction, command) {
|
||||||
if (command.ownerOnly && interaction.user.id != hf.config.owner_id) {
|
if (command.ownerOnly && interaction.user.id != hf.config.owner_id) {
|
||||||
|
@ -17,10 +10,7 @@ async function runCommand(interaction, command) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (command.elevatedOnly && !hf.config.elevated.includes(interaction.user.id)) {
|
||||||
command.elevatedOnly &&
|
|
||||||
!hf.config.elevated.includes(interaction.user.id)
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
content: "No\n\nSent from my iPhone",
|
content: "No\n\nSent from my iPhone",
|
||||||
flags: MessageFlags.EPHEMERAL,
|
flags: MessageFlags.EPHEMERAL,
|
||||||
|
@ -45,7 +35,7 @@ async function runCommand(interaction, command) {
|
||||||
|
|
||||||
async function InteractionDispatcher(interaction) {
|
async function InteractionDispatcher(interaction) {
|
||||||
const command = hf.interactionCommands.get(interaction.data.name);
|
const command = hf.interactionCommands.get(interaction.data.name);
|
||||||
const shouldSend = getOption(interaction, command, "send");
|
const shouldSend = command.getOption(interaction, "send");
|
||||||
const ack = interaction.acknowledge(shouldSend ? 0 : MessageFlags.EPHEMERAL);
|
const ack = interaction.acknowledge(shouldSend ? 0 : MessageFlags.EPHEMERAL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -86,24 +76,19 @@ async function InteractionDispatcher(interaction) {
|
||||||
}
|
}
|
||||||
if (response.embeds) {
|
if (response.embeds) {
|
||||||
for (const embed of response.embeds) {
|
for (const embed of response.embeds) {
|
||||||
embed.color =
|
embed.color = embed.color ?? getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id));
|
||||||
embed.color ??
|
|
||||||
getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ack;
|
await ack;
|
||||||
await interaction.createMessage(
|
await interaction.createMessage(
|
||||||
Object.assign(
|
Object.assign(typeof response === "string" ? {content: response} : response, {
|
||||||
typeof response === "string" ? {content: response} : response,
|
flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL,
|
||||||
{
|
allowedMentions: {
|
||||||
flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL,
|
repliedUser: false,
|
||||||
allowedMentions: {
|
},
|
||||||
repliedUser: false,
|
})
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await ack;
|
await ack;
|
||||||
|
@ -128,4 +113,4 @@ async function InteractionDispatcher(interaction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {InteractionDispatcher, getOption};
|
module.exports = {InteractionDispatcher};
|
||||||
|
|
|
@ -5,16 +5,7 @@ const BRIGHT = "[9";
|
||||||
const NORMAL_BG = "[4";
|
const NORMAL_BG = "[4";
|
||||||
const BRIGHT_BG = "[10";
|
const BRIGHT_BG = "[10";
|
||||||
|
|
||||||
const NAMES = [
|
const NAMES = ["Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"];
|
||||||
"Black",
|
|
||||||
"Red",
|
|
||||||
"Green",
|
|
||||||
"Yellow",
|
|
||||||
"Blue",
|
|
||||||
"Magenta",
|
|
||||||
"Cyan",
|
|
||||||
"White",
|
|
||||||
];
|
|
||||||
|
|
||||||
const COLORS = {};
|
const COLORS = {};
|
||||||
for (const index in NAMES) {
|
for (const index in NAMES) {
|
||||||
|
@ -73,6 +64,5 @@ function baseLogger(prefix, level, ...message) {
|
||||||
|
|
||||||
module.exports = {};
|
module.exports = {};
|
||||||
for (const level in LEVEL_COLORS) {
|
for (const level in LEVEL_COLORS) {
|
||||||
module.exports[level] = (prefix, ...message) =>
|
module.exports[level] = (prefix, ...message) => baseLogger.apply(null, [prefix, level, ...message]);
|
||||||
baseLogger.apply(null, [prefix, level, ...message]);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
hf.database.run(
|
hf.database.run("CREATE TABLE IF NOT EXISTS user_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID");
|
||||||
"CREATE TABLE IF NOT EXISTS user_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID"
|
|
||||||
);
|
|
||||||
|
|
||||||
function setUserData(id, key, value) {
|
function setUserData(id, key, value) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
440
src/lib/utils.js
440
src/lib/utils.js
|
@ -1,440 +0,0 @@
|
||||||
const {Collection} = require("@projectdysnomia/dysnomia");
|
|
||||||
const murmurhash = require("murmurhash").v3;
|
|
||||||
const {tinycolor} = require("@ctrl/tinycolor");
|
|
||||||
|
|
||||||
const logger = require("../lib/logger.js");
|
|
||||||
|
|
||||||
function pastelize(id) {
|
|
||||||
const hue = murmurhash(id) % 360;
|
|
||||||
const hex = tinycolor(`hsl(${hue},75%,60%)`).toHex();
|
|
||||||
return parseInt(hex, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatUsername(user) {
|
|
||||||
return user.discriminator && user.discriminator != "0"
|
|
||||||
? `${user.username}#${user.discriminator}`
|
|
||||||
: `@${user.username}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTopColor(msg, id, fallback = 0x7289da) {
|
|
||||||
if (!msg.guildID) return fallback;
|
|
||||||
const guild = msg.channel?.guild ?? hf.bot.guilds.get(msg.guildID);
|
|
||||||
if (!guild) return fallback;
|
|
||||||
|
|
||||||
const roles = guild.members
|
|
||||||
.get(id)
|
|
||||||
.roles.map((role) => guild.roles.get(role))
|
|
||||||
.filter((role) => role.color);
|
|
||||||
roles.sort((a, b) => b.position - a.position);
|
|
||||||
|
|
||||||
return roles[0]?.color || fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeString(string, newLines = true) {
|
|
||||||
string = string ? string.toString() : "";
|
|
||||||
string = string.replace(/`/g, "'");
|
|
||||||
string = string.replace(/<@/g, "<@\u200b");
|
|
||||||
string = string.replace(/<#/g, "<#\u200b");
|
|
||||||
string = string.replace(/<&/g, "<&\u200b");
|
|
||||||
if (newLines) string = string.replace(/\n/g, " ");
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTime(number) {
|
|
||||||
let seconds = parseInt(number) / 1000;
|
|
||||||
const days = Math.floor(seconds / 86400);
|
|
||||||
seconds = seconds % 86400;
|
|
||||||
const hours = Math.floor(seconds / 3600);
|
|
||||||
seconds = seconds % 3600;
|
|
||||||
const minutes = Math.floor(seconds / 60);
|
|
||||||
seconds = Math.floor(seconds % 60);
|
|
||||||
|
|
||||||
return (
|
|
||||||
(days !== 0 ? `${days.toString().padStart(2, "0")}:` : "") +
|
|
||||||
(hours !== 0 ? `${hours.toString().padStart(2, "0")}:` : "") +
|
|
||||||
`${minutes.toString().padStart(2, "0")}:${seconds
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function readableTime(number) {
|
|
||||||
const seconds = number / 1000;
|
|
||||||
const days = seconds / 60 / 60 / 24;
|
|
||||||
const years = days / 365.25;
|
|
||||||
|
|
||||||
if (years >= 1) {
|
|
||||||
return `${years.toFixed(2)} years`;
|
|
||||||
} else {
|
|
||||||
return `${days.toFixed(2)} days`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function isGif(url) {
|
|
||||||
const type = await fetch(url, {method: "HEAD"}).then((res) =>
|
|
||||||
res.headers.get("Content-Type")
|
|
||||||
);
|
|
||||||
return type == "image/gif";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function findLastImage(msg, gif = false) {
|
|
||||||
const messages = await msg.channel.getMessages(20);
|
|
||||||
let img;
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (message.attachments.size > 0) {
|
|
||||||
img = [...msg.attachments.values()][0].url;
|
|
||||||
if (gif && (await isGif(img))) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (message.embeds.length > 0) {
|
|
||||||
img = message.embeds[0]?.thumbnail?.url || message.embeds[0]?.image?.url;
|
|
||||||
if (img) {
|
|
||||||
if (gif && (await isGif(img))) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
if (!img) {
|
|
||||||
reject("Image not found in last 20 messages.");
|
|
||||||
} else {
|
|
||||||
resolve(img);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlRegex = /((https?):\/)?\/?([^:/\s]+)((\/\w+)*\/)([\w\-.]+)/;
|
|
||||||
|
|
||||||
async function getImage(msg, str) {
|
|
||||||
const refMsg = msg.referencedMessage;
|
|
||||||
if (refMsg) {
|
|
||||||
const attachments = [...refMsg.attachments.values()];
|
|
||||||
if (attachments[0]?.url) {
|
|
||||||
return attachments[0].url;
|
|
||||||
} else if (/<a?:[a-zA-Z0-9_]+:([0-9]+)>/.test(refMsg.content)) {
|
|
||||||
const id = refMsg.content.match(/<a?:[a-zA-Z0-9_]+:([0-9]+)>/)[1];
|
|
||||||
return `https://cdn.discordapp.com/emojis/${id}.png?v=1`;
|
|
||||||
} else if (/<@!?([0-9]+)>/.test(refMsg.content)) {
|
|
||||||
const id = refMsg.content.match(/<@!?([0-9]+)>/)[1];
|
|
||||||
const user = await hf.bot.requestHandler.request(
|
|
||||||
"GET",
|
|
||||||
"/users/" + id,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
if (user)
|
|
||||||
return `https://cdn.discordapp.com/avatars/${id}/${user.avatar}.png?size=1024`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const img = await findLastImage(msg, false);
|
|
||||||
if (!str) {
|
|
||||||
if (img) return img;
|
|
||||||
}
|
|
||||||
|
|
||||||
const attachments = [...msg.attachments.values()];
|
|
||||||
if (attachments[0]?.url) {
|
|
||||||
return attachments[0]?.url;
|
|
||||||
} else if (urlRegex.test(str)) {
|
|
||||||
return str;
|
|
||||||
} else if (/<a?:[a-zA-Z0-9_]+:([0-9]+)>/.test(str)) {
|
|
||||||
const id = str.match(/<a?:[a-zA-Z0-9_]+:([0-9]+)>/)[1];
|
|
||||||
return `https://cdn.discordapp.com/emojis/${id}.png?v=1`;
|
|
||||||
} else if (/<@!?([0-9]+)>/.test(str)) {
|
|
||||||
const id = str.match(/<@!?([0-9]+)>/)[1];
|
|
||||||
const user = await hf.bot.requestHandler.request(
|
|
||||||
"GET",
|
|
||||||
"/users/" + id,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
if (user)
|
|
||||||
return `https://cdn.discordapp.com/avatars/${id}/${user.avatar}.png?size=1024`;
|
|
||||||
} else if (img) {
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function hastebin(body) {
|
|
||||||
const res = await fetch(`${hf.config.haste_provider}/documents`, {
|
|
||||||
method: "POST",
|
|
||||||
body,
|
|
||||||
}).then((r) => r.json());
|
|
||||||
return `<${hf.config.haste_provider}/${res.key}>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
hf.selectionMessages = hf.selectionMessages ?? new Collection();
|
|
||||||
async function selectionMessage(
|
|
||||||
msg,
|
|
||||||
heading,
|
|
||||||
options,
|
|
||||||
timeout = 30000,
|
|
||||||
maxItems = 1
|
|
||||||
) {
|
|
||||||
const data = {
|
|
||||||
content: heading,
|
|
||||||
allowedMentions: {
|
|
||||||
repliedUser: false,
|
|
||||||
},
|
|
||||||
messageReference: {
|
|
||||||
messageID: msg.id,
|
|
||||||
},
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
type: 3,
|
|
||||||
custom_id: msg.id,
|
|
||||||
options: [],
|
|
||||||
max_values: maxItems,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
type: 2,
|
|
||||||
style: 4,
|
|
||||||
label: "Cancel",
|
|
||||||
custom_id: "cancel",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
options.slice(0, 25).forEach((value) => {
|
|
||||||
data.components[0].components[0].options.push({
|
|
||||||
label: value.display,
|
|
||||||
value: value.key,
|
|
||||||
description: value.description,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (options.length > 25) {
|
|
||||||
data.content += `\n\nDisplaying 25/${options.length} results`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayMessage = await msg.channel
|
|
||||||
.createMessage(data)
|
|
||||||
.catch((err) =>
|
|
||||||
logger.error(
|
|
||||||
"selectionMessage",
|
|
||||||
"Failed to create selection message: " + err
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
function listener(interaction) {
|
|
||||||
const user = interaction.member.user || interaction.user;
|
|
||||||
|
|
||||||
if (
|
|
||||||
user.id == msg.author.id &&
|
|
||||||
interaction.channel.id == msg.channel.id &&
|
|
||||||
interaction.message.components[0].components[0].custom_id == msg.id
|
|
||||||
) {
|
|
||||||
if (interaction.data.custom_id == "cancel") {
|
|
||||||
hf.events.remove("interactionCreate", `selection.${msg.id}`);
|
|
||||||
clearTimeout(hf.selectionMessages.get(msg.id));
|
|
||||||
hf.selectionMessages.delete(msg.id);
|
|
||||||
|
|
||||||
interaction.deferUpdate();
|
|
||||||
|
|
||||||
displayMessage.delete();
|
|
||||||
|
|
||||||
reject("Canceled");
|
|
||||||
} else {
|
|
||||||
hf.events.remove("interactionCreate", `selection.${msg.id}`);
|
|
||||||
clearTimeout(hf.selectionMessages.get(msg.id));
|
|
||||||
hf.selectionMessages.delete(msg.id);
|
|
||||||
|
|
||||||
interaction.deferUpdate();
|
|
||||||
|
|
||||||
displayMessage.delete();
|
|
||||||
|
|
||||||
let result;
|
|
||||||
|
|
||||||
if (maxItems > 1) {
|
|
||||||
result = options
|
|
||||||
.filter((opt) => interaction.data.values.includes(opt.key))
|
|
||||||
.map((opt) => opt.key);
|
|
||||||
} else {
|
|
||||||
result = options.filter(
|
|
||||||
(opt) => opt.key == interaction.data.values[0]
|
|
||||||
)[0].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hf.events.add("interactionCreate", `selection.${msg.id}`, listener);
|
|
||||||
hf.selectionMessages.set(
|
|
||||||
msg.id,
|
|
||||||
setTimeout(() => {
|
|
||||||
hf.events.remove("interactionCreate", `selection.${msg.id}`);
|
|
||||||
hf.selectionMessages.delete(msg.id);
|
|
||||||
|
|
||||||
reject("Request timed out");
|
|
||||||
}, timeout)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function lookupUser(msg, str, filter) {
|
|
||||||
if (/[0-9]{17,21}/.test(str)) {
|
|
||||||
return await hf.bot.requestHandler.request(
|
|
||||||
"GET",
|
|
||||||
"/users/" + str.match(/([0-9]{17,21})/)[1],
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let users;
|
|
||||||
if (filter) {
|
|
||||||
users = hf.bot.users.filter(filter).values();
|
|
||||||
} else if (msg.guildID) {
|
|
||||||
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
|
||||||
users = guild.members.values();
|
|
||||||
} else {
|
|
||||||
users = hf.bot.users.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/(.+?)#([0-9]{4})/.test(str)) {
|
|
||||||
const [_, name, discrim] = str.match(/(.+?)#([0-9]{4})/);
|
|
||||||
for (const user of users) {
|
|
||||||
if (
|
|
||||||
user.username.toLowerCase() == name.toLowerCase() &&
|
|
||||||
user.discriminator == discrim
|
|
||||||
) {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const selection = [];
|
|
||||||
for (const user of users) {
|
|
||||||
if (
|
|
||||||
user.username.toLowerCase() == str.toLowerCase() ||
|
|
||||||
(user.nickname && user.nickname == str.toLowerCase())
|
|
||||||
) {
|
|
||||||
return user;
|
|
||||||
} else if (
|
|
||||||
user.username.toLowerCase().indexOf(str.toLowerCase()) > -1 ||
|
|
||||||
(user.nick && user.nick.toLowerCase().indexOf(str.toLowerCase()) > -1) ||
|
|
||||||
(user.globalName &&
|
|
||||||
user.globalName.toLowerCase().indexOf(str.toLowerCase()) > -1)
|
|
||||||
) {
|
|
||||||
selection.push({
|
|
||||||
value: user,
|
|
||||||
key: user.id,
|
|
||||||
display: `${formatUsername(user)}${
|
|
||||||
user.nick
|
|
||||||
? ` (${user.nick})`
|
|
||||||
: user.globalName
|
|
||||||
? ` (${user.globalName})`
|
|
||||||
: ""
|
|
||||||
}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.sort((a, b) => a.display - b.display);
|
|
||||||
|
|
||||||
if (selection.length == 0) {
|
|
||||||
return "No results";
|
|
||||||
} else if (selection.length == 1) {
|
|
||||||
return selection[0].value;
|
|
||||||
} else {
|
|
||||||
selection.splice(20);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return await selectionMessage(
|
|
||||||
msg,
|
|
||||||
"Multiple users found, please pick from this list:",
|
|
||||||
selection
|
|
||||||
);
|
|
||||||
} catch (out) {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/39243641
|
|
||||||
const htmlEntities = {
|
|
||||||
nbsp: " ",
|
|
||||||
cent: "¢",
|
|
||||||
pound: "£",
|
|
||||||
yen: "¥",
|
|
||||||
euro: "€",
|
|
||||||
copy: "©",
|
|
||||||
reg: "®",
|
|
||||||
lt: "<",
|
|
||||||
gt: ">",
|
|
||||||
quot: '"',
|
|
||||||
amp: "&",
|
|
||||||
apos: "'",
|
|
||||||
};
|
|
||||||
|
|
||||||
function parseHtmlEntities(str) {
|
|
||||||
return str.replace(/&([^;]+);/g, function (entity, entityCode) {
|
|
||||||
let match;
|
|
||||||
|
|
||||||
if (entityCode in htmlEntities) {
|
|
||||||
return htmlEntities[entityCode];
|
|
||||||
} else if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
|
||||||
return String.fromCharCode(parseInt(match[1], 16));
|
|
||||||
} else if ((match = entityCode.match(/^#(\d+)$/))) {
|
|
||||||
return String.fromCharCode(~~match[1]);
|
|
||||||
} else {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const UPLOAD_LIMIT = 26214400;
|
|
||||||
const UPLOAD_LIMIT_TIER_2 = 52428800;
|
|
||||||
const UPLOAD_LIMIT_TIER_3 = 104857600;
|
|
||||||
|
|
||||||
function getUploadLimit(guild) {
|
|
||||||
if (!guild) return UPLOAD_LIMIT;
|
|
||||||
|
|
||||||
if (guild.premiumTier == 2) return UPLOAD_LIMIT_TIER_2;
|
|
||||||
if (guild.premiumTier == 3) return UPLOAD_LIMIT_TIER_3;
|
|
||||||
|
|
||||||
return UPLOAD_LIMIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TWITTER_EPOCH = 1288834974657;
|
|
||||||
const DISCORD_EPOCH = 1420070400000;
|
|
||||||
function snowflakeToTimestamp(snowflake, twitter = false) {
|
|
||||||
return (
|
|
||||||
Math.floor(Number(snowflake) / Math.pow(2, 22)) +
|
|
||||||
(twitter == true ? TWITTER_EPOCH : DISCORD_EPOCH)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
pastelize,
|
|
||||||
formatUsername,
|
|
||||||
getTopColor,
|
|
||||||
safeString,
|
|
||||||
formatTime,
|
|
||||||
readableTime,
|
|
||||||
isGif,
|
|
||||||
findLastImage,
|
|
||||||
getImage,
|
|
||||||
hastebin,
|
|
||||||
selectionMessage,
|
|
||||||
lookupUser,
|
|
||||||
parseHtmlEntities,
|
|
||||||
getUploadLimit,
|
|
||||||
snowflakeToTimestamp,
|
|
||||||
};
|
|
|
@ -9,28 +9,20 @@ if (hf.__anonradio_timeout) {
|
||||||
async function updateNowPlaying() {
|
async function updateNowPlaying() {
|
||||||
let playing;
|
let playing;
|
||||||
try {
|
try {
|
||||||
playing = await fetch("https://anonradio.net/playing").then((res) =>
|
playing = await fetch("https://anonradio.net/playing").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
try {
|
try {
|
||||||
playing = await fetch("http://anonradio.net/playing").then((res) =>
|
playing = await fetch("http://anonradio.net/playing").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let schedule;
|
let schedule;
|
||||||
try {
|
try {
|
||||||
schedule = await fetch("https://anonradio.net/schedule/").then((res) =>
|
schedule = await fetch("https://anonradio.net/schedule/").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
try {
|
try {
|
||||||
schedule = await fetch("http://anonradio.net/schedule/").then((res) =>
|
schedule = await fetch("http://anonradio.net/schedule/").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
@ -68,19 +60,14 @@ async function updateNowPlaying() {
|
||||||
} else if (playing.startsWith("Coming up")) {
|
} else if (playing.startsWith("Coming up")) {
|
||||||
title = playing;
|
title = playing;
|
||||||
} else {
|
} else {
|
||||||
const metadataLine = playing.match(
|
const metadataLine = playing.match(/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/);
|
||||||
/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/
|
|
||||||
);
|
|
||||||
const current = metadataLine?.[1] ?? "??";
|
const current = metadataLine?.[1] ?? "??";
|
||||||
const peakDay = metadataLine?.[2] ?? "??";
|
const peakDay = metadataLine?.[2] ?? "??";
|
||||||
const peakMonth = metadataLine?.[3] ?? "??";
|
const peakMonth = metadataLine?.[3] ?? "??";
|
||||||
const dj = metadataLine?.[4] ?? "unknown";
|
const dj = metadataLine?.[4] ?? "unknown";
|
||||||
const metadata = metadataLine?.[5] ?? "unknown";
|
const metadata = metadataLine?.[5] ?? "unknown";
|
||||||
|
|
||||||
if (
|
if (metadata == "https://archives.anonradio.net" || liveNow.name == "Synth Battle Royale") {
|
||||||
metadata == "https://archives.anonradio.net" ||
|
|
||||||
liveNow.name == "Synth Battle Royale"
|
|
||||||
) {
|
|
||||||
title = `${liveNow.name} (\`${dj}\`)`;
|
title = `${liveNow.name} (\`${dj}\`)`;
|
||||||
} else {
|
} else {
|
||||||
title = `${metadata} (\`${dj}\`)`;
|
title = `${metadata} (\`${dj}\`)`;
|
||||||
|
@ -93,24 +80,16 @@ async function updateNowPlaying() {
|
||||||
try {
|
try {
|
||||||
const icecast = await fetch("http://anonradio.net:8010/status-json.xsl")
|
const icecast = await fetch("http://anonradio.net:8010/status-json.xsl")
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((data) =>
|
.then((data) => JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",')));
|
||||||
JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",'))
|
const streamData = icecast.icestats.source.find((src) => src.listenurl == "http://anonradio.net:8010/openmic");
|
||||||
);
|
|
||||||
const streamData = icecast.icestats.source.find(
|
|
||||||
(src) => src.listenurl == "http://anonradio.net:8010/openmic"
|
|
||||||
);
|
|
||||||
if (streamData && streamData.stream_start_iso8601) {
|
if (streamData && streamData.stream_start_iso8601) {
|
||||||
const startTime = new Date(streamData.stream_start_iso8601).getTime();
|
const startTime = new Date(streamData.stream_start_iso8601).getTime();
|
||||||
title = `${streamData.title} (\`${
|
title = `${streamData.title} (\`${
|
||||||
streamData.server_name
|
streamData.server_name ? streamData.server_name + " | " + liveNow.id : liveNow.id
|
||||||
? streamData.server_name + " | " + liveNow.id
|
|
||||||
: liveNow.id
|
|
||||||
}\`)`;
|
}\`)`;
|
||||||
openmicTime = `-\\*- OpenMIC DJ has been streaming since <t:${Math.round(
|
openmicTime = `-\\*- OpenMIC DJ has been streaming since <t:${Math.round(startTime / 1000)}:R> -\\*-\n`;
|
||||||
startTime / 1000
|
|
||||||
)}:R> -\\*-\n`;
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +108,7 @@ async function updateNowPlaying() {
|
||||||
content: timestamp + "\n" + content,
|
content: timestamp + "\n" + content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,10 @@ const {resolve} = require("path");
|
||||||
const guildSettings = require("../lib/guildSettings.js");
|
const guildSettings = require("../lib/guildSettings.js");
|
||||||
|
|
||||||
function spawn(args) {
|
function spawn(args) {
|
||||||
const shell =
|
const shell = process.env.SHELL || (process.platform == "win32" ? "pwsh" : "sh");
|
||||||
process.env.SHELL || (process.platform == "win32" ? "powershell" : "sh");
|
|
||||||
|
|
||||||
const newArgs = [];
|
const newArgs = [];
|
||||||
if (shell.match(/powershell/i) && process.platform == "win32") {
|
if (shell.match(/pwsh/i) && process.platform == "win32") {
|
||||||
newArgs.push("-NoLogo", "-Command");
|
newArgs.push("-NoLogo", "-Command");
|
||||||
} else {
|
} else {
|
||||||
newArgs.push("-c");
|
newArgs.push("-c");
|
||||||
|
@ -52,16 +51,18 @@ reload.callback = function (msg, line) {
|
||||||
if (err.code == "MODULE_NOT_FOUND") {
|
if (err.code == "MODULE_NOT_FOUND") {
|
||||||
return "Module not found.";
|
return "Module not found.";
|
||||||
} else {
|
} else {
|
||||||
|
logger.info("hf:modules", `Failed to resolve "${line}": ${err}`);
|
||||||
return `:warning: An error occurred: \`\`\`\n${err}\`\`\``;
|
return `:warning: An error occurred: \`\`\`\n${err}\`\`\``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info("hf:modules", `Reloading module: '${line}'`);
|
logger.info("hf:modules", `Reloading module: "${line}"`);
|
||||||
delete require.cache[require.resolve(`./${line}.js`)];
|
delete require.cache[require.resolve(`./${line}.js`)];
|
||||||
require(`./${line}.js`);
|
require(`./${line}.js`);
|
||||||
return {reaction: "\uD83D\uDC4C"};
|
return {reaction: "\uD83D\uDC4C"};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
logger.info("hf:modules", `Failed to reload "${line}": ${err}`);
|
||||||
return `:warning: An error occurred: \`\`\`\n${err}\`\`\``;
|
return `:warning: An error occurred: \`\`\`\n${err}\`\`\``;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -96,10 +97,7 @@ _eval.callback = async function (msg, line) {
|
||||||
out = errored ? out : inspect(out, {depth: 0});
|
out = errored ? out : inspect(out, {depth: 0});
|
||||||
|
|
||||||
const token = hf.config.token;
|
const token = hf.config.token;
|
||||||
out = out.replace(
|
out = out.replace(new RegExp(token.replace(/\./g, "\\."), "g"), "lol no key 4 u");
|
||||||
new RegExp(token.replace(/\./g, "\\."), "g"),
|
|
||||||
"lol no key 4 u"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (errored) {
|
if (errored) {
|
||||||
return ":warning: Output (errored):\n```js\n" + out + "\n```";
|
return ":warning: Output (errored):\n```js\n" + out + "\n```";
|
||||||
|
@ -136,9 +134,7 @@ exec.callback = async function (msg, line) {
|
||||||
});
|
});
|
||||||
|
|
||||||
proc.on("close", async (code) => {
|
proc.on("close", async (code) => {
|
||||||
out += `\n\x1b[0m\x1b[1m====\x1b[0m\n\x1b[1m${
|
out += `\n\x1b[0m\x1b[1m====\x1b[0m\n\x1b[1m${code != 0 ? "\x1b[31m" : ""}Exited with ${code}\x1b[0m`;
|
||||||
code != 0 ? "\x1b[31m" : ""
|
|
||||||
}Exited with ${code}\x1b[0m`;
|
|
||||||
if (out.length > 1980) {
|
if (out.length > 1980) {
|
||||||
msg.channel.createMessage({
|
msg.channel.createMessage({
|
||||||
content: `Output too long to send in a message:`,
|
content: `Output too long to send in a message:`,
|
||||||
|
@ -188,10 +184,7 @@ settings.callback = async function (msg, line, [cmd, key, value]) {
|
||||||
.join("\n")}\n\`\`\``;
|
.join("\n")}\n\`\`\``;
|
||||||
}
|
}
|
||||||
case "enable": {
|
case "enable": {
|
||||||
if (
|
if (!msg.channel.permissionsOf(msg.author.id).has("manageGuild") && !hf.config.elevated.includes(msg.author.id))
|
||||||
!msg.channel.permissionsOf(msg.author.id).has("manageGuild") &&
|
|
||||||
!hf.config.elevated.includes(msg.author.id)
|
|
||||||
)
|
|
||||||
return "You do not have `Manage Server` permissions";
|
return "You do not have `Manage Server` permissions";
|
||||||
if (msg.author.bot) return "Zero-width space your say command.";
|
if (msg.author.bot) return "Zero-width space your say command.";
|
||||||
|
|
||||||
|
@ -204,10 +197,7 @@ settings.callback = async function (msg, line, [cmd, key, value]) {
|
||||||
return {reaction: "\uD83D\uDC4C"};
|
return {reaction: "\uD83D\uDC4C"};
|
||||||
}
|
}
|
||||||
case "disable": {
|
case "disable": {
|
||||||
if (
|
if (!msg.channel.permissionsOf(msg.author.id).has("manageGuild") && !hf.config.elevated.includes(msg.author.id))
|
||||||
!msg.channel.permissionsOf(msg.author.id).has("manageGuild") &&
|
|
||||||
!hf.config.elevated.includes(msg.author.id)
|
|
||||||
)
|
|
||||||
return "You do not have `Manage Server` permissions";
|
return "You do not have `Manage Server` permissions";
|
||||||
if (msg.author.bot) return "Zero-width space your say command.";
|
if (msg.author.bot) return "Zero-width space your say command.";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const {ApplicationCommandOptionTypes, MessageFlags} =
|
const {ApplicationCommandOptionTypes, MessageFlags} = require("../util/dconstants.js");
|
||||||
require("@projectdysnomia/dysnomia").Constants;
|
|
||||||
const InteractionCommand = require("../lib/interactionCommand.js");
|
const InteractionCommand = require("../lib/interactionCommand.js");
|
||||||
const {getOption} = require("../lib/interactionDispatcher.js");
|
|
||||||
|
|
||||||
const events = require("../lib/events.js");
|
const events = require("../lib/events.js");
|
||||||
const {hasFlag} = require("../lib/guildSettings.js");
|
const {hasFlag} = require("../lib/guildSettings.js");
|
||||||
|
@ -15,10 +13,7 @@ const REGEX_SPOILER = /(?:\s|^)\|\|([\s\S]+?)\|\|/;
|
||||||
|
|
||||||
function unindent(str) {
|
function unindent(str) {
|
||||||
str = str.replace(/\t/g, " ");
|
str = str.replace(/\t/g, " ");
|
||||||
const minIndent =
|
const minIndent = str.match(/^ *(?=\S)/gm)?.reduce((prev, curr) => Math.min(prev, curr.length), Infinity) ?? 0;
|
||||||
str
|
|
||||||
.match(/^ *(?=\S)/gm)
|
|
||||||
?.reduce((prev, curr) => Math.min(prev, curr.length), Infinity) ?? 0;
|
|
||||||
if (!minIndent) return str;
|
if (!minIndent) return str;
|
||||||
return str.replace(new RegExp(`^ {${minIndent}}`, "gm"), "");
|
return str.replace(new RegExp(`^ {${minIndent}}`, "gm"), "");
|
||||||
}
|
}
|
||||||
|
@ -44,12 +39,7 @@ const fileTypeAliases = {
|
||||||
"code-snippets": "json",
|
"code-snippets": "json",
|
||||||
};
|
};
|
||||||
|
|
||||||
async function processFile(
|
async function processFile(link, originalLink, spoiler = false, linkFile = false) {
|
||||||
link,
|
|
||||||
originalLink,
|
|
||||||
spoiler = false,
|
|
||||||
linkFile = false
|
|
||||||
) {
|
|
||||||
link = link.replaceAll("||", "").trim();
|
link = link.replaceAll("||", "").trim();
|
||||||
const res = await fetch(link);
|
const res = await fetch(link);
|
||||||
if (!res.ok) return "";
|
if (!res.ok) return "";
|
||||||
|
@ -63,10 +53,7 @@ async function processFile(
|
||||||
urlObj.pathname.lastIndexOf("/") + 1,
|
urlObj.pathname.lastIndexOf("/") + 1,
|
||||||
urlObj.pathname.length
|
urlObj.pathname.length
|
||||||
);
|
);
|
||||||
const fileType =
|
const fileType = fileName.lastIndexOf(".") == -1 ? "" : fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||||
fileName.lastIndexOf(".") == -1
|
|
||||||
? ""
|
|
||||||
: fileName.substring(fileName.lastIndexOf(".") + 1);
|
|
||||||
|
|
||||||
if (linkFile) {
|
if (linkFile) {
|
||||||
fileName = `[${fileName}](<${originalLink}>)`;
|
fileName = `[${fileName}](<${originalLink}>)`;
|
||||||
|
@ -101,9 +88,7 @@ async function processFile(
|
||||||
if (lines.length > 20) return "";
|
if (lines.length > 20) return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetLines = (
|
let targetLines = (entireFile ? lines : lines.slice(startLine - 1, endLine)).join("\n");
|
||||||
entireFile ? lines : lines.slice(startLine - 1, endLine)
|
|
||||||
).join("\n");
|
|
||||||
|
|
||||||
let warning = "";
|
let warning = "";
|
||||||
if (spoiler && targetLines.includes("||")) {
|
if (spoiler && targetLines.includes("||")) {
|
||||||
|
@ -115,11 +100,9 @@ async function processFile(
|
||||||
warning = " - :warning: Zero width spaces present";
|
warning = " - :warning: Zero width spaces present";
|
||||||
}
|
}
|
||||||
|
|
||||||
return `**${fileName}:** ${whichLines}${warning}\n${
|
return `**${fileName}:** ${whichLines}${warning}\n${spoiler ? "||" : ""}\`\`\`${
|
||||||
spoiler ? "||" : ""
|
fileTypeAliases[fileType] ?? fileType
|
||||||
}\`\`\`${fileTypeAliases[fileType] ?? fileType}\n${unindent(
|
}\n${unindent(targetLines)}\n\`\`\`${spoiler ? "||" : ""}`;
|
||||||
targetLines
|
|
||||||
)}\n\`\`\`${spoiler ? "||" : ""}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
events.add("messageCreate", "codePreviews", async function (msg) {
|
events.add("messageCreate", "codePreviews", async function (msg) {
|
||||||
|
@ -136,27 +119,21 @@ events.add("messageCreate", "codePreviews", async function (msg) {
|
||||||
if (githubLinks?.length) {
|
if (githubLinks?.length) {
|
||||||
for (const link of githubLinks) {
|
for (const link of githubLinks) {
|
||||||
const spoiler = REGEX_SPOILER.test(link);
|
const spoiler = REGEX_SPOILER.test(link);
|
||||||
files.push(
|
files.push(await processFile(link.replace("/blob/", "/raw/"), link, spoiler));
|
||||||
await processFile(link.replace("/blob/", "/raw/"), link, spoiler)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gitlabLinks?.length) {
|
if (gitlabLinks?.length) {
|
||||||
for (const link of gitlabLinks) {
|
for (const link of gitlabLinks) {
|
||||||
const spoiler = REGEX_SPOILER.test(link);
|
const spoiler = REGEX_SPOILER.test(link);
|
||||||
files.push(
|
files.push(await processFile(link.replace("/blob/", "/raw/"), link, spoiler));
|
||||||
await processFile(link.replace("/blob/", "/raw/"), link, spoiler)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (giteaLinks?.length) {
|
if (giteaLinks?.length) {
|
||||||
for (const link of giteaLinks) {
|
for (const link of giteaLinks) {
|
||||||
const spoiler = REGEX_SPOILER.test(link);
|
const spoiler = REGEX_SPOILER.test(link);
|
||||||
files.push(
|
files.push(await processFile(link.replace("/src/", "/raw/"), link, spoiler));
|
||||||
await processFile(link.replace("/src/", "/raw/"), link, spoiler)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +178,7 @@ events.add("messageCreate", "codePreviews", async function (msg) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const codepreviewsCommand = new InteractionCommand("codepreview");
|
const codepreviewsCommand = new InteractionCommand("codepreview");
|
||||||
codepreviewsCommand.helpText =
|
codepreviewsCommand.helpText = "Post snippets of codes from files on GitHub, Gitlab and Gitea instances.";
|
||||||
"Post snippets of codes from files on GitHub, Gitlab and Gitea instances.";
|
|
||||||
codepreviewsCommand.options.url = {
|
codepreviewsCommand.options.url = {
|
||||||
name: "url",
|
name: "url",
|
||||||
type: ApplicationCommandOptionTypes.STRING,
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
@ -218,8 +194,8 @@ codepreviewsCommand.options.spoiler = {
|
||||||
default: false,
|
default: false,
|
||||||
};
|
};
|
||||||
codepreviewsCommand.callback = async function (interaction) {
|
codepreviewsCommand.callback = async function (interaction) {
|
||||||
const url = getOption(interaction, codepreviewsCommand, "url");
|
const url = this.getOption(interaction, "url");
|
||||||
const spoiler = getOption(interaction, codepreviewsCommand, "spoiler");
|
const spoiler = this.getOption(interaction, "spoiler");
|
||||||
|
|
||||||
const githubOrGitlab = url.match(REGEX_GITHUB) ?? url.match(REGEX_GITLAB);
|
const githubOrGitlab = url.match(REGEX_GITHUB) ?? url.match(REGEX_GITLAB);
|
||||||
const gitea = url.match(REGEX_GITEA);
|
const gitea = url.match(REGEX_GITEA);
|
||||||
|
@ -238,8 +214,7 @@ codepreviewsCommand.callback = async function (interaction) {
|
||||||
|
|
||||||
if (out == "") {
|
if (out == "") {
|
||||||
return {
|
return {
|
||||||
content:
|
content: "No content was returned. Provided file is either too long, a markdown file, or not plaintext.",
|
||||||
"No content was returned. Provided file is either too long, a markdown file, or not plaintext.",
|
|
||||||
flags: MessageFlags.EPHEMERAL,
|
flags: MessageFlags.EPHEMERAL,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const Dysnomia = require("@projectdysnomia/dysnomia");
|
const {Message} = require("@projectdysnomia/dysnomia");
|
||||||
const {MessageFlags, ApplicationCommandOptionTypes, Permissions} =
|
const {MessageFlags, ApplicationCommandOptionTypes, Permissions} = require("../util/dconstants.js");
|
||||||
Dysnomia.Constants;
|
|
||||||
const fs = require("node:fs");
|
const fs = require("node:fs");
|
||||||
const path = require("node:path");
|
const path = require("node:path");
|
||||||
const httpSignature = require("@peertube/http-signature");
|
const httpSignature = require("@peertube/http-signature");
|
||||||
|
@ -8,22 +7,20 @@ const httpSignature = require("@peertube/http-signature");
|
||||||
const events = require("../lib/events.js");
|
const events = require("../lib/events.js");
|
||||||
const logger = require("../lib/logger.js");
|
const logger = require("../lib/logger.js");
|
||||||
const {hasFlag} = require("../lib/guildSettings.js");
|
const {hasFlag} = require("../lib/guildSettings.js");
|
||||||
const {parseHtmlEntities, getUploadLimit} = require("../lib/utils.js");
|
|
||||||
const InteractionCommand = require("../lib/interactionCommand.js");
|
const InteractionCommand = require("../lib/interactionCommand.js");
|
||||||
const {getOption} = require("../lib/interactionDispatcher.js");
|
|
||||||
|
|
||||||
const FRIENDLY_USERAGENT =
|
const {getUploadLimit} = require("../util/misc.js");
|
||||||
"HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)";
|
const {htmlToMarkdown} = require("../util/html.js");
|
||||||
|
|
||||||
const URLS_REGEX =
|
const FRIENDLY_USERAGENT = "HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)";
|
||||||
/(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g;
|
|
||||||
|
const URLS_REGEX = /(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g;
|
||||||
const SPOILER_REGEX = /(?:\s|^)\|\|([\s\S]+?)\|\|/;
|
const SPOILER_REGEX = /(?:\s|^)\|\|([\s\S]+?)\|\|/;
|
||||||
|
|
||||||
const PATH_REGEX = {
|
const PATH_REGEX = {
|
||||||
mastodon: /^\/@(.+?)\/(\d+)\/?/,
|
mastodon: /^\/@(.+?)\/(\d+)\/?/,
|
||||||
mastodon2: /^\/(.+?)\/statuses\/\d+\/?/,
|
mastodon2: /^\/(.+?)\/statuses\/\d+\/?/,
|
||||||
pleroma:
|
pleroma: /^\/objects\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?/,
|
||||||
/^\/objects\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?/,
|
|
||||||
pleroma2: /^\/notice\/[A-Za-z0-9]+\/?/,
|
pleroma2: /^\/notice\/[A-Za-z0-9]+\/?/,
|
||||||
misskey: /^\/notes\/[a-z0-9]+\/?/,
|
misskey: /^\/notes\/[a-z0-9]+\/?/,
|
||||||
gotosocial: /^\/@(.+?)\/statuses\/[0-9A-Z]+\/?/,
|
gotosocial: /^\/@(.+?)\/statuses\/[0-9A-Z]+\/?/,
|
||||||
|
@ -76,10 +73,7 @@ async function resolvePlatform(url) {
|
||||||
}).then((res) => res.json());
|
}).then((res) => res.json());
|
||||||
|
|
||||||
if (!nodeinfo?.software?.name) {
|
if (!nodeinfo?.software?.name) {
|
||||||
logger.error(
|
logger.error("fedimbed", `Got nodeinfo for "${urlObj.hostname}", but missing software name.`);
|
||||||
"fedimbed",
|
|
||||||
`Got nodeinfo for "${urlObj.hostname}", but missing software name.`
|
|
||||||
);
|
|
||||||
domainCache.set(urlObj.hostname, null);
|
domainCache.set(urlObj.hostname, null);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -89,9 +83,7 @@ async function resolvePlatform(url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyId = "https://hf.c7.pm/actor#main-key";
|
const keyId = "https://hf.c7.pm/actor#main-key";
|
||||||
const privKey = fs.readFileSync(
|
const privKey = fs.readFileSync(path.resolve(__dirname, "../../priv/private.pem"));
|
||||||
path.resolve(__dirname, "../../priv/private.pem")
|
|
||||||
);
|
|
||||||
async function signedFetch(url, options) {
|
async function signedFetch(url, options) {
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
|
|
||||||
|
@ -122,36 +114,6 @@ async function signedFetch(url, options) {
|
||||||
return await fetch(url, options);
|
return await fetch(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function htmlToMarkdown(str) {
|
|
||||||
// FIXME: stop being lazy and use an html parser
|
|
||||||
str = str.replace(/<a .*?href="([^"]+?)".*?>(.+?)<\/a>/gi, (_, url, text) =>
|
|
||||||
url == text ? url : `[${text}](${url})`
|
|
||||||
);
|
|
||||||
str = str.replace(
|
|
||||||
/<img .*?src="([^"]+?)".*?(alt|title)="([^"]+?)".*?\/>/gi,
|
|
||||||
"[$3]($1)"
|
|
||||||
);
|
|
||||||
str = str.replace(/<\/?\s*br\s*\/?>/gi, "\n");
|
|
||||||
str = str.replace(
|
|
||||||
/<blockquote.*?>((.|\n)*?)<\/blockquote>/gi,
|
|
||||||
(_, quote) => "> " + quote.split("\n").join("\n> ")
|
|
||||||
);
|
|
||||||
str = str.replace(/<\/p><p>/gi, "\n\n");
|
|
||||||
str = str.replace(/<ol>/gi, "\n");
|
|
||||||
str = str.replace(/<li>/gi, "- ");
|
|
||||||
str = str.replace(/<\/li>/gi, "\n");
|
|
||||||
str = str.replace(/<\/?code>/gi, "`");
|
|
||||||
str = str.replace(/<\/?em>/gi, "*");
|
|
||||||
str = str.replace(/<\/?u>/gi, "__");
|
|
||||||
str = str.replace(/<\/?s>/gi, "~~");
|
|
||||||
str = str.replace(/(<([^>]+)>)/gi, "");
|
|
||||||
str = parseHtmlEntities(str);
|
|
||||||
// whyyyyyyyyyyyy
|
|
||||||
str = str.replace(/\[https?:\/\/.+?\]\((https?:\/\/.+?)\)/gi, "$1");
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processUrl(msg, url, spoiler = false) {
|
async function processUrl(msg, url, spoiler = false) {
|
||||||
let invalidUrl = false;
|
let invalidUrl = false;
|
||||||
let urlObj;
|
let urlObj;
|
||||||
|
@ -200,10 +162,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
},
|
},
|
||||||
}).then((res) => res.text());
|
}).then((res) => res.text());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error("fedimbed", `Failed to signed fetch "${url}", retrying unsigned: ${err}`);
|
||||||
"fedimbed",
|
|
||||||
`Failed to signed fetch "${url}", retrying unsigned: ${err}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!rawPostData) {
|
if (!rawPostData) {
|
||||||
try {
|
try {
|
||||||
|
@ -223,10 +182,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
try {
|
try {
|
||||||
postData = JSON.parse(rawPostData);
|
postData = JSON.parse(rawPostData);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error("fedimbed", `Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"`);
|
||||||
"fedimbed",
|
|
||||||
`Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn("fedimbed", `Got non-JSON for "${url}": ${rawPostData}`);
|
logger.warn("fedimbed", `Got non-JSON for "${url}": ${rawPostData}`);
|
||||||
|
@ -280,18 +236,13 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
options.body = JSON.stringify({noteId});
|
options.body = JSON.stringify({noteId});
|
||||||
headers["Content-Type"] = "application/json";
|
headers["Content-Type"] = "application/json";
|
||||||
} else {
|
} else {
|
||||||
logger.error(
|
logger.error("fedimbed", `Missing MastoAPI replacement for "${platform}"`);
|
||||||
"fedimbed",
|
|
||||||
`Missing MastoAPI replacement for "${platform}"`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirUrl) {
|
if (redirUrl) {
|
||||||
logger.verbose(
|
logger.verbose(
|
||||||
"fedimbed",
|
"fedimbed",
|
||||||
`Redirecting "${url}" to "${redirUrl}": ${JSON.stringify(
|
`Redirecting "${url}" to "${redirUrl}": ${JSON.stringify(options)}, ${JSON.stringify(headers)}`
|
||||||
options
|
|
||||||
)}, ${JSON.stringify(headers)}`
|
|
||||||
);
|
);
|
||||||
let rawPostData2;
|
let rawPostData2;
|
||||||
try {
|
try {
|
||||||
|
@ -304,10 +255,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
})
|
})
|
||||||
).then((res) => res.text());
|
).then((res) => res.text());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error("fedimbed", `Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}`);
|
||||||
"fedimbed",
|
|
||||||
`Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!rawPostData2) {
|
if (!rawPostData2) {
|
||||||
try {
|
try {
|
||||||
|
@ -320,10 +268,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
})
|
})
|
||||||
).then((res) => res.text());
|
).then((res) => res.text());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error("fedimbed", `Failed to fetch "${url}" via MastoAPI: ${err}`);
|
||||||
"fedimbed",
|
|
||||||
`Failed to fetch "${url}" via MastoAPI: ${err}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,27 +276,18 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
if (rawPostData2?.startsWith("{")) {
|
if (rawPostData2?.startsWith("{")) {
|
||||||
postData2 = JSON.parse(rawPostData2);
|
postData2 = JSON.parse(rawPostData2);
|
||||||
} else {
|
} else {
|
||||||
logger.warn(
|
logger.warn("fedimbed", `Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}`);
|
||||||
"fedimbed",
|
|
||||||
`Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!postData2) {
|
if (!postData2) {
|
||||||
logger.warn(
|
logger.warn("fedimbed", `Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.`);
|
||||||
"fedimbed",
|
|
||||||
`Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.`
|
|
||||||
);
|
|
||||||
} else if (postData2.error) {
|
} else if (postData2.error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"fedimbed",
|
"fedimbed",
|
||||||
`Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify(
|
`Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify(postData2.error)}`
|
||||||
postData2.error
|
|
||||||
)}`
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
cw =
|
cw = postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw;
|
||||||
postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw;
|
|
||||||
content =
|
content =
|
||||||
postData2.akkoma?.source?.content ??
|
postData2.akkoma?.source?.content ??
|
||||||
postData2.pleroma?.content?.["text/plain"] ??
|
postData2.pleroma?.content?.["text/plain"] ??
|
||||||
|
@ -364,21 +300,12 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
postData2.user?.name ??
|
postData2.user?.name ??
|
||||||
postData2.user?.username,
|
postData2.user?.username,
|
||||||
handle:
|
handle:
|
||||||
postData2.account?.fqn ??
|
postData2.account?.fqn ?? `${postData2.account?.username ?? postData2.user?.username}@${urlObj.hostname}`,
|
||||||
`${postData2.account?.username ?? postData2.user?.username}@${
|
url: postData2.account?.url ?? `${urlObj.origin}/@${postData2.account?.username ?? postData2.user?.username}`,
|
||||||
urlObj.hostname
|
|
||||||
}`,
|
|
||||||
url:
|
|
||||||
postData2.account?.url ??
|
|
||||||
`${urlObj.origin}/@${
|
|
||||||
postData2.account?.username ?? postData2.user?.username
|
|
||||||
}`,
|
|
||||||
avatar: postData2.account?.avatar ?? postData2.user?.avatarUrl,
|
avatar: postData2.account?.avatar ?? postData2.user?.avatarUrl,
|
||||||
};
|
};
|
||||||
timestamp = postData2.created_at ?? postData2.createdAt;
|
timestamp = postData2.created_at ?? postData2.createdAt;
|
||||||
emotes = postData2.emojis
|
emotes = postData2.emojis.filter((x) => !x.name.endsWith("@.")).map((x) => ({name: `:${x.name}:`, url: x.url}));
|
||||||
.filter((x) => !x.name.endsWith("@."))
|
|
||||||
.map((x) => ({name: `:${x.name}:`, url: x.url}));
|
|
||||||
sensitive = postData2.sensitive;
|
sensitive = postData2.sensitive;
|
||||||
|
|
||||||
const attachments = postData2.media_attachments ?? postData2.files;
|
const attachments = postData2.media_attachments ?? postData2.files;
|
||||||
|
@ -465,15 +392,12 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
if (realUrlObj.origin != urlObj.origin) {
|
if (realUrlObj.origin != urlObj.origin) {
|
||||||
platform = await resolvePlatform(postData.id);
|
platform = await resolvePlatform(postData.id);
|
||||||
color = PLATFORM_COLORS[platform];
|
color = PLATFORM_COLORS[platform];
|
||||||
platformName = platform
|
platformName = platform.replace("gotosocial", "GoToSocial").replace(/^(.)/, (_, c) => c.toUpperCase());
|
||||||
.replace("gotosocial", "GoToSocial")
|
|
||||||
.replace(/^(.)/, (_, c) => c.toUpperCase());
|
|
||||||
url = postData.id;
|
url = postData.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content =
|
content = postData._misskey_content ?? postData.source?.content ?? postData.content;
|
||||||
postData._misskey_content ?? postData.source?.content ?? postData.content;
|
|
||||||
cw = postData.summary;
|
cw = postData.summary;
|
||||||
timestamp = postData.published;
|
timestamp = postData.published;
|
||||||
sensitive = postData.sensitive;
|
sensitive = postData.sensitive;
|
||||||
|
@ -482,36 +406,29 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
let tag = postData.tag;
|
let tag = postData.tag;
|
||||||
// gts moment
|
// gts moment
|
||||||
if (!Array.isArray(tag)) tag = [tag];
|
if (!Array.isArray(tag)) tag = [tag];
|
||||||
emotes = tag
|
emotes = tag.filter((x) => !!x.icon).map((x) => ({name: x.name, url: x.icon.url}));
|
||||||
.filter((x) => !!x.icon)
|
|
||||||
.map((x) => ({name: x.name, url: x.icon.url}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: gts doesnt send singular attachments as array
|
// NB: gts doesnt send singular attachments as array
|
||||||
const attachments = Array.isArray(postData.attachment)
|
const attachments = Array.isArray(postData.attachment) ? postData.attachment : [postData.attachment];
|
||||||
? postData.attachment
|
|
||||||
: [postData.attachment];
|
|
||||||
for (const attachment of attachments) {
|
for (const attachment of attachments) {
|
||||||
if (attachment.mediaType) {
|
if (attachment.mediaType) {
|
||||||
if (attachment.mediaType.startsWith("video/")) {
|
if (attachment.mediaType.startsWith("video/")) {
|
||||||
videos.push({
|
videos.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: attachment.mediaType,
|
type: attachment.mediaType,
|
||||||
});
|
});
|
||||||
} else if (attachment.mediaType.startsWith("image/")) {
|
} else if (attachment.mediaType.startsWith("image/")) {
|
||||||
images.push({
|
images.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: attachment.mediaType,
|
type: attachment.mediaType,
|
||||||
});
|
});
|
||||||
} else if (attachment.mediaType.startsWith("audio/")) {
|
} else if (attachment.mediaType.startsWith("audio/")) {
|
||||||
audios.push({
|
audios.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: attachment.mediaType,
|
type: attachment.mediaType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -524,22 +441,19 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
if (contentType.startsWith("image/")) {
|
if (contentType.startsWith("image/")) {
|
||||||
images.push({
|
images.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: contentType,
|
type: contentType,
|
||||||
});
|
});
|
||||||
} else if (contentType.startsWith("video/")) {
|
} else if (contentType.startsWith("video/")) {
|
||||||
videos.push({
|
videos.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: contentType,
|
type: contentType,
|
||||||
});
|
});
|
||||||
} else if (contentType.startsWith("audio/")) {
|
} else if (contentType.startsWith("audio/")) {
|
||||||
audios.push({
|
audios.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: contentType,
|
type: contentType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -551,39 +465,29 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
? type
|
? type
|
||||||
: type +
|
: type +
|
||||||
"/" +
|
"/" +
|
||||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image"
|
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" ? "png" : type == "video" ? "mp4" : "mpeg");
|
||||||
? "png"
|
|
||||||
: type == "video"
|
|
||||||
? "mp4"
|
|
||||||
: "mpeg");
|
|
||||||
if (type.startsWith("image")) {
|
if (type.startsWith("image")) {
|
||||||
images.push({
|
images.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: fileType,
|
type: fileType,
|
||||||
});
|
});
|
||||||
} else if (type.startsWith("video")) {
|
} else if (type.startsWith("video")) {
|
||||||
videos.push({
|
videos.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: fileType,
|
type: fileType,
|
||||||
});
|
});
|
||||||
} else if (type.startsWith("audio")) {
|
} else if (type.startsWith("audio")) {
|
||||||
audios.push({
|
audios.push({
|
||||||
url: attachment.url,
|
url: attachment.url,
|
||||||
desc:
|
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||||
attachment.name ?? attachment.description ?? attachment.comment,
|
|
||||||
type: fileType,
|
type: fileType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn(
|
logger.warn("fedimbed", `Unhandled attachment structure! ${JSON.stringify(attachment)}`);
|
||||||
"fedimbed",
|
|
||||||
`Unhandled attachment structure! ${JSON.stringify(attachment)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,30 +503,23 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
images.push({
|
images.push({
|
||||||
url: postData.image.url,
|
url: postData.image.url,
|
||||||
desc: "",
|
desc: "",
|
||||||
type:
|
type: contentType ?? "image/" + imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1),
|
||||||
contentType ??
|
|
||||||
"image/" +
|
|
||||||
imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postData.name) title = postData.name;
|
if (postData.name) title = postData.name;
|
||||||
|
|
||||||
// Author data is not sent with the post with AS2
|
// Author data is not sent with the post with AS2
|
||||||
const authorData = await signedFetch(
|
const authorData = await signedFetch(postData.actor ?? postData.attributedTo, {
|
||||||
postData.actor ?? postData.attributedTo,
|
headers: {
|
||||||
{
|
"User-Agent": FRIENDLY_USERAGENT,
|
||||||
headers: {
|
Accept: "application/activity+json",
|
||||||
"User-Agent": FRIENDLY_USERAGENT,
|
},
|
||||||
Accept: "application/activity+json",
|
})
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
// only posts can be activity+json right now, reduce log spam
|
// only posts can be activity+json right now, reduce log spam
|
||||||
if (platform !== "cohost")
|
if (platform !== "cohost") logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);
|
||||||
logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (authorData) {
|
if (authorData) {
|
||||||
|
@ -637,9 +534,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
// bootleg author, mainly for cohost
|
// bootleg author, mainly for cohost
|
||||||
const authorUrl = postData.actor ?? postData.attributedTo;
|
const authorUrl = postData.actor ?? postData.attributedTo;
|
||||||
const authorUrlObj = new URL(authorUrl);
|
const authorUrlObj = new URL(authorUrl);
|
||||||
const name = authorUrlObj.pathname.substring(
|
const name = authorUrlObj.pathname.substring(authorUrlObj.pathname.lastIndexOf("/") + 1);
|
||||||
authorUrlObj.pathname.lastIndexOf("/") + 1
|
|
||||||
);
|
|
||||||
author = {
|
author = {
|
||||||
name,
|
name,
|
||||||
handle: `${name}@${authorUrlObj.hostname}`,
|
handle: `${name}@${authorUrlObj.hostname}`,
|
||||||
|
@ -661,10 +556,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
|
|
||||||
// We could just continue without author but it'd look ugly and be confusing.
|
// We could just continue without author but it'd look ugly and be confusing.
|
||||||
if (!author) {
|
if (!author) {
|
||||||
logger.warn(
|
logger.warn("fedimbed", `Bailing trying to re-embed "${url}": Failed to get author.`);
|
||||||
"fedimbed",
|
|
||||||
`Bailing trying to re-embed "${url}": Failed to get author.`
|
|
||||||
);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,12 +574,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
|
|
||||||
let desc = "";
|
let desc = "";
|
||||||
let MAX_LENGTH = 3999;
|
let MAX_LENGTH = 3999;
|
||||||
if (
|
if ((cw != "" || sensitive) && images.length == 0 && videos.length == 0 && audios.length == 0) {
|
||||||
(cw != "" || sensitive) &&
|
|
||||||
images.length == 0 &&
|
|
||||||
videos.length == 0 &&
|
|
||||||
audios.length == 0
|
|
||||||
) {
|
|
||||||
desc += "||" + content + "||";
|
desc += "||" + content + "||";
|
||||||
MAX_LENGTH -= 4;
|
MAX_LENGTH -= 4;
|
||||||
|
|
||||||
|
@ -708,9 +595,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = author.name
|
const user = author.name ? `${author.name} (${author.handle})` : author.handle;
|
||||||
? `${author.name} (${author.handle})`
|
|
||||||
: author.handle;
|
|
||||||
|
|
||||||
const baseEmbed = {
|
const baseEmbed = {
|
||||||
color,
|
color,
|
||||||
|
@ -734,9 +619,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
};
|
};
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
if (images.length > 1) {
|
if (images.length > 1) {
|
||||||
const links = images
|
const links = images.map((attachment, index) => `[Image ${index + 1}](${attachment.url})`).join(" | ");
|
||||||
.map((attachment, index) => `[Image ${index + 1}](${attachment.url})`)
|
|
||||||
.join(" | ");
|
|
||||||
|
|
||||||
if (links.length <= 1024)
|
if (links.length <= 1024)
|
||||||
baseEmbed.fields.push({
|
baseEmbed.fields.push({
|
||||||
|
@ -756,9 +639,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
if (videos.length > 1) {
|
if (videos.length > 1) {
|
||||||
baseEmbed.fields.push({
|
baseEmbed.fields.push({
|
||||||
name: "Videos",
|
name: "Videos",
|
||||||
value: videos
|
value: videos.map((attachment, index) => `[Video ${index + 1}](${attachment.url})`).join(" | "),
|
||||||
.map((attachment, index) => `[Video ${index + 1}](${attachment.url})`)
|
|
||||||
.join(" | "),
|
|
||||||
inline: true,
|
inline: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -773,9 +654,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
if (audios.length > 1) {
|
if (audios.length > 1) {
|
||||||
baseEmbed.fields.push({
|
baseEmbed.fields.push({
|
||||||
name: "Audios",
|
name: "Audios",
|
||||||
value: audios
|
value: audios.map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`).join(" | "),
|
||||||
.map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`)
|
|
||||||
.join(" | "),
|
|
||||||
inline: true,
|
inline: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -796,29 +675,24 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
const percent = o.count / poll.total;
|
const percent = o.count / poll.total;
|
||||||
const bar = Math.round(percent * 30);
|
const bar = Math.round(percent * 30);
|
||||||
|
|
||||||
return `**${o.name}** (${o.count}, ${Math.round(
|
return `**${o.name}** (${o.count}, ${Math.round(percent * 100)}%)\n\`[${"=".repeat(bar)}${" ".repeat(
|
||||||
percent * 100
|
30 - bar
|
||||||
)}%)\n\`[${"=".repeat(bar)}${" ".repeat(30 - bar)}]\``;
|
)}]\``;
|
||||||
})
|
})
|
||||||
.join("\n\n") +
|
.join("\n\n") + `\n\n${poll.total} votes \u2022 Ends <t:${Math.floor(poll.end.getTime() / 1000)}:R>`,
|
||||||
`\n\n${poll.total} votes \u2022 Ends <t:${Math.floor(
|
|
||||||
poll.end.getTime() / 1000
|
|
||||||
)}:R>`,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let sendWait = false;
|
let sendWait = false;
|
||||||
if (videos.length > 0 || audios.length > 0 || images.length > 4) {
|
if (videos.length > 0 || audios.length > 0 || images.length > 4) {
|
||||||
sendWait = true;
|
sendWait = true;
|
||||||
if (msg instanceof Dysnomia.Message) await msg.addReaction("\uD83D\uDCE4");
|
if (msg instanceof Message) await msg.addReaction("\uD83D\uDCE4");
|
||||||
}
|
}
|
||||||
|
|
||||||
const embeds = [];
|
const embeds = [];
|
||||||
const files = [];
|
const files = [];
|
||||||
|
|
||||||
const guild =
|
const guild = msg.channel?.guild ?? (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false);
|
||||||
msg.channel?.guild ??
|
|
||||||
(msg.guildID ? hf.bot.guilds.get(msg.guildID) : false);
|
|
||||||
|
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
if (images.length <= 4) {
|
if (images.length <= 4) {
|
||||||
|
@ -852,9 +726,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||||
(attachment.type.indexOf("/") > -1
|
(attachment.type.indexOf("/") > -1
|
||||||
? attachment.type.replace("/", ".")
|
? attachment.type.replace("/", ".")
|
||||||
: attachment.type +
|
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
||||||
"." +
|
|
||||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
|
||||||
file,
|
file,
|
||||||
description: attachment.desc,
|
description: attachment.desc,
|
||||||
});
|
});
|
||||||
|
@ -886,9 +758,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||||
(attachment.type.indexOf("/") > -1
|
(attachment.type.indexOf("/") > -1
|
||||||
? attachment.type.replace("/", ".")
|
? attachment.type.replace("/", ".")
|
||||||
: attachment.type +
|
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
||||||
"." +
|
|
||||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
|
||||||
file,
|
file,
|
||||||
description: attachment.desc,
|
description: attachment.desc,
|
||||||
});
|
});
|
||||||
|
@ -959,9 +829,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||||
(attachment.type.indexOf("/") > -1
|
(attachment.type.indexOf("/") > -1
|
||||||
? attachment.type.replace("/", ".").replace("quicktime", "mov")
|
? attachment.type.replace("/", ".").replace("quicktime", "mov")
|
||||||
: attachment.type +
|
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")),
|
||||||
"." +
|
|
||||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")),
|
|
||||||
file,
|
file,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -989,14 +857,8 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
filename:
|
filename:
|
||||||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||||
(attachment.type.indexOf("/") > -1
|
(attachment.type.indexOf("/") > -1
|
||||||
? attachment.type
|
? attachment.type.replace("/", ".").replace("mpeg", "mp3").replace("vnd.wave", "wav").replace("x-", "")
|
||||||
.replace("/", ".")
|
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")),
|
||||||
.replace("mpeg", "mp3")
|
|
||||||
.replace("vnd.wave", "wav")
|
|
||||||
.replace("x-", "")
|
|
||||||
: attachment.type +
|
|
||||||
"." +
|
|
||||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")),
|
|
||||||
file,
|
file,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1006,8 +868,7 @@ async function processUrl(msg, url, spoiler = false) {
|
||||||
return {
|
return {
|
||||||
response: {
|
response: {
|
||||||
content:
|
content:
|
||||||
cw != "" &&
|
cw != "" && (images.length > 0 || videos.length > 0 || audios.length > 0)
|
||||||
(images.length > 0 || videos.length > 0 || audios.length > 0)
|
|
||||||
? `:warning: ${cw} || ${url} ||`
|
? `:warning: ${cw} || ${url} ||`
|
||||||
: spoiler
|
: spoiler
|
||||||
? `|| ${url} ||`
|
? `|| ${url} ||`
|
||||||
|
@ -1051,10 +912,7 @@ events.add("messageCreate", "fedimbed", async function (msg) {
|
||||||
for (const service of Object.keys(PATH_REGEX)) {
|
for (const service of Object.keys(PATH_REGEX)) {
|
||||||
const regex = PATH_REGEX[service];
|
const regex = PATH_REGEX[service];
|
||||||
if (urlObj && regex.test(urlObj.pathname)) {
|
if (urlObj && regex.test(urlObj.pathname)) {
|
||||||
logger.verbose(
|
logger.verbose("fedimbed", `Hit "${service}" for "${url}", processing now.`);
|
||||||
"fedimbed",
|
|
||||||
`Hit "${service}" for "${url}", processing now.`
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
const {response, sendWait} = await processUrl(msg, url, hasSpoiler);
|
const {response, sendWait} = await processUrl(msg, url, hasSpoiler);
|
||||||
await msg.channel.createMessage(response).then(() => {
|
await msg.channel.createMessage(response).then(() => {
|
||||||
|
@ -1067,10 +925,7 @@ events.add("messageCreate", "fedimbed", async function (msg) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error("fedimbed", `Error processing "${url}":\n` + err.stack);
|
||||||
"fedimbed",
|
|
||||||
`Error processing "${url}":\n` + err.stack
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1080,8 +935,7 @@ events.add("messageCreate", "fedimbed", async function (msg) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const fedimbedCommand = new InteractionCommand("fedimbed");
|
const fedimbedCommand = new InteractionCommand("fedimbed");
|
||||||
fedimbedCommand.helpText =
|
fedimbedCommand.helpText = "Better embeds for fediverse (Mastodon, Pleroma, etc) posts";
|
||||||
"Better embeds for fediverse (Mastodon, Pleroma, etc) posts";
|
|
||||||
fedimbedCommand.options.url = {
|
fedimbedCommand.options.url = {
|
||||||
name: "url",
|
name: "url",
|
||||||
type: ApplicationCommandOptionTypes.STRING,
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
@ -1098,8 +952,8 @@ fedimbedCommand.options.spoiler = {
|
||||||
};
|
};
|
||||||
fedimbedCommand.permissions = Permissions.embedLinks | Permissions.attachFiles;
|
fedimbedCommand.permissions = Permissions.embedLinks | Permissions.attachFiles;
|
||||||
fedimbedCommand.callback = async function (interaction) {
|
fedimbedCommand.callback = async function (interaction) {
|
||||||
let url = getOption(interaction, fedimbedCommand, "url");
|
let url = this.getOption(interaction, "url");
|
||||||
const spoiler = getOption(interaction, fedimbedCommand, "spoiler");
|
const spoiler = this.getOption(interaction, "spoiler");
|
||||||
|
|
||||||
url = url
|
url = url
|
||||||
.replace(/\|/g, "")
|
.replace(/\|/g, "")
|
||||||
|
|
|
@ -5,16 +5,15 @@ const CATEGORY = "misc";
|
||||||
const FOXWELLS_GUILD_ID = "300436792916836352";
|
const FOXWELLS_GUILD_ID = "300436792916836352";
|
||||||
|
|
||||||
const {tinycolor} = require("@ctrl/tinycolor");
|
const {tinycolor} = require("@ctrl/tinycolor");
|
||||||
const {pastelize} = require("../lib/utils.js");
|
const {pastelize} = require("../util/misc.js");
|
||||||
|
const {createBoardMessage} = require("../util/starboard.js");
|
||||||
|
|
||||||
const logger = require("../lib/logger.js");
|
const logger = require("../lib/logger.js");
|
||||||
|
|
||||||
// taken from rot13.com
|
// taken from rot13.com
|
||||||
function rot(s, i) {
|
function rot(s, i) {
|
||||||
return s.replace(/[a-zA-Z]/g, function (c) {
|
return s.replace(/[a-zA-Z]/g, function (c) {
|
||||||
return String.fromCharCode(
|
return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + i) ? c : c - 26);
|
||||||
(c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + i) ? c : c - 26
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,9 +41,7 @@ utsuholights.callback = async function (msg, line, [hex, bri]) {
|
||||||
cachedLightsURL = Buffer.from(rot(LIGHTS_URL, 13), "base64").toString(); // Wow! It's That Easy!
|
cachedLightsURL = Buffer.from(rot(LIGHTS_URL, 13), "base64").toString(); // Wow! It's That Easy!
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(`${cachedLightsURL}?r=${r}&g=${g}&b=${b}${bri ? `&bri=${bri}` : ""}`);
|
||||||
`${cachedLightsURL}?r=${r}&g=${g}&b=${b}${bri ? `&bri=${bri}` : ""}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
return {reaction: "\uD83D\uDC4C"};
|
return {reaction: "\uD83D\uDC4C"};
|
||||||
|
@ -54,17 +51,11 @@ utsuholights.callback = async function (msg, line, [hex, bri]) {
|
||||||
};
|
};
|
||||||
//hf.registerCommand(utsuholights);
|
//hf.registerCommand(utsuholights);
|
||||||
|
|
||||||
const JPEG_HEADER = Buffer.from([
|
const JPEG_HEADER = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46]);
|
||||||
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46,
|
|
||||||
]);
|
|
||||||
|
|
||||||
async function fetchPlant() {
|
async function fetchPlant() {
|
||||||
const res = await fetch("https://nuclear.utsuho.rocks/plant/");
|
const res = await fetch("https://nuclear.utsuho.rocks/plant/");
|
||||||
const boundary = res.headers
|
const boundary = res.headers.get("Content-Type").split(";")[1].trim().replace("boundary=", "--");
|
||||||
.get("Content-Type")
|
|
||||||
.split(";")[1]
|
|
||||||
.trim()
|
|
||||||
.replace("boundary=", "--");
|
|
||||||
let buffer = Buffer.alloc(0);
|
let buffer = Buffer.alloc(0);
|
||||||
|
|
||||||
let searchForNextBoundary = false;
|
let searchForNextBoundary = false;
|
||||||
|
@ -78,9 +69,7 @@ async function fetchPlant() {
|
||||||
} else if (searchForNextBoundary) {
|
} else if (searchForNextBoundary) {
|
||||||
if (data.toString().startsWith(boundary)) {
|
if (data.toString().startsWith(boundary)) {
|
||||||
res.body.end();
|
res.body.end();
|
||||||
const length = Number(
|
const length = Number(buffer.toString().match(/Content-Length:.*?(\d+)/)[1]);
|
||||||
buffer.toString().match(/Content-Length:.*?(\d+)/)[1]
|
|
||||||
);
|
|
||||||
const headerOffset = buffer.indexOf(JPEG_HEADER);
|
const headerOffset = buffer.indexOf(JPEG_HEADER);
|
||||||
const data = buffer.slice(headerOffset, headerOffset + length);
|
const data = buffer.slice(headerOffset, headerOffset + length);
|
||||||
|
|
||||||
|
@ -169,127 +158,6 @@ function deleteBoardEntry(id) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findSuitableImage(msg) {
|
|
||||||
const out = {};
|
|
||||||
|
|
||||||
const attachments = [...msg.attachments.values()];
|
|
||||||
const attachment = attachments[0];
|
|
||||||
|
|
||||||
if (attachment) {
|
|
||||||
const url = attachment.url;
|
|
||||||
const parsed = new URL(url);
|
|
||||||
if (/(jpe?g|png|gif|webp)$/.test(parsed.pathname)) {
|
|
||||||
out.url = url;
|
|
||||||
} else if (/(mp4|webm|mov)$/.test(parsed.pathname)) {
|
|
||||||
out.video = true;
|
|
||||||
out.attachment = true;
|
|
||||||
out.url = "attachment://thumb.jpg";
|
|
||||||
|
|
||||||
let thumbUrl = attachment.proxyURL;
|
|
||||||
if (thumbUrl.includes("media.discordapp.net") && thumbUrl.includes("?")) {
|
|
||||||
if (thumbUrl.endsWith("&")) {
|
|
||||||
thumbUrl = thumbUrl += "format=jpeg";
|
|
||||||
} else {
|
|
||||||
thumbUrl = thumbUrl += "&format=jpeg";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thumbUrl = thumbUrl += "?format=jpeg";
|
|
||||||
}
|
|
||||||
|
|
||||||
out.file = await fetch(thumbUrl)
|
|
||||||
.then((res) => res.arrayBuffer())
|
|
||||||
.then((buf) => Buffer.from(buf));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const embed of msg.embeds) {
|
|
||||||
if (!embed.url) continue;
|
|
||||||
const parsed = new URL(embed.url);
|
|
||||||
if (embed.url && /(jpe?g|png|gif|webp)$/.test(parsed.pathname)) {
|
|
||||||
out.url = embed.url;
|
|
||||||
} else if (embed.image) {
|
|
||||||
out.url = embed.image.url;
|
|
||||||
break;
|
|
||||||
} else if (embed.video) {
|
|
||||||
out.video = true;
|
|
||||||
out.url = "attachment://thumb.jpg";
|
|
||||||
|
|
||||||
let thumbUrl =
|
|
||||||
embed.video.proxyURL ??
|
|
||||||
embed.video.url.replace("cdn.discordapp.com", "media.discordapp.net");
|
|
||||||
if (
|
|
||||||
thumbUrl.includes("media.discordapp.net") &&
|
|
||||||
thumbUrl.includes("?")
|
|
||||||
) {
|
|
||||||
if (thumbUrl.endsWith("&")) {
|
|
||||||
thumbUrl = thumbUrl += "format=jpeg";
|
|
||||||
} else {
|
|
||||||
thumbUrl = thumbUrl += "&format=jpeg";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thumbUrl = thumbUrl += "?format=jpeg";
|
|
||||||
}
|
|
||||||
if (embed.thumbnail?.url) thumbUrl = embed.thumbnail.url;
|
|
||||||
|
|
||||||
let valid = await fetch(thumbUrl, {method: "HEAD"}).then(
|
|
||||||
(res) => res.ok
|
|
||||||
);
|
|
||||||
if (!valid && thumbUrl.includes("media.discordapp.net")) {
|
|
||||||
thumbUrl = await hf.bot.requestHandler
|
|
||||||
.request("POST", "/attachments/refresh-urls", true, {
|
|
||||||
attachment_urls: [thumbUrl],
|
|
||||||
})
|
|
||||||
.then((res) => res.refreshed_urls[0].refreshed);
|
|
||||||
valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid)
|
|
||||||
out.file = await fetch(thumbUrl)
|
|
||||||
.then((res) => res.arrayBuffer())
|
|
||||||
.then((buf) => Buffer.from(buf));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createBoardMessage(msg, count, fetchAttachment = true) {
|
|
||||||
const name = msg.member?.nick ?? msg.author.globalName ?? msg.author.username;
|
|
||||||
const embed = {
|
|
||||||
title: `${count} \u2b50 - ${msg.jumpLink}`,
|
|
||||||
color: pastelize(name),
|
|
||||||
description: msg.content,
|
|
||||||
timestamp: new Date(msg.timestamp).toISOString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let image;
|
|
||||||
if (fetchAttachment) {
|
|
||||||
image = await findSuitableImage(msg);
|
|
||||||
if (image.url) {
|
|
||||||
if (image.video) {
|
|
||||||
embed.description += `\n(contains video ${
|
|
||||||
image.attachment ? "attachment" : "embed"
|
|
||||||
})`;
|
|
||||||
}
|
|
||||||
embed.image = {
|
|
||||||
url: image.url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
avatarURL: msg.member?.avatarURL ?? msg.author.avatarURL,
|
|
||||||
username: name,
|
|
||||||
threadID: VINBOARD_THREAD_ID,
|
|
||||||
embeds: [embed],
|
|
||||||
attachments: image?.file
|
|
||||||
? [{file: image.file, filename: "thumb.jpg"}]
|
|
||||||
: null,
|
|
||||||
wait: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let vinboard_webhook;
|
let vinboard_webhook;
|
||||||
let vin_channel;
|
let vin_channel;
|
||||||
let board_channel;
|
let board_channel;
|
||||||
|
@ -300,9 +168,7 @@ async function processReaction(_msg, reaction, user) {
|
||||||
if (reaction.name != "\u2b50") return;
|
if (reaction.name != "\u2b50") return;
|
||||||
|
|
||||||
if (!vin_channel) {
|
if (!vin_channel) {
|
||||||
vin_channel = hf.bot.guilds
|
vin_channel = hf.bot.guilds.get(FOXWELLS_GUILD_ID).channels.get(VINBOARD_CHANNEL_ID);
|
||||||
.get(FOXWELLS_GUILD_ID)
|
|
||||||
.channels.get(VINBOARD_CHANNEL_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vin_channel) {
|
if (!vin_channel) {
|
||||||
|
@ -311,9 +177,7 @@ async function processReaction(_msg, reaction, user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!board_channel) {
|
if (!board_channel) {
|
||||||
board_channel = hf.bot.guilds
|
board_channel = hf.bot.guilds.get(FOXWELLS_GUILD_ID).threads.get(VINBOARD_THREAD_ID);
|
||||||
.get(FOXWELLS_GUILD_ID)
|
|
||||||
.threads.get(VINBOARD_THREAD_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!board_channel) {
|
if (!board_channel) {
|
||||||
|
@ -321,15 +185,11 @@ async function processReaction(_msg, reaction, user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg =
|
const msg = vin_channel.messages.get(_msg.id) ?? (await vin_channel.getMessage(_msg.id));
|
||||||
vin_channel.messages.get(_msg.id) ??
|
|
||||||
(await vin_channel.getMessage(_msg.id));
|
|
||||||
|
|
||||||
if (!vinboard_webhook) {
|
if (!vinboard_webhook) {
|
||||||
const webhooks = await vin_channel.getWebhooks();
|
const webhooks = await vin_channel.getWebhooks();
|
||||||
vinboard_webhook = webhooks.find(
|
vinboard_webhook = webhooks.find((webhook) => webhook.id == VINBOARD_WEBHOOK_ID);
|
||||||
(webhook) => webhook.id == VINBOARD_WEBHOOK_ID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vinboard_webhook) {
|
if (!vinboard_webhook) {
|
||||||
|
@ -338,9 +198,7 @@ async function processReaction(_msg, reaction, user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const reacts = await msg.getReaction("\u2b50");
|
const reacts = await msg.getReaction("\u2b50");
|
||||||
const trueCount = reacts.filter(
|
const trueCount = reacts.filter((reactor) => reactor.id != msg.author.id).length;
|
||||||
(reactor) => reactor.id != msg.author.id
|
|
||||||
).length;
|
|
||||||
|
|
||||||
const dbEntry = await getBoardEntry(msg.id);
|
const dbEntry = await getBoardEntry(msg.id);
|
||||||
if (dbEntry) {
|
if (dbEntry) {
|
||||||
|
@ -360,12 +218,7 @@ async function processReaction(_msg, reaction, user) {
|
||||||
board_channel.messages.get(dbEntry.board_id) ??
|
board_channel.messages.get(dbEntry.board_id) ??
|
||||||
(await board_channel.getMessage(dbEntry.board_id).catch(() => {}));
|
(await board_channel.getMessage(dbEntry.board_id).catch(() => {}));
|
||||||
if (_boardMessage) {
|
if (_boardMessage) {
|
||||||
logger.verbose(
|
logger.verbose("vinboard", `Updating count for "${msg.id}" (${dbEntry.count ?? 0} -> ${trueCount})`);
|
||||||
"vinboard",
|
|
||||||
`Updating count for "${msg.id}" (${
|
|
||||||
dbEntry.count ?? 0
|
|
||||||
} -> ${trueCount})`
|
|
||||||
);
|
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
avatarURL: _boardMessage.author.avatarURL,
|
avatarURL: _boardMessage.author.avatarURL,
|
||||||
|
@ -374,24 +227,17 @@ async function processReaction(_msg, reaction, user) {
|
||||||
embeds: _boardMessage.embeds,
|
embeds: _boardMessage.embeds,
|
||||||
wait: true,
|
wait: true,
|
||||||
};
|
};
|
||||||
props.attachments = [..._boardMessage.attachments.values()].map(
|
props.attachments = [..._boardMessage.attachments.values()].map((attach) => ({id: attach.id}));
|
||||||
(attach) => ({id: attach.id})
|
|
||||||
);
|
|
||||||
props.embeds[0].title = `${trueCount} \u2b50`;
|
props.embeds[0].title = `${trueCount} \u2b50`;
|
||||||
props.embeds[0].color = pastelize(msg.author.username);
|
props.embeds[0].color = pastelize(msg.author.username);
|
||||||
await hf.bot.editWebhookMessage(
|
await hf.bot.editWebhookMessage(vinboard_webhook.id, vinboard_webhook.token, _boardMessage.id, props);
|
||||||
vinboard_webhook.id,
|
|
||||||
vinboard_webhook.token,
|
|
||||||
_boardMessage.id,
|
|
||||||
props
|
|
||||||
);
|
|
||||||
await setBoardEntry(msg.id, trueCount, _boardMessage.id);
|
await setBoardEntry(msg.id, trueCount, _boardMessage.id);
|
||||||
} else {
|
} else {
|
||||||
logger.verbose("vinboard", `Creating entry for "${msg.id}"`);
|
logger.verbose("vinboard", `Creating entry for "${msg.id}"`);
|
||||||
const boardMessage = await hf.bot.executeWebhook(
|
const boardMessage = await hf.bot.executeWebhook(
|
||||||
vinboard_webhook.id,
|
vinboard_webhook.id,
|
||||||
vinboard_webhook.token,
|
vinboard_webhook.token,
|
||||||
await createBoardMessage(msg, trueCount)
|
await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID)
|
||||||
);
|
);
|
||||||
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
||||||
}
|
}
|
||||||
|
@ -400,7 +246,7 @@ async function processReaction(_msg, reaction, user) {
|
||||||
const boardMessage = await hf.bot.executeWebhook(
|
const boardMessage = await hf.bot.executeWebhook(
|
||||||
vinboard_webhook.id,
|
vinboard_webhook.id,
|
||||||
vinboard_webhook.token,
|
vinboard_webhook.token,
|
||||||
await createBoardMessage(msg, trueCount)
|
await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID)
|
||||||
);
|
);
|
||||||
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
||||||
}
|
}
|
||||||
|
@ -409,7 +255,7 @@ async function processReaction(_msg, reaction, user) {
|
||||||
const boardMessage = await hf.bot.executeWebhook(
|
const boardMessage = await hf.bot.executeWebhook(
|
||||||
vinboard_webhook.id,
|
vinboard_webhook.id,
|
||||||
vinboard_webhook.token,
|
vinboard_webhook.token,
|
||||||
await createBoardMessage(msg, trueCount)
|
await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID)
|
||||||
);
|
);
|
||||||
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Command = require("../lib/command.js");
|
const Command = require("../lib/command.js");
|
||||||
const CATEGORY = "general";
|
const CATEGORY = "general";
|
||||||
|
|
||||||
const {snowflakeToTimestamp} = require("../lib/utils.js");
|
const {snowflakeToTimestamp} = require("../util/time.js");
|
||||||
|
|
||||||
const help = new Command("help");
|
const help = new Command("help");
|
||||||
help.category = CATEGORY;
|
help.category = CATEGORY;
|
||||||
|
@ -27,9 +27,7 @@ help.callback = function (msg, line) {
|
||||||
for (const cat in sorted) {
|
for (const cat in sorted) {
|
||||||
embed.fields.push({
|
embed.fields.push({
|
||||||
name: cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1),
|
name: cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1),
|
||||||
value: `${sorted[cat].length} Commands\n\`${
|
value: `${sorted[cat].length} Commands\n\`${hf.config.prefix}help --${cat.toLowerCase()}\``,
|
||||||
hf.config.prefix
|
|
||||||
}help --${cat.toLowerCase()}\``,
|
|
||||||
inline: true,
|
inline: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -40,9 +38,7 @@ help.callback = function (msg, line) {
|
||||||
|
|
||||||
if (sorted[cat]) {
|
if (sorted[cat]) {
|
||||||
const embed = {
|
const embed = {
|
||||||
title: `HiddenPhox Help: Category > ${
|
title: `HiddenPhox Help: Category > ${cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1)}`,
|
||||||
cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1)
|
|
||||||
}`,
|
|
||||||
fields: [],
|
fields: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,9 +74,7 @@ help.callback = function (msg, line) {
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "Category",
|
name: "Category",
|
||||||
value:
|
value: cmd.category.toUpperCase().charAt(0) + cmd.category.toLowerCase().substring(1),
|
||||||
cmd.category.toUpperCase().charAt(0) +
|
|
||||||
cmd.category.toLowerCase().substring(1),
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -125,9 +119,7 @@ ping.callback = async function (msg) {
|
||||||
const msgTimestamp = snowflakeToTimestamp(msg.id);
|
const msgTimestamp = snowflakeToTimestamp(msg.id);
|
||||||
const noncePing = Math.floor(msgTimestamp - nonceTimestamp);
|
const noncePing = Math.floor(msgTimestamp - nonceTimestamp);
|
||||||
|
|
||||||
const gateway = hf.bot.shards.get(
|
const gateway = hf.bot.shards.get(hf.bot.guildShardMap[hf.bot.channelGuildMap[msg.channel.id]] || 0).latency;
|
||||||
hf.bot.guildShardMap[hf.bot.channelGuildMap[msg.channel.id]] || 0
|
|
||||||
).latency;
|
|
||||||
|
|
||||||
const newMsg = await msg.channel.createMessage({
|
const newMsg = await msg.channel.createMessage({
|
||||||
content: `Pong.\n**RTT:** \`...\`\n**Gateway:** \`${gateway}ms\`${
|
content: `Pong.\n**RTT:** \`...\`\n**Gateway:** \`${gateway}ms\`${
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const Command = require("../lib/command.js");
|
const Command = require("../lib/command.js");
|
||||||
const CATEGORY = "image";
|
const CATEGORY = "image";
|
||||||
|
|
||||||
const {getImage} = require("../lib/utils.js");
|
const {getImage} = require("../util/image.js");
|
||||||
|
|
||||||
const dumpyConvert = require("dumpy").convert;
|
//const dumpyConvert = require("dumpy").convert;
|
||||||
const Jimp = require("jimp");
|
const Jimp = require("jimp");
|
||||||
|
|
||||||
async function createImageCallback(msg, url, callback, filename) {
|
async function createImageCallback(msg, url, callback, filename) {
|
||||||
|
@ -29,7 +29,7 @@ async function createImageCallback(msg, url, callback, filename) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dumpy = new Command("dumpy");
|
/*const dumpy = new Command("dumpy");
|
||||||
dumpy.category = CATEGORY;
|
dumpy.category = CATEGORY;
|
||||||
dumpy.helpText = "Among Us Dumpy GIF Creator";
|
dumpy.helpText = "Among Us Dumpy GIF Creator";
|
||||||
dumpy.usage = "<width> [url]";
|
dumpy.usage = "<width> [url]";
|
||||||
|
@ -37,14 +37,9 @@ dumpy.callback = async function (msg, line, [width, url]) {
|
||||||
if (isNaN(parseInt(width))) url = width;
|
if (isNaN(parseInt(width))) url = width;
|
||||||
width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48);
|
width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48);
|
||||||
|
|
||||||
return await createImageCallback(
|
return await createImageCallback(msg, url, async (img) => await dumpyConvert(img, width), "dumpy.gif");
|
||||||
msg,
|
|
||||||
url,
|
|
||||||
async (img) => await dumpyConvert(img, width),
|
|
||||||
"dumpy.gif"
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
hf.registerCommand(dumpy);
|
hf.registerCommand(dumpy);*/
|
||||||
|
|
||||||
const hooh = new Command("hooh");
|
const hooh = new Command("hooh");
|
||||||
hooh.category = CATEGORY;
|
hooh.category = CATEGORY;
|
||||||
|
@ -56,14 +51,7 @@ hooh.callback = async function (msg, line) {
|
||||||
line,
|
line,
|
||||||
async (url) => {
|
async (url) => {
|
||||||
const img = await Jimp.read(url);
|
const img = await Jimp.read(url);
|
||||||
const half1 = img
|
const half1 = img.clone().crop(0, img.bitmap.height / 2, img.bitmap.width, img.bitmap.height / 2);
|
||||||
.clone()
|
|
||||||
.crop(
|
|
||||||
0,
|
|
||||||
img.bitmap.height / 2,
|
|
||||||
img.bitmap.width,
|
|
||||||
img.bitmap.height / 2
|
|
||||||
);
|
|
||||||
const half2 = half1.clone().mirror(false, true);
|
const half2 = half1.clone().mirror(false, true);
|
||||||
|
|
||||||
return await img
|
return await img
|
||||||
|
@ -86,9 +74,7 @@ woow.callback = async function (msg, line) {
|
||||||
line,
|
line,
|
||||||
async (url) => {
|
async (url) => {
|
||||||
const img = await Jimp.read(url);
|
const img = await Jimp.read(url);
|
||||||
const half1 = img
|
const half1 = img.clone().crop(0, 0, img.bitmap.width, img.bitmap.height / 2);
|
||||||
.clone()
|
|
||||||
.crop(0, 0, img.bitmap.width, img.bitmap.height / 2);
|
|
||||||
const half2 = half1.clone().mirror(false, true);
|
const half2 = half1.clone().mirror(false, true);
|
||||||
|
|
||||||
return await img
|
return await img
|
||||||
|
@ -111,9 +97,7 @@ haah.callback = async function (msg, line) {
|
||||||
line,
|
line,
|
||||||
async (url) => {
|
async (url) => {
|
||||||
const img = await Jimp.read(url);
|
const img = await Jimp.read(url);
|
||||||
const half1 = img
|
const half1 = img.clone().crop(0, 0, img.bitmap.width / 2, img.bitmap.height);
|
||||||
.clone()
|
|
||||||
.crop(0, 0, img.bitmap.width / 2, img.bitmap.height);
|
|
||||||
const half2 = half1.clone().mirror(true, false);
|
const half2 = half1.clone().mirror(true, false);
|
||||||
|
|
||||||
return await img
|
return await img
|
||||||
|
@ -136,9 +120,7 @@ waaw.callback = async function (msg, line) {
|
||||||
line,
|
line,
|
||||||
async (url) => {
|
async (url) => {
|
||||||
const img = await Jimp.read(url);
|
const img = await Jimp.read(url);
|
||||||
const half1 = img
|
const half1 = img.clone().crop(img.bitmap.width / 2, 0, img.bitmap.width / 2, img.bitmap.height);
|
||||||
.clone()
|
|
||||||
.crop(img.bitmap.width / 2, 0, img.bitmap.width / 2, img.bitmap.height);
|
|
||||||
const half2 = half1.clone().mirror(true, false);
|
const half2 = half1.clone().mirror(true, false);
|
||||||
|
|
||||||
return await img
|
return await img
|
||||||
|
@ -160,9 +142,7 @@ invert.callback = async function (msg, line) {
|
||||||
msg,
|
msg,
|
||||||
line,
|
line,
|
||||||
async (img) => {
|
async (img) => {
|
||||||
return await Jimp.read(img).then((x) =>
|
return await Jimp.read(img).then((x) => x.invert().getBufferAsync(Jimp.MIME_PNG));
|
||||||
x.invert().getBufferAsync(Jimp.MIME_PNG)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
"invert.png"
|
"invert.png"
|
||||||
);
|
);
|
||||||
|
@ -178,9 +158,7 @@ flip.callback = async function (msg, line) {
|
||||||
msg,
|
msg,
|
||||||
line,
|
line,
|
||||||
async (img) => {
|
async (img) => {
|
||||||
return await Jimp.read(img).then((x) =>
|
return await Jimp.read(img).then((x) => x.mirror(true, false).getBufferAsync(Jimp.MIME_PNG));
|
||||||
x.mirror(true, false).getBufferAsync(Jimp.MIME_PNG)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
"flip.png"
|
"flip.png"
|
||||||
);
|
);
|
||||||
|
@ -196,9 +174,7 @@ flop.callback = async function (msg, line) {
|
||||||
msg,
|
msg,
|
||||||
line,
|
line,
|
||||||
async (img) => {
|
async (img) => {
|
||||||
return await Jimp.read(img).then((x) =>
|
return await Jimp.read(img).then((x) => x.mirror(false, true).getBufferAsync(Jimp.MIME_PNG));
|
||||||
x.mirror(false, true).getBufferAsync(Jimp.MIME_PNG)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
"flop.png"
|
"flop.png"
|
||||||
);
|
);
|
||||||
|
@ -215,9 +191,7 @@ jpeg.callback = async function (msg, line) {
|
||||||
msg,
|
msg,
|
||||||
line,
|
line,
|
||||||
async (img) => {
|
async (img) => {
|
||||||
return await Jimp.read(img).then((x) =>
|
return await Jimp.read(img).then((x) => x.quality(1).getBufferAsync(Jimp.MIME_JPEG));
|
||||||
x.quality(1).getBufferAsync(Jimp.MIME_JPEG)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
"jpeg.jpg"
|
"jpeg.jpg"
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,16 +3,12 @@ const InteractionCommand = require("../lib/interactionCommand.js");
|
||||||
const logger = require("../lib/logger.js");
|
const logger = require("../lib/logger.js");
|
||||||
const CATEGORY = "misc";
|
const CATEGORY = "misc";
|
||||||
|
|
||||||
const {ApplicationCommandOptionTypes} =
|
const {ApplicationCommandOptionTypes} = require("../util/dconstants.js");
|
||||||
require("@projectdysnomia/dysnomia").Constants;
|
|
||||||
const {librex} = require("../../config.json");
|
const {formatUsername, safeString} = require("../util/misc.js");
|
||||||
const {getOption} = require("../lib/interactionDispatcher.js");
|
const {formatTime} = require("../util/time.js");
|
||||||
const {
|
const {parseHtmlEntities} = require("../util/html.js");
|
||||||
formatTime,
|
|
||||||
parseHtmlEntities,
|
|
||||||
formatUsername,
|
|
||||||
safeString,
|
|
||||||
} = require("../lib/utils.js");
|
|
||||||
const GoogleImages = require("google-images");
|
const GoogleImages = require("google-images");
|
||||||
const {tinycolor, random: randomColor} = require("@ctrl/tinycolor");
|
const {tinycolor, random: randomColor} = require("@ctrl/tinycolor");
|
||||||
const sharp = require("sharp");
|
const sharp = require("sharp");
|
||||||
|
@ -28,15 +24,13 @@ yt.usage = "[search term]";
|
||||||
yt.callback = async function (msg, line) {
|
yt.callback = async function (msg, line) {
|
||||||
if (!line) return "Arguments are required.";
|
if (!line) return "Arguments are required.";
|
||||||
|
|
||||||
const req = await fetch(
|
const req = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`).then((x) =>
|
||||||
`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`
|
x.json()
|
||||||
).then((x) => x.json());
|
);
|
||||||
|
|
||||||
const topVid = req.items[0];
|
const topVid = req.items[0];
|
||||||
|
|
||||||
let out = `**${safeString(
|
let out = `**${safeString(parseHtmlEntities(topVid.title))}** | \`${safeString(
|
||||||
parseHtmlEntities(topVid.title)
|
|
||||||
)}** | \`${safeString(
|
|
||||||
parseHtmlEntities(topVid.uploaderName)
|
parseHtmlEntities(topVid.uploaderName)
|
||||||
)}\`\nhttps://youtube.com${topVid.url}\n\n**__See Also:__**\n`;
|
)}\`\nhttps://youtube.com${topVid.url}\n\n**__See Also:__**\n`;
|
||||||
|
|
||||||
|
@ -44,9 +38,7 @@ yt.callback = async function (msg, line) {
|
||||||
const vid = req.items[i];
|
const vid = req.items[i];
|
||||||
if (!vid) continue;
|
if (!vid) continue;
|
||||||
|
|
||||||
out += `- **${safeString(
|
out += `- **${safeString(parseHtmlEntities(vid.title))}** | By: \`${safeString(
|
||||||
parseHtmlEntities(vid.title)
|
|
||||||
)}** | By: \`${safeString(
|
|
||||||
parseHtmlEntities(vid.uploaderName)
|
parseHtmlEntities(vid.uploaderName)
|
||||||
)}\` | <https://youtube.com${vid.url}>\n`;
|
)}\` | <https://youtube.com${vid.url}>\n`;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +57,7 @@ ytInteraction.options.search = {
|
||||||
default: "",
|
default: "",
|
||||||
};
|
};
|
||||||
ytInteraction.callback = async function (interaction) {
|
ytInteraction.callback = async function (interaction) {
|
||||||
const search = getOption(interaction, ytInteraction, "search");
|
const search = this.getOption(interaction, "search");
|
||||||
|
|
||||||
return yt.callback(interaction, search);
|
return yt.callback(interaction, search);
|
||||||
};
|
};
|
||||||
|
@ -78,9 +70,9 @@ fyt.usage = "[search term]";
|
||||||
fyt.callback = async function (msg, line) {
|
fyt.callback = async function (msg, line) {
|
||||||
if (!line) return "Arguments are required.";
|
if (!line) return "Arguments are required.";
|
||||||
|
|
||||||
const req = await fetch(
|
const req = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`).then((x) =>
|
||||||
`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`
|
x.json()
|
||||||
).then((x) => x.json());
|
);
|
||||||
|
|
||||||
const vid = req.items[0];
|
const vid = req.items[0];
|
||||||
|
|
||||||
|
@ -100,7 +92,7 @@ fytInteraction.options.search = {
|
||||||
default: "",
|
default: "",
|
||||||
};
|
};
|
||||||
fytInteraction.callback = async function (interaction) {
|
fytInteraction.callback = async function (interaction) {
|
||||||
const search = getOption(interaction, fytInteraction, "search");
|
const search = this.getOption(interaction, "search");
|
||||||
|
|
||||||
return fyt.callback(interaction, search);
|
return fyt.callback(interaction, search);
|
||||||
};
|
};
|
||||||
|
@ -119,9 +111,7 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) {
|
||||||
const query = args.join(" ");
|
const query = args.join(" ");
|
||||||
|
|
||||||
const req = await fetch(
|
const req = await fetch(
|
||||||
`http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(
|
`http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(query)}&appid=LH2K8H-T3QKETAGT3&output=json`
|
||||||
query
|
|
||||||
)}&appid=LH2K8H-T3QKETAGT3&output=json`
|
|
||||||
).then((x) => x.json());
|
).then((x) => x.json());
|
||||||
|
|
||||||
const data = req.queryresult.pods;
|
const data = req.queryresult.pods;
|
||||||
|
@ -148,11 +138,9 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) {
|
||||||
for (const x in extra) {
|
for (const x in extra) {
|
||||||
embed.fields.push({
|
embed.fields.push({
|
||||||
name: extra[x].title,
|
name: extra[x].title,
|
||||||
value: `[${
|
value: `[${extra[x].subpods[0].plaintext.length > 0 ? extra[x].subpods[0].plaintext : "<click for image>"}](${
|
||||||
extra[x].subpods[0].plaintext.length > 0
|
extra[x].subpods[0].img.src
|
||||||
? extra[x].subpods[0].plaintext
|
})`,
|
||||||
: "<click for image>"
|
|
||||||
}](${extra[x].subpods[0].img.src})`,
|
|
||||||
inline: true,
|
inline: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -173,8 +161,7 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let string = "";
|
let string = "";
|
||||||
if (data[1].subpods[0].plaintext.length > 0)
|
if (data[1].subpods[0].plaintext.length > 0) string = safeString(data[1].subpods[0].plaintext);
|
||||||
string = safeString(data[1].subpods[0].plaintext);
|
|
||||||
|
|
||||||
let text;
|
let text;
|
||||||
if (string.length > 2000 - (6 + safeString(query).length)) {
|
if (string.length > 2000 - (6 + safeString(query).length)) {
|
||||||
|
@ -216,8 +203,8 @@ wolframInteraction.options.verbose = {
|
||||||
default: false,
|
default: false,
|
||||||
};
|
};
|
||||||
wolframInteraction.callback = async function (interaction) {
|
wolframInteraction.callback = async function (interaction) {
|
||||||
const query = getOption(interaction, wolframInteraction, "query");
|
const query = this.getOption(interaction, "query");
|
||||||
const verbose = getOption(interaction, wolframInteraction, "verbose");
|
const verbose = this.getOption(interaction, "verbose");
|
||||||
|
|
||||||
return wolfram.callback(interaction, query, [query], {verbose});
|
return wolfram.callback(interaction, query, [query], {verbose});
|
||||||
};
|
};
|
||||||
|
@ -232,10 +219,7 @@ gimg.callback = async function (msg, line) {
|
||||||
if (!line) return "No arguments given.";
|
if (!line) return "No arguments given.";
|
||||||
|
|
||||||
const images = await imagesClient.search(line, {
|
const images = await imagesClient.search(line, {
|
||||||
safe:
|
safe: msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") ? "off" : "high",
|
||||||
msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]")
|
|
||||||
? "off"
|
|
||||||
: "high",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const index = Math.floor(Math.random() * images.length);
|
const index = Math.floor(Math.random() * images.length);
|
||||||
|
@ -250,9 +234,7 @@ gimg.callback = async function (msg, line) {
|
||||||
url: image.url,
|
url: image.url,
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
text: `Image ${Number(index) + 1}/${
|
text: `Image ${Number(index) + 1}/${images.length}. Rerun to get a different image.`,
|
||||||
images.length
|
|
||||||
}. Rerun to get a different image.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -268,10 +250,7 @@ fimg.callback = async function (msg, line) {
|
||||||
if (!line) return "No arguments given.";
|
if (!line) return "No arguments given.";
|
||||||
|
|
||||||
const images = await imagesClient.search(line, {
|
const images = await imagesClient.search(line, {
|
||||||
safe:
|
safe: msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") ? "off" : "high",
|
||||||
msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]")
|
|
||||||
? "off"
|
|
||||||
: "high",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const image = images[0];
|
const image = images[0];
|
||||||
|
@ -295,22 +274,15 @@ poll.category = CATEGORY;
|
||||||
poll.helpText = "Start a poll";
|
poll.helpText = "Start a poll";
|
||||||
poll.usage = "[topic] [option 1] [option 2] [...option 3-10]";
|
poll.usage = "[topic] [option 1] [option 2] [...option 3-10]";
|
||||||
poll.callback = async function (msg, line, [topic, ...options]) {
|
poll.callback = async function (msg, line, [topic, ...options]) {
|
||||||
if (!line || !topic)
|
if (!line || !topic) return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"';
|
||||||
return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"';
|
|
||||||
|
|
||||||
const arrOptions = [...options].slice(0, 10);
|
const arrOptions = [...options].slice(0, 10);
|
||||||
if (arrOptions.length < 2) return "A minimum of two options are required.";
|
if (arrOptions.length < 2) return "A minimum of two options are required.";
|
||||||
|
|
||||||
const reactions = [];
|
const reactions = [];
|
||||||
let displayString = `**${formatUsername(
|
let displayString = `**${formatUsername(msg.author)}** has started a poll:\n## __${topic}__\n`;
|
||||||
msg.author
|
|
||||||
)}** has started a poll:\n## __${topic}__\n`;
|
|
||||||
for (let i = 0; i < arrOptions.length; i++) {
|
for (let i = 0; i < arrOptions.length; i++) {
|
||||||
displayString +=
|
displayString += (i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`) + ": " + arrOptions[i] + "\n";
|
||||||
(i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`) +
|
|
||||||
": " +
|
|
||||||
arrOptions[i] +
|
|
||||||
"\n";
|
|
||||||
reactions[i] = i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`;
|
reactions[i] = i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,28 +325,20 @@ anonradio.callback = async function () {
|
||||||
|
|
||||||
let playing;
|
let playing;
|
||||||
try {
|
try {
|
||||||
playing = await fetch("https://anonradio.net/playing").then((res) =>
|
playing = await fetch("https://anonradio.net/playing").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
try {
|
try {
|
||||||
playing = await fetch("http://anonradio.net/playing").then((res) =>
|
playing = await fetch("http://anonradio.net/playing").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let schedule;
|
let schedule;
|
||||||
try {
|
try {
|
||||||
schedule = await fetch("https://anonradio.net/schedule/").then((res) =>
|
schedule = await fetch("https://anonradio.net/schedule/").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
try {
|
try {
|
||||||
schedule = await fetch("http://anonradio.net/schedule/").then((res) =>
|
schedule = await fetch("http://anonradio.net/schedule/").then((res) => res.text());
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
@ -384,9 +348,7 @@ anonradio.callback = async function () {
|
||||||
|
|
||||||
const icecast = await fetch("http://anonradio.net:8010/status-json.xsl")
|
const icecast = await fetch("http://anonradio.net:8010/status-json.xsl")
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((data) =>
|
.then((data) => JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",')));
|
||||||
JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",'))
|
|
||||||
);
|
|
||||||
|
|
||||||
let lines = schedule.split("\n");
|
let lines = schedule.split("\n");
|
||||||
lines = lines.slice(4, lines.length - 2);
|
lines = lines.slice(4, lines.length - 2);
|
||||||
|
@ -418,17 +380,11 @@ anonradio.callback = async function () {
|
||||||
hour = hour.slice(0, 2) + ":" + hour.slice(-2);
|
hour = hour.slice(0, 2) + ":" + hour.slice(-2);
|
||||||
|
|
||||||
const timestamp =
|
const timestamp =
|
||||||
Date.parse(
|
Date.parse(`${DAYS[targetDay]}, ${currentYear}-${targetMonth}-${targetDateDay} ${hour} UTC`) / 1000;
|
||||||
`${DAYS[targetDay]}, ${currentYear}-${targetMonth}-${targetDateDay} ${hour} UTC`
|
|
||||||
) / 1000;
|
|
||||||
|
|
||||||
let nameOut = name;
|
let nameOut = name;
|
||||||
|
|
||||||
if (time == "Sat 0300")
|
if (time == "Sat 0300") nameOut = name.replace("Open Mic - Anyone can stream", "Synth Battle Royale");
|
||||||
nameOut = name.replace(
|
|
||||||
"Open Mic - Anyone can stream",
|
|
||||||
"Synth Battle Royale"
|
|
||||||
);
|
|
||||||
|
|
||||||
parsedLines.push({
|
parsedLines.push({
|
||||||
timestamp,
|
timestamp,
|
||||||
|
@ -451,14 +407,9 @@ anonradio.callback = async function () {
|
||||||
title = `${liveNow.name} (\`${liveNow.id}\`)`;
|
title = `${liveNow.name} (\`${liveNow.id}\`)`;
|
||||||
subtitle = playing;
|
subtitle = playing;
|
||||||
} else {
|
} else {
|
||||||
const [_, current, peakDay, peakMonth, dj, metadata] = playing.match(
|
const [_, current, peakDay, peakMonth, dj, metadata] = playing.match(/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/);
|
||||||
/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (metadata == "https://archives.anonradio.net" || liveNow.name == "Synth Battle Royale") {
|
||||||
metadata == "https://archives.anonradio.net" ||
|
|
||||||
liveNow.name == "Synth Battle Royale"
|
|
||||||
) {
|
|
||||||
title = `${liveNow.name} (\`${dj}\`)`;
|
title = `${liveNow.name} (\`${dj}\`)`;
|
||||||
} else {
|
} else {
|
||||||
title = `${metadata} (\`${dj}\`)`;
|
title = `${metadata} (\`${dj}\`)`;
|
||||||
|
@ -468,14 +419,10 @@ anonradio.callback = async function () {
|
||||||
|
|
||||||
let openmicTime = "";
|
let openmicTime = "";
|
||||||
if (liveNow.id == "openmic") {
|
if (liveNow.id == "openmic") {
|
||||||
const streamData = icecast.icestats.source.find(
|
const streamData = icecast.icestats.source.find((src) => src.listenurl == "http://anonradio.net:8010/openmic");
|
||||||
(src) => src.listenurl == "http://anonradio.net:8010/openmic"
|
|
||||||
);
|
|
||||||
if (streamData && streamData.stream_start_iso8601) {
|
if (streamData && streamData.stream_start_iso8601) {
|
||||||
const startTime = new Date(streamData.stream_start_iso8601).getTime();
|
const startTime = new Date(streamData.stream_start_iso8601).getTime();
|
||||||
openmicTime = `-\\*- OpenMIC DJ has been streaming for ${formatTime(
|
openmicTime = `-\\*- OpenMIC DJ has been streaming for ${formatTime(Date.now() - startTime)} -\\*-\n`;
|
||||||
Date.now() - startTime
|
|
||||||
)} -\\*-\n`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,9 +454,7 @@ shodan.callback = async function (msg, line) {
|
||||||
if (!line || line == "") return "Arguments required.";
|
if (!line || line == "") return "Arguments required.";
|
||||||
if (!REGEX_IPV4.test(line)) return "Invalid IP address.";
|
if (!REGEX_IPV4.test(line)) return "Invalid IP address.";
|
||||||
|
|
||||||
const data = await fetch("https://internetdb.shodan.io/" + line).then((res) =>
|
const data = await fetch("https://internetdb.shodan.io/" + line).then((res) => res.json());
|
||||||
res.json()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data.detail) return data.detail;
|
if (data.detail) return data.detail;
|
||||||
|
|
||||||
|
@ -519,10 +464,7 @@ shodan.callback = async function (msg, line) {
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "Hostnames",
|
name: "Hostnames",
|
||||||
value:
|
value: data.hostnames.length > 0 ? data.hostnames.map((str) => `\`${str}\``).join(" ") : "None",
|
||||||
data.hostnames.length > 0
|
|
||||||
? data.hostnames.map((str) => `\`${str}\``).join(" ")
|
|
||||||
: "None",
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -532,26 +474,17 @@ shodan.callback = async function (msg, line) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Tags",
|
name: "Tags",
|
||||||
value:
|
value: data.tags.length > 0 ? data.tags.map((str) => `\`${str}\``).join(", ") : "None",
|
||||||
data.tags.length > 0
|
|
||||||
? data.tags.map((str) => `\`${str}\``).join(", ")
|
|
||||||
: "None",
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CPEs",
|
name: "CPEs",
|
||||||
value:
|
value: data.cpes.length > 0 ? data.cpes.map((str) => `\`${str}\``).join(" ") : "None",
|
||||||
data.cpes.length > 0
|
|
||||||
? data.cpes.map((str) => `\`${str}\``).join(" ")
|
|
||||||
: "None",
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Vulnerabilities",
|
name: "Vulnerabilities",
|
||||||
value:
|
value: data.vulns.length > 0 ? data.vulns.map((str) => `\`${str}\``).join(" ") : "None",
|
||||||
data.vulns.length > 0
|
|
||||||
? data.vulns.map((str) => `\`${str}\``).join(" ")
|
|
||||||
: "None",
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -596,9 +529,7 @@ generate.callback = async function (msg, line) {
|
||||||
const title = `Responses for "${safeString(line)}"`;
|
const title = `Responses for "${safeString(line)}"`;
|
||||||
|
|
||||||
const out = {
|
const out = {
|
||||||
content: `Generated in ${formatTime(Date.now() - start)}${
|
content: `Generated in ${formatTime(Date.now() - start)}${retries > 0 ? " with " + retries + " retries" : ""}`,
|
||||||
retries > 0 ? " with " + retries + " retries" : ""
|
|
||||||
}`,
|
|
||||||
embeds: [],
|
embeds: [],
|
||||||
files: images,
|
files: images,
|
||||||
};
|
};
|
||||||
|
@ -627,7 +558,7 @@ search.addAlias("google");
|
||||||
search.addAlias("ddg");
|
search.addAlias("ddg");
|
||||||
search.callback = async function (msg, line, args, {results = 2}) {
|
search.callback = async function (msg, line, args, {results = 2}) {
|
||||||
const query = args.join(" ");
|
const query = args.join(" ");
|
||||||
if (!librex) return "LibreX instance not defined.";
|
if (!hf.config.librex) return "LibreX instance not defined.";
|
||||||
if (!query || query == "") return "Search query required.";
|
if (!query || query == "") return "Search query required.";
|
||||||
|
|
||||||
const encodedQuery = encodeURIComponent(query);
|
const encodedQuery = encodeURIComponent(query);
|
||||||
|
@ -638,9 +569,7 @@ search.callback = async function (msg, line, args, {results = 2}) {
|
||||||
if (res.url != url) return res.url;
|
if (res.url != url) return res.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(`${librex}/api.php?q=${encodedQuery}&p=0&t=0`).then(
|
const res = await fetch(`${hf.config.librex}/api.php?q=${encodedQuery}&p=0&t=0`).then((res) => res.json());
|
||||||
(res) => res.json()
|
|
||||||
);
|
|
||||||
if (res.error?.message) {
|
if (res.error?.message) {
|
||||||
if (res.error.message.indexOf("No results found.") > -1) {
|
if (res.error.message.indexOf("No results found.") > -1) {
|
||||||
return "Search returned no results.";
|
return "Search returned no results.";
|
||||||
|
@ -649,27 +578,17 @@ search.callback = async function (msg, line, args, {results = 2}) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const searchResults = Object.values(res)
|
const searchResults = Object.values(res)
|
||||||
.filter(
|
.filter((result) => result.did_you_mean == null && typeof result !== "string")
|
||||||
(result) => result.did_you_mean == null && typeof result !== "string"
|
|
||||||
)
|
|
||||||
.splice(0, Number(results));
|
.splice(0, Number(results));
|
||||||
|
|
||||||
if (searchResults.length > 0) {
|
if (searchResults.length > 0) {
|
||||||
let out = `__**Results for \`${safeString(query)}\`**__\n`;
|
let out = `__**Results for \`${safeString(query)}\`**__\n`;
|
||||||
for (const result of searchResults) {
|
for (const result of searchResults) {
|
||||||
if (result.special_response) {
|
if (result.special_response) {
|
||||||
out +=
|
out += "> " + safeString(parseHtmlEntities(result.special_response.response.split("\n").join("\n> ")));
|
||||||
"> " +
|
|
||||||
safeString(
|
|
||||||
parseHtmlEntities(
|
|
||||||
result.special_response.response.split("\n").join("\n> ")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
out += `\n<${encodeURI(result.special_response.source)}>`;
|
out += `\n<${encodeURI(result.special_response.source)}>`;
|
||||||
} else {
|
} else {
|
||||||
out += `**${safeString(
|
out += `**${safeString(parseHtmlEntities(result.title)).trim()}** - <${encodeURI(result.url)}>`;
|
||||||
parseHtmlEntities(result.title)
|
|
||||||
).trim()}** - <${encodeURI(result.url)}>`;
|
|
||||||
out += `\n> ${safeString(parseHtmlEntities(result.description))}`;
|
out += `\n> ${safeString(parseHtmlEntities(result.description))}`;
|
||||||
}
|
}
|
||||||
out += "\n\n";
|
out += "\n\n";
|
||||||
|
@ -702,8 +621,8 @@ searchInteraction.options.results = {
|
||||||
default: 2,
|
default: 2,
|
||||||
};
|
};
|
||||||
searchInteraction.callback = async function (interaction) {
|
searchInteraction.callback = async function (interaction) {
|
||||||
const query = getOption(interaction, searchInteraction, "query");
|
const query = this.getOption(interaction, "query");
|
||||||
const results = getOption(interaction, searchInteraction, "results");
|
const results = this.getOption(interaction, "results");
|
||||||
|
|
||||||
return search.callback(interaction, query, [query], {results});
|
return search.callback(interaction, query, [query], {results});
|
||||||
};
|
};
|
||||||
|
@ -717,9 +636,7 @@ color.callback = async function (msg, line, args, {truerandom}) {
|
||||||
random = false;
|
random = false;
|
||||||
|
|
||||||
if (!line || line == "" || args.length == 0) {
|
if (!line || line == "" || args.length == 0) {
|
||||||
color = truerandom
|
color = truerandom ? tinycolor(Math.floor(Math.random() * 0xffffff)) : randomColor();
|
||||||
? tinycolor(Math.floor(Math.random() * 0xffffff))
|
|
||||||
: randomColor();
|
|
||||||
random = true;
|
random = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,14 +723,13 @@ colorInteraction.options.input = {
|
||||||
colorInteraction.options.truerandom = {
|
colorInteraction.options.truerandom = {
|
||||||
name: "truerandom",
|
name: "truerandom",
|
||||||
type: ApplicationCommandOptionTypes.BOOLEAN,
|
type: ApplicationCommandOptionTypes.BOOLEAN,
|
||||||
description:
|
description: "Should the random color give a 'true random' color instead of an adjust color",
|
||||||
"Should the random color give a 'true random' color instead of an adjust color",
|
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
};
|
};
|
||||||
colorInteraction.callback = async function (interaction) {
|
colorInteraction.callback = async function (interaction) {
|
||||||
const input = getOption(interaction, colorInteraction, "input");
|
const input = this.getOption(interaction, "input");
|
||||||
const truerandom = getOption(interaction, colorInteraction, "truerandom");
|
const truerandom = this.getOption(interaction, "truerandom");
|
||||||
|
|
||||||
return color.callback(interaction, input, [input], {truerandom});
|
return color.callback(interaction, input, [input], {truerandom});
|
||||||
};
|
};
|
||||||
|
@ -849,10 +765,7 @@ const handshake = Buffer.concat([
|
||||||
identPort,
|
identPort,
|
||||||
writeVarInt(1),
|
writeVarInt(1),
|
||||||
]);
|
]);
|
||||||
const handshakeWithLength = Buffer.concat([
|
const handshakeWithLength = Buffer.concat([writeVarInt(handshake.length), handshake]);
|
||||||
writeVarInt(handshake.length),
|
|
||||||
handshake,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const status = Buffer.concat([writeVarInt(1), writeVarInt(0x0)]);
|
const status = Buffer.concat([writeVarInt(1), writeVarInt(0x0)]);
|
||||||
|
|
||||||
|
@ -927,12 +840,7 @@ mcserver.callback = async function (msg, line) {
|
||||||
}
|
}
|
||||||
const dataAsString = totalData.toString().trim();
|
const dataAsString = totalData.toString().trim();
|
||||||
console.log(dataAsString);
|
console.log(dataAsString);
|
||||||
const json = JSON.parse(
|
const json = JSON.parse(dataAsString.slice(dataAsString.indexOf("{"), dataAsString.lastIndexOf("}") + 1));
|
||||||
dataAsString.slice(
|
|
||||||
dataAsString.indexOf("{"),
|
|
||||||
dataAsString.lastIndexOf("}") + 1
|
|
||||||
)
|
|
||||||
);
|
|
||||||
logger.verbose("mcserver", "close", json);
|
logger.verbose("mcserver", "close", json);
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
return resolve(json);
|
return resolve(json);
|
||||||
|
@ -946,22 +854,16 @@ mcserver.callback = async function (msg, line) {
|
||||||
} else {
|
} else {
|
||||||
await msg.removeReaction("\uD83C\uDFD3");
|
await msg.removeReaction("\uD83C\uDFD3");
|
||||||
|
|
||||||
const motd = data.description.text.replace(
|
const motd = data.description.text.replace(/\u00a7([a-f0-9k-or])/gi, (formatting) => {
|
||||||
/\u00a7([a-f0-9k-or])/gi,
|
const ansi = formattingToAnsi[formatting];
|
||||||
(formatting) => {
|
return ansi ? `\x1b[${ansi}m` : "";
|
||||||
const ansi = formattingToAnsi[formatting];
|
});
|
||||||
return ansi ? `\x1b[${ansi}m` : "";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const players = data.players?.sample?.map((player) => player.name) ?? [];
|
const players = data.players?.sample?.map((player) => player.name) ?? [];
|
||||||
const totalPlayers = `(${data.players.online}/${data.players.max})`;
|
const totalPlayers = `(${data.players.online}/${data.players.max})`;
|
||||||
let image;
|
let image;
|
||||||
if (data.favicon) {
|
if (data.favicon) {
|
||||||
image = Buffer.from(
|
image = Buffer.from(data.favicon.slice(data.favicon.indexOf(",")), "base64");
|
||||||
data.favicon.slice(data.favicon.indexOf(",")),
|
|
||||||
"base64"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const Command = require("../lib/command.js");
|
const Command = require("../lib/command.js");
|
||||||
const CATEGORY = "moderation";
|
const CATEGORY = "moderation";
|
||||||
|
|
||||||
const {lookupUser, formatUsername} = require("../lib/utils.js");
|
const {formatUsername} = require("../util/misc.js");
|
||||||
|
const {lookupUser} = require("../util/selection.js");
|
||||||
|
|
||||||
const tidy = new Command("tidy");
|
const tidy = new Command("tidy");
|
||||||
tidy.addAlias("prune");
|
tidy.addAlias("prune");
|
||||||
|
@ -14,16 +15,10 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) {
|
||||||
if (!showHelp && !msg.guildID) {
|
if (!showHelp && !msg.guildID) {
|
||||||
return "Can only be used in guilds.";
|
return "Can only be used in guilds.";
|
||||||
}
|
}
|
||||||
if (
|
if (!showHelp && !msg.channel.permissionsOf(msg.author.id).has("manageMessages")) {
|
||||||
!showHelp &&
|
|
||||||
!msg.channel.permissionsOf(msg.author.id).has("manageMessages")
|
|
||||||
) {
|
|
||||||
return "You do not have `Manage Messages` permission.";
|
return "You do not have `Manage Messages` permission.";
|
||||||
}
|
}
|
||||||
if (
|
if (!showHelp && !msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages")) {
|
||||||
!showHelp &&
|
|
||||||
!msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages")
|
|
||||||
) {
|
|
||||||
return "I do not have `Manage Messages` permission.";
|
return "I do not have `Manage Messages` permission.";
|
||||||
}
|
}
|
||||||
if (msg.author.bot) return "Zero-width space your say command.";
|
if (msg.author.bot) return "Zero-width space your say command.";
|
||||||
|
@ -36,10 +31,7 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) {
|
||||||
limit: count && parseInt(count) > 0 ? parseInt(count) : 10,
|
limit: count && parseInt(count) > 0 ? parseInt(count) : 10,
|
||||||
})
|
})
|
||||||
.then((msgs) => msgs.map((m) => m.id));
|
.then((msgs) => msgs.map((m) => m.id));
|
||||||
await msg.channel.deleteMessages(
|
await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)}`);
|
||||||
messages,
|
|
||||||
`Message purge by ${formatUsername(msg.author)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return `Deleted ${messages.length} message(s).`;
|
return `Deleted ${messages.length} message(s).`;
|
||||||
}
|
}
|
||||||
|
@ -53,14 +45,10 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) {
|
||||||
before: msg.id,
|
before: msg.id,
|
||||||
limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10,
|
limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10,
|
||||||
})
|
})
|
||||||
.then((msgs) =>
|
.then((msgs) => msgs.filter((m) => m.author.id == user.id).map((m) => m.id));
|
||||||
msgs.filter((m) => m.author.id == user.id).map((m) => m.id)
|
|
||||||
);
|
|
||||||
await msg.channel.deleteMessages(
|
await msg.channel.deleteMessages(
|
||||||
messages,
|
messages,
|
||||||
`Message purge by ${formatUsername(
|
`Message purge by ${formatUsername(msg.author)} targeting ${formatUsername(user)}`
|
||||||
msg.author
|
|
||||||
)} targeting ${formatUsername(user)}`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return `Deleted ${messages.length} message(s).`;
|
return `Deleted ${messages.length} message(s).`;
|
||||||
|
@ -73,29 +61,20 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) {
|
||||||
limit: count && parseInt(count) > 0 ? parseInt(count) : 50,
|
limit: count && parseInt(count) > 0 ? parseInt(count) : 50,
|
||||||
})
|
})
|
||||||
.then((msgs) => msgs.filter((m) => msg.author.bot).map((m) => m.id));
|
.then((msgs) => msgs.filter((m) => msg.author.bot).map((m) => m.id));
|
||||||
await msg.channel.deleteMessages(
|
await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)} targeting bots`);
|
||||||
messages,
|
|
||||||
`Message purge by ${formatUsername(msg.author)} targeting bots`
|
|
||||||
);
|
|
||||||
|
|
||||||
return `Deleted ${messages.length} message(s).`;
|
return `Deleted ${messages.length} message(s).`;
|
||||||
}
|
}
|
||||||
case "filter": {
|
case "filter": {
|
||||||
if (count.length === 0)
|
if (count.length === 0) return "Filter must be at least 1 character long.";
|
||||||
return "Filter must be at least 1 character long.";
|
|
||||||
|
|
||||||
const messages = await msg.channel
|
const messages = await msg.channel
|
||||||
.getMessages({
|
.getMessages({
|
||||||
before: msg.id,
|
before: msg.id,
|
||||||
limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10,
|
limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10,
|
||||||
})
|
})
|
||||||
.then((msgs) =>
|
.then((msgs) => msgs.filter((m) => m.content.indexOf(count) > -1).map((m) => m.id));
|
||||||
msgs.filter((m) => m.content.indexOf(count) > -1).map((m) => m.id)
|
await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)} targeting "${count}"`);
|
||||||
);
|
|
||||||
await msg.channel.deleteMessages(
|
|
||||||
messages,
|
|
||||||
`Message purge by ${formatUsername(msg.author)} targeting "${count}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
return `Deleted ${messages.length} message(s).`;
|
return `Deleted ${messages.length} message(s).`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,23 +4,18 @@ const {Readable} = require("node:stream");
|
||||||
const ffprobe = require("node-ffprobe");
|
const ffprobe = require("node-ffprobe");
|
||||||
|
|
||||||
const Command = require("../lib/command.js");
|
const Command = require("../lib/command.js");
|
||||||
const {
|
|
||||||
formatTime,
|
const {formatUsername} = require("../util/misc.js");
|
||||||
parseHtmlEntities,
|
const {formatTime} = require("../util/time.js");
|
||||||
formatUsername,
|
const {parseHtmlEntities} = require("../util/html.js");
|
||||||
selectionMessage,
|
const {selectionMessage} = require("../util/selection.js");
|
||||||
} = require("../lib/utils.js");
|
|
||||||
|
|
||||||
const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
|
const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
|
||||||
const REGEX_YOUTUBE_PLAYLIST =
|
const REGEX_YOUTUBE_PLAYLIST = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/;
|
||||||
/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/;
|
|
||||||
const REGEX_YOUTUBE_PLAYLIST_SHORT = /^PL[a-zA-Z0-9-_]{1,32}$/;
|
const REGEX_YOUTUBE_PLAYLIST_SHORT = /^PL[a-zA-Z0-9-_]{1,32}$/;
|
||||||
const REGEX_SOUNDCLOUD =
|
const REGEX_SOUNDCLOUD = /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/;
|
||||||
/^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/;
|
const REGEX_SOUNDCLOUD_PLAYLIST = /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/(sets\/.+|likes|tracks)$/;
|
||||||
const REGEX_SOUNDCLOUD_PLAYLIST =
|
const REGEX_FILE = /^(https?:\/\/)?.*\..*\/.+\.(mp3|ogg|flac|wav|webm|mp4|mov|mkv|mod|s3m|it|xm)$/;
|
||||||
/^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/(sets\/.+|likes|tracks)$/;
|
|
||||||
const REGEX_FILE =
|
|
||||||
/^(https?:\/\/)?.*\..*\/.+\.(mp3|ogg|flac|wav|webm|mp4|mov|mkv|mod|s3m|it|xm)$/;
|
|
||||||
|
|
||||||
let SOUNDCLOUD_CLIENTID;
|
let SOUNDCLOUD_CLIENTID;
|
||||||
|
|
||||||
|
@ -40,9 +35,7 @@ async function getSoundcloudClientID() {
|
||||||
return SOUNDCLOUD_CLIENTID;
|
return SOUNDCLOUD_CLIENTID;
|
||||||
}
|
}
|
||||||
const page = await fetch("https://soundcloud.com").then((res) => res.text());
|
const page = await fetch("https://soundcloud.com").then((res) => res.text());
|
||||||
const scripts = page
|
const scripts = page.match(/<script crossorigin src="(.+?)"><\/script>/g).reverse();
|
||||||
.match(/<script crossorigin src="(.+?)"><\/script>/g)
|
|
||||||
.reverse();
|
|
||||||
for (const script of scripts) {
|
for (const script of scripts) {
|
||||||
const url = script.match(/src="(.+?)"/)[1];
|
const url = script.match(/src="(.+?)"/)[1];
|
||||||
const contents = await fetch(url).then((res) => res.text());
|
const contents = await fetch(url).then((res) => res.text());
|
||||||
|
@ -55,37 +48,23 @@ async function getSoundcloudClientID() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processPlaylist(
|
async function processPlaylist(url, type, shuffle = false, limit = -1, offset = 0) {
|
||||||
url,
|
|
||||||
type,
|
|
||||||
shuffle = false,
|
|
||||||
limit = -1,
|
|
||||||
offset = 0
|
|
||||||
) {
|
|
||||||
let playlist;
|
let playlist;
|
||||||
|
|
||||||
if (type === "yt") {
|
if (type === "yt") {
|
||||||
const playlistId =
|
const playlistId = url.match(REGEX_YOUTUBE_PLAYLIST)?.[4] ?? url.match(REGEX_YOUTUBE_PLAYLIST_SHORT)?.[0];
|
||||||
url.match(REGEX_YOUTUBE_PLAYLIST)?.[4] ??
|
|
||||||
url.match(REGEX_YOUTUBE_PLAYLIST_SHORT)?.[0];
|
|
||||||
if (!playlistId) return null;
|
if (!playlistId) return null;
|
||||||
|
|
||||||
const baseUrl = "/playlists/" + playlistId;
|
const baseUrl = "/playlists/" + playlistId;
|
||||||
|
|
||||||
const data = await fetch(hf.config.piped_api + baseUrl).then((res) =>
|
const data = await fetch(hf.config.piped_api + baseUrl).then((res) => res.json());
|
||||||
res.json()
|
|
||||||
);
|
|
||||||
|
|
||||||
playlist = data.relatedStreams;
|
playlist = data.relatedStreams;
|
||||||
|
|
||||||
let pageToken = data.nextpage;
|
let pageToken = data.nextpage;
|
||||||
while (pageToken?.startsWith("{")) {
|
while (pageToken?.startsWith("{")) {
|
||||||
const pageData = await fetch(
|
const pageData = await fetch(
|
||||||
hf.config.piped_api +
|
hf.config.piped_api + "/nextpage" + baseUrl + "&nextpage=" + encodeURIComponent(pageToken)
|
||||||
"/nextpage" +
|
|
||||||
baseUrl +
|
|
||||||
"&nextpage=" +
|
|
||||||
encodeURIComponent(pageToken)
|
|
||||||
).then((res) => res.json());
|
).then((res) => res.json());
|
||||||
if (pageData.nextpage) pageToken = pageData.nextpage;
|
if (pageData.nextpage) pageToken = pageData.nextpage;
|
||||||
|
|
||||||
|
@ -100,23 +79,18 @@ async function processPlaylist(
|
||||||
).then((res) => res.json());
|
).then((res) => res.json());
|
||||||
|
|
||||||
while (!userInfo.uri) {
|
while (!userInfo.uri) {
|
||||||
userInfo = await fetch(
|
userInfo = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`).then(
|
||||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
|
(res) => res.json()
|
||||||
).then((res) => res.json());
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const likesUrl =
|
const likesUrl = userInfo.uri.replace("api.", "api-v2.") + "/likes?limit=500&client_id=" + clientId;
|
||||||
userInfo.uri.replace("api.", "api-v2.") +
|
|
||||||
"/likes?limit=500&client_id=" +
|
|
||||||
clientId;
|
|
||||||
|
|
||||||
let currentLikes = await fetch(likesUrl).then((res) => res.json());
|
let currentLikes = await fetch(likesUrl).then((res) => res.json());
|
||||||
playlist = currentLikes.collection;
|
playlist = currentLikes.collection;
|
||||||
|
|
||||||
while (currentLikes.next_href != null) {
|
while (currentLikes.next_href != null) {
|
||||||
currentLikes = await fetch(
|
currentLikes = await fetch(currentLikes.next_href + "&client_id=" + clientId).then((res) => res.json());
|
||||||
currentLikes.next_href + "&client_id=" + clientId
|
|
||||||
).then((res) => res.json());
|
|
||||||
playlist = [...playlist, ...currentLikes.collection];
|
playlist = [...playlist, ...currentLikes.collection];
|
||||||
}
|
}
|
||||||
} else if (url.indexOf("/tracks")) {
|
} else if (url.indexOf("/tracks")) {
|
||||||
|
@ -125,29 +99,22 @@ async function processPlaylist(
|
||||||
).then((res) => res.json());
|
).then((res) => res.json());
|
||||||
|
|
||||||
while (!userInfo.uri) {
|
while (!userInfo.uri) {
|
||||||
userInfo = await fetch(
|
userInfo = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`).then(
|
||||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
|
(res) => res.json()
|
||||||
).then((res) => res.json());
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tracksUrl =
|
const tracksUrl = userInfo.uri.replace("api.", "api-v2.") + "/tracks?limit=500&client_id=" + clientId;
|
||||||
userInfo.uri.replace("api.", "api-v2.") +
|
|
||||||
"/tracks?limit=500&client_id=" +
|
|
||||||
clientId;
|
|
||||||
|
|
||||||
let currentTracks = await fetch(tracksUrl).then((res) => res.json());
|
let currentTracks = await fetch(tracksUrl).then((res) => res.json());
|
||||||
playlist = currentTracks.collection;
|
playlist = currentTracks.collection;
|
||||||
|
|
||||||
while (currentTracks.next_href != null) {
|
while (currentTracks.next_href != null) {
|
||||||
currentTracks = await fetch(
|
currentTracks = await fetch(currentTracks.next_href + "&client_id=" + clientId).then((res) => res.json());
|
||||||
currentTracks.next_href + "&client_id=" + clientId
|
|
||||||
).then((res) => res.json());
|
|
||||||
playlist = [...playlist, ...currentTracks.collection];
|
playlist = [...playlist, ...currentTracks.collection];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
playlist = await fetch(
|
playlist = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`)
|
||||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
|
|
||||||
)
|
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((obj) => obj.tracks);
|
.then((obj) => obj.tracks);
|
||||||
}
|
}
|
||||||
|
@ -210,21 +177,10 @@ async function createVoiceConnection(guild_id, voice_id, text_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const REGEX_HLS_AUDIO_TRACK = /#EXT-X-MEDIA:URI="(.+?)",TYPE=AUDIO,/;
|
const REGEX_HLS_AUDIO_TRACK = /#EXT-X-MEDIA:URI="(.+?)",TYPE=AUDIO,/;
|
||||||
async function enqueue({
|
async function enqueue({guild_id, voice_id, text_id, url, type, addedBy, suppress = false, queueNext = false}) {
|
||||||
guild_id,
|
|
||||||
voice_id,
|
|
||||||
text_id,
|
|
||||||
url,
|
|
||||||
type,
|
|
||||||
addedBy,
|
|
||||||
suppress = false,
|
|
||||||
queueNext = false,
|
|
||||||
}) {
|
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
|
|
||||||
const connection =
|
const connection = voiceStorage.get(guild_id) ?? (await createVoiceConnection(guild_id, voice_id, text_id));
|
||||||
voiceStorage.get(guild_id) ??
|
|
||||||
(await createVoiceConnection(guild_id, voice_id, text_id));
|
|
||||||
const textChannel = hf.bot.guilds.get(guild_id).channels.get(text_id);
|
const textChannel = hf.bot.guilds.get(guild_id).channels.get(text_id);
|
||||||
|
|
||||||
let title,
|
let title,
|
||||||
|
@ -245,9 +201,7 @@ async function enqueue({
|
||||||
id = uri.searchParams.get("v");
|
id = uri.searchParams.get("v");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info = await fetch(`${hf.config.piped_api}/streams/${id}`).then((res) =>
|
info = await fetch(`${hf.config.piped_api}/streams/${id}`).then((res) => res.json());
|
||||||
res.json()
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await textChannel.createMessage({
|
await textChannel.createMessage({
|
||||||
content: `:warning: Failed to get metadata: \`\`\`\n${err}\n\`\`\``,
|
content: `:warning: Failed to get metadata: \`\`\`\n${err}\n\`\`\``,
|
||||||
|
@ -261,35 +215,23 @@ async function enqueue({
|
||||||
const hlsUrl = new URL(info.hls);
|
const hlsUrl = new URL(info.hls);
|
||||||
const hlsBase = await fetch(info.hls)
|
const hlsBase = await fetch(info.hls)
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((data) =>
|
.then((data) => data.replaceAll("/api/manifest/", `https://${hlsUrl.hostname}/api/manifest/`));
|
||||||
data.replaceAll(
|
|
||||||
"/api/manifest/",
|
|
||||||
`https://${hlsUrl.hostname}/api/manifest/`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
media = Readable.from(
|
media = Readable.from(
|
||||||
await fetch(hlsBase.match(REGEX_HLS_AUDIO_TRACK)[1])
|
await fetch(hlsBase.match(REGEX_HLS_AUDIO_TRACK)[1])
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((data) =>
|
.then((data) => data.replaceAll("/videoplayback/", `https://${hlsUrl.hostname}/videoplayback/`))
|
||||||
data.replaceAll(
|
|
||||||
"/videoplayback/",
|
|
||||||
`https://${hlsUrl.hostname}/videoplayback/`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} else if (type == "sc") {
|
} else if (type == "sc") {
|
||||||
if (url?.startsWith("sc:"))
|
if (url?.startsWith("sc:")) url = url.replace(/^sc:/, "https://soundcloud.com/");
|
||||||
url = url.replace(/^sc:/, "https://soundcloud.com/");
|
|
||||||
const client_id = await getSoundcloudClientID();
|
const client_id = await getSoundcloudClientID();
|
||||||
|
|
||||||
const info = await fetch(
|
const info = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${client_id}`).then((res) =>
|
||||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${client_id}`
|
res.json()
|
||||||
).then((res) => res.json());
|
);
|
||||||
|
|
||||||
const formatUrl = info.media.transcodings.filter(
|
const formatUrl = info.media.transcodings.filter((obj) => !obj.snipped && obj.format.protocol == "progressive")[0]
|
||||||
(obj) => !obj.snipped && obj.format.protocol == "progressive"
|
.url;
|
||||||
)[0].url;
|
|
||||||
const streamUrl = await fetch(`${formatUrl}?client_id=${client_id}`)
|
const streamUrl = await fetch(`${formatUrl}?client_id=${client_id}`)
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((obj) => obj.url);
|
.then((obj) => obj.url);
|
||||||
|
@ -314,11 +256,7 @@ async function enqueue({
|
||||||
|
|
||||||
if (info.tags) {
|
if (info.tags) {
|
||||||
title = `${
|
title = `${
|
||||||
info.tags.artist ??
|
info.tags.artist ?? info.tags.ARTIST ?? info.tags.album_artist ?? info.tags.ALBUM_ARTIST ?? "<unknown artist>"
|
||||||
info.tags.ARTIST ??
|
|
||||||
info.tags.album_artist ??
|
|
||||||
info.tags.ALBUM_ARTIST ??
|
|
||||||
"<unknown artist>"
|
|
||||||
} - ${info.tags.title ?? info.tags.TITLE ?? "<no title>"}`;
|
} - ${info.tags.title ?? info.tags.TITLE ?? "<no title>"}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,26 +285,17 @@ async function enqueue({
|
||||||
textChannel.createMessage({
|
textChannel.createMessage({
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
title: `<:ms_tick:503341995348066313> Added to queue ${
|
title: `<:ms_tick:503341995348066313> Added to queue ${queueNext === true ? "(next up)" : ""}`,
|
||||||
queueNext === true ? "(next up)" : ""
|
|
||||||
}`,
|
|
||||||
color: 0x00cc00,
|
color: 0x00cc00,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "Title",
|
name: "Title",
|
||||||
value: (title !== url ? `[${title}](${url})` : url).substring(
|
value: (title !== url ? `[${title}](${url})` : url).substring(0, 1024),
|
||||||
0,
|
|
||||||
1024
|
|
||||||
),
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Length",
|
name: "Length",
|
||||||
value: stream
|
value: stream ? "<continuous>" : length ? formatTime(length) : "<unknown>",
|
||||||
? "<continuous>"
|
|
||||||
: length
|
|
||||||
? formatTime(length)
|
|
||||||
: "<unknown>",
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -393,10 +322,7 @@ async function enqueue({
|
||||||
await connection.connection.play(media, {
|
await connection.connection.play(media, {
|
||||||
inlineVolume: true,
|
inlineVolume: true,
|
||||||
voiceDataTimeout: -1,
|
voiceDataTimeout: -1,
|
||||||
inputArgs: [
|
inputArgs: ["-protocol_whitelist", "file,http,https,tcp,tls,pipe,data,crypto"],
|
||||||
"-protocol_whitelist",
|
|
||||||
"file,http,https,tcp,tls,pipe,data,crypto",
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
textChannel.createMessage({
|
textChannel.createMessage({
|
||||||
|
@ -407,19 +333,12 @@ async function enqueue({
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "Title",
|
name: "Title",
|
||||||
value: (title && title != url
|
value: (title && title != url ? `[${title}](${url})` : url).substring(0, 1024),
|
||||||
? `[${title}](${url})`
|
|
||||||
: url
|
|
||||||
).substring(0, 1024),
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Length",
|
name: "Length",
|
||||||
value: stream
|
value: stream ? "<continuous>" : length ? formatTime(length) : "<unknown>",
|
||||||
? "<continuous>"
|
|
||||||
: length
|
|
||||||
? formatTime(length)
|
|
||||||
: "<unknown>",
|
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -448,20 +367,17 @@ async function enqueue({
|
||||||
}
|
}
|
||||||
|
|
||||||
async function youtubeSearch(msg, str) {
|
async function youtubeSearch(msg, str) {
|
||||||
const {items} = await fetch(
|
const {items} = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(str)}&filter=videos`).then((x) =>
|
||||||
`${hf.config.piped_api}/search?q=${encodeURIComponent(str)}&filter=videos`
|
x.json()
|
||||||
).then((x) => x.json());
|
);
|
||||||
|
|
||||||
const selection = items.map((item) => ({
|
const selection = items.map((item) => ({
|
||||||
value: "https://youtube.com" + item.url,
|
value: "https://youtube.com" + item.url,
|
||||||
key: item.url.replace("/watch?v=", ""),
|
key: item.url.replace("/watch?v=", ""),
|
||||||
display: `${parseHtmlEntities(item.title).substring(0, 99)}${
|
display: `${parseHtmlEntities(item.title).substring(0, 99)}${parseHtmlEntities(item.title).length > 99 ? "…" : ""}`,
|
||||||
parseHtmlEntities(item.title).length > 99 ? "…" : ""
|
description: `from ${parseHtmlEntities(item.uploaderName).substring(0, 95)}${
|
||||||
|
parseHtmlEntities(item.uploaderName).length > 95 ? "…" : ""
|
||||||
}`,
|
}`,
|
||||||
description: `from ${parseHtmlEntities(item.uploaderName).substring(
|
|
||||||
0,
|
|
||||||
95
|
|
||||||
)}${parseHtmlEntities(item.uploaderName).length > 95 ? "…" : ""}`,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -478,12 +394,7 @@ command.addAlias("m");
|
||||||
command.category = "misc";
|
command.category = "misc";
|
||||||
command.helpText = "Music";
|
command.helpText = "Music";
|
||||||
command.usage = "help";
|
command.usage = "help";
|
||||||
command.callback = async function (
|
command.callback = async function (msg, line, args, {shuffle = false, limit = -1, offset = 0, next = false}) {
|
||||||
msg,
|
|
||||||
line,
|
|
||||||
args,
|
|
||||||
{shuffle = false, limit = -1, offset = 0, next = false}
|
|
||||||
) {
|
|
||||||
if (!msg.guildID) return "This command can only be used in guilds.";
|
if (!msg.guildID) return "This command can only be used in guilds.";
|
||||||
|
|
||||||
const subcommand = args.shift();
|
const subcommand = args.shift();
|
||||||
|
@ -503,10 +414,7 @@ command.callback = async function (
|
||||||
let type;
|
let type;
|
||||||
let playlist = false;
|
let playlist = false;
|
||||||
|
|
||||||
if (
|
if (REGEX_YOUTUBE_PLAYLIST.test(argStr) || REGEX_YOUTUBE_PLAYLIST_SHORT.test(argStr)) {
|
||||||
REGEX_YOUTUBE_PLAYLIST.test(argStr) ||
|
|
||||||
REGEX_YOUTUBE_PLAYLIST_SHORT.test(argStr)
|
|
||||||
) {
|
|
||||||
type = "yt";
|
type = "yt";
|
||||||
playlist = true;
|
playlist = true;
|
||||||
} else if (REGEX_SOUNDCLOUD_PLAYLIST.test(argStr)) {
|
} else if (REGEX_SOUNDCLOUD_PLAYLIST.test(argStr)) {
|
||||||
|
@ -533,25 +441,17 @@ command.callback = async function (
|
||||||
const statusMessage = await msg.channel.createMessage({
|
const statusMessage = await msg.channel.createMessage({
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
title:
|
title: "<a:loading:493087964918972426> Processing playlist...",
|
||||||
"<a:loading:493087964918972426> Processing playlist...",
|
|
||||||
description: `Fetching tracks...`,
|
description: `Fetching tracks...`,
|
||||||
color: 0xcc0088,
|
color: 0xcc0088,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const playlist = await processPlaylist(
|
const playlist = await processPlaylist(argStr, type, shuffle, limit, offset);
|
||||||
argStr,
|
|
||||||
type,
|
|
||||||
shuffle,
|
|
||||||
limit,
|
|
||||||
offset
|
|
||||||
);
|
|
||||||
await statusMessage.edit({
|
await statusMessage.edit({
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
title:
|
title: "<a:loading:493087964918972426> Processing playlist...",
|
||||||
"<a:loading:493087964918972426> Processing playlist...",
|
|
||||||
description: `${playlist.length} tracks`,
|
description: `${playlist.length} tracks`,
|
||||||
color: 0xcc0088,
|
color: 0xcc0088,
|
||||||
},
|
},
|
||||||
|
@ -562,9 +462,7 @@ command.callback = async function (
|
||||||
if (type == "yt") {
|
if (type == "yt") {
|
||||||
url = "https://youtube.com" + track.url;
|
url = "https://youtube.com" + track.url;
|
||||||
} else if (type == "sc") {
|
} else if (type == "sc") {
|
||||||
url = track.track
|
url = track.track ? track.track.permalink_url : track.permalink_url;
|
||||||
? track.track.permalink_url
|
|
||||||
: track.permalink_url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await enqueue({
|
await enqueue({
|
||||||
|
@ -599,20 +497,13 @@ command.callback = async function (
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (argStr.match(/^https?:\/\//)) {
|
if (argStr.match(/^https?:\/\//)) {
|
||||||
let contentType = await fetch(argStr, {method: "HEAD"}).then(
|
let contentType = await fetch(argStr, {method: "HEAD"}).then((res) => res.headers.get("Content-Type"));
|
||||||
(res) => res.headers.get("Content-Type")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!contentType) {
|
if (!contentType) {
|
||||||
contentType = await fetch(argStr, {method: "GET"}).then((res) =>
|
contentType = await fetch(argStr, {method: "GET"}).then((res) => res.headers.get("Content-Type"));
|
||||||
res.headers.get("Content-Type")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (contentType.startsWith("audio/") || contentType.startsWith("video/")) {
|
||||||
contentType.startsWith("audio/") ||
|
|
||||||
contentType.startsWith("video/")
|
|
||||||
) {
|
|
||||||
await enqueue({
|
await enqueue({
|
||||||
guild_id: msg.guildID,
|
guild_id: msg.guildID,
|
||||||
voice_id: msg.member.voiceState.channelID,
|
voice_id: msg.member.voiceState.channelID,
|
||||||
|
@ -684,30 +575,22 @@ command.callback = async function (
|
||||||
return "You are not in a voice channel.";
|
return "You are not in a voice channel.";
|
||||||
}
|
}
|
||||||
case "np": {
|
case "np": {
|
||||||
if (!voiceStorage.has(msg.guildID))
|
if (!voiceStorage.has(msg.guildID)) return "The bot is not in a voice channel.";
|
||||||
return "The bot is not in a voice channel.";
|
|
||||||
|
|
||||||
const connection = voiceStorage.get(msg.guildID);
|
const connection = voiceStorage.get(msg.guildID);
|
||||||
const nowPlaying = connection.nowplaying;
|
const nowPlaying = connection.nowplaying;
|
||||||
if (!nowPlaying || !connection.connection.playing)
|
if (!nowPlaying || !connection.connection.playing) return "Nothing is currently playing.";
|
||||||
return "Nothing is currently playing.";
|
|
||||||
|
|
||||||
const position = Date.now() - nowPlaying.start;
|
const position = Date.now() - nowPlaying.start;
|
||||||
|
|
||||||
const timeEnd =
|
const timeEnd = nowPlaying.length == 0 ? "\u221e" : formatTime(nowPlaying.length);
|
||||||
nowPlaying.length == 0 ? "\u221e" : formatTime(nowPlaying.length);
|
|
||||||
const timePos = formatTime(position);
|
const timePos = formatTime(position);
|
||||||
|
|
||||||
const progress =
|
const progress = nowPlaying.length == 0 ? 1 : position / nowPlaying.length;
|
||||||
nowPlaying.length == 0 ? 1 : position / nowPlaying.length;
|
|
||||||
const barLength = Math.round(progress * NOWPLAYING_BAR_LENGTH);
|
const barLength = Math.round(progress * NOWPLAYING_BAR_LENGTH);
|
||||||
|
|
||||||
const bar = `\`[${"=".repeat(barLength)}${" ".repeat(
|
const bar = `\`[${"=".repeat(barLength)}${" ".repeat(NOWPLAYING_BAR_LENGTH - barLength)}]\``;
|
||||||
NOWPLAYING_BAR_LENGTH - barLength
|
const time = `\`${timePos}${" ".repeat(NOWPLAYING_BAR_LENGTH + 2 - timePos.length - timeEnd.length)}${timeEnd}\``;
|
||||||
)}]\``;
|
|
||||||
const time = `\`${timePos}${" ".repeat(
|
|
||||||
NOWPLAYING_BAR_LENGTH + 2 - timePos.length - timeEnd.length
|
|
||||||
)}${timeEnd}\``;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embed: {
|
embed: {
|
||||||
|
@ -738,8 +621,7 @@ command.callback = async function (
|
||||||
}
|
}
|
||||||
case "queue":
|
case "queue":
|
||||||
case "q": {
|
case "q": {
|
||||||
if (!voiceStorage.has(msg.guildID))
|
if (!voiceStorage.has(msg.guildID)) return "The bot is not in a voice channel";
|
||||||
return "The bot is not in a voice channel";
|
|
||||||
|
|
||||||
const connection = voiceStorage.get(msg.guildID);
|
const connection = voiceStorage.get(msg.guildID);
|
||||||
const queue = connection.queue;
|
const queue = connection.queue;
|
||||||
|
@ -754,11 +636,9 @@ command.callback = async function (
|
||||||
const item = queue[index];
|
const item = queue[index];
|
||||||
fields.push({
|
fields.push({
|
||||||
name: item.title ?? item.url,
|
name: item.title ?? item.url,
|
||||||
value: `${item.title ? `[Link](${item.url}) - ` : ""}${formatTime(
|
value: `${item.title ? `[Link](${item.url}) - ` : ""}${formatTime(item.length)}\nAdded by: <@${
|
||||||
item.length
|
item.addedBy
|
||||||
)}\nAdded by: <@${item.addedBy}>\n<t:${Math.floor(
|
}>\n<t:${Math.floor(nextTrack / 1000)}:R>`,
|
||||||
nextTrack / 1000
|
|
||||||
)}:R>`,
|
|
||||||
inline: true,
|
inline: true,
|
||||||
});
|
});
|
||||||
nextTrack += item.length;
|
nextTrack += item.length;
|
||||||
|
@ -796,8 +676,7 @@ command.callback = async function (
|
||||||
if (queue.length === 0) return "Nothing else is currently queued";
|
if (queue.length === 0) return "Nothing else is currently queued";
|
||||||
|
|
||||||
const hasManageMessages = msg.member.permissions.has("manageMessages");
|
const hasManageMessages = msg.member.permissions.has("manageMessages");
|
||||||
if (!hasManageMessages)
|
if (!hasManageMessages) queue = queue.filter((item) => item.addedBy == msg.member.id);
|
||||||
queue = queue.filter((item) => item.addedBy == msg.member.id);
|
|
||||||
|
|
||||||
if (queue.length === 0) return "You currently have nothing queued";
|
if (queue.length === 0) return "You currently have nothing queued";
|
||||||
|
|
||||||
|
@ -809,9 +688,7 @@ command.callback = async function (
|
||||||
return {
|
return {
|
||||||
key: item.id,
|
key: item.id,
|
||||||
display: (item.title ?? item.url).substr(0, 100),
|
display: (item.title ?? item.url).substr(0, 100),
|
||||||
description: hasManageMessages
|
description: hasManageMessages ? `Added by: ${formatUsername(user)}` : "",
|
||||||
? `Added by: ${formatUsername(user)}`
|
|
||||||
: "",
|
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
30000,
|
30000,
|
||||||
|
@ -819,9 +696,7 @@ command.callback = async function (
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Array.isArray(toRemove)) {
|
if (Array.isArray(toRemove)) {
|
||||||
connection.queue = connection.queue.filter(
|
connection.queue = connection.queue.filter((item) => !toRemove.includes(item.id));
|
||||||
(item) => !toRemove.includes(item.id)
|
|
||||||
);
|
|
||||||
return `Removed ${toRemove.length} item(s).`;
|
return `Removed ${toRemove.length} item(s).`;
|
||||||
} else {
|
} else {
|
||||||
return toRemove;
|
return toRemove;
|
||||||
|
|
|
@ -16,18 +16,12 @@ const {resolve} = require("path");
|
||||||
const timer = require("../lib/timer");
|
const timer = require("../lib/timer");
|
||||||
const logger = require("../lib/logger");
|
const logger = require("../lib/logger");
|
||||||
|
|
||||||
if (!fs.existsSync(resolve(__dirname, "..", "..", "private_reminders.json")))
|
if (!fs.existsSync(resolve(__dirname, "..", "..", "private_reminders.json"))) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const tzFormatterCache = {};
|
const tzFormatterCache = {};
|
||||||
const dmCache = {};
|
const dmCache = {};
|
||||||
|
|
||||||
const reminderData = require(resolve(
|
const reminderData = require(resolve(__dirname, "..", "..", "private_reminders.json"));
|
||||||
__dirname,
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"private_reminders.json"
|
|
||||||
));
|
|
||||||
|
|
||||||
hf.database.run(
|
hf.database.run(
|
||||||
"CREATE TABLE IF NOT EXISTS private_reminders (user TEXT NOT NULL PRIMARY KEY, last_run TEXT NOT NULL) WITHOUT ROWID"
|
"CREATE TABLE IF NOT EXISTS private_reminders (user TEXT NOT NULL PRIMARY KEY, last_run TEXT NOT NULL) WITHOUT ROWID"
|
||||||
|
@ -97,10 +91,7 @@ hf.bot.once("ready", () => {
|
||||||
const lastRan = await getLastRun(data.user);
|
const lastRan = await getLastRun(data.user);
|
||||||
|
|
||||||
if (date != lastRan && hour == data.hour && minutes == 0) {
|
if (date != lastRan && hour == data.hour && minutes == 0) {
|
||||||
logger.verbose(
|
logger.verbose("privateReminders", `attempting to send reminder to ${data.user}`);
|
||||||
"privateReminders",
|
|
||||||
`attempting to send reminder to ${data.user}`
|
|
||||||
);
|
|
||||||
if (channel != null) {
|
if (channel != null) {
|
||||||
await channel.createMessage({
|
await channel.createMessage({
|
||||||
content: ":alarm_clock: " + data.message,
|
content: ":alarm_clock: " + data.message,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const Command = require("../lib/command.js");
|
const Command = require("../lib/command.js");
|
||||||
|
|
||||||
const {selectionMessage} = require("../lib/utils.js");
|
const {selectionMessage} = require("../util/selection.js");
|
||||||
|
|
||||||
hf.database.run(
|
hf.database.run(
|
||||||
"CREATE TABLE IF NOT EXISTS roleme (guild TEXT NOT NULL PRIMARY KEY, roles TEXT NOT NULL) WITHOUT ROWID"
|
"CREATE TABLE IF NOT EXISTS roleme (guild TEXT NOT NULL PRIMARY KEY, roles TEXT NOT NULL) WITHOUT ROWID"
|
||||||
|
@ -74,11 +74,7 @@ async function lookupRole(msg, str, filter) {
|
||||||
selection.splice(20);
|
selection.splice(20);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await selectionMessage(
|
return await selectionMessage(msg, "Multiple roles found, please pick from this list:", selection);
|
||||||
msg,
|
|
||||||
"Multiple roles found, please pick from this list:",
|
|
||||||
selection
|
|
||||||
);
|
|
||||||
} catch (out) {
|
} catch (out) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -129,9 +125,7 @@ roleme.callback = async function (msg, line, args) {
|
||||||
|
|
||||||
const roles = await getRoles(msg.guildID);
|
const roles = await getRoles(msg.guildID);
|
||||||
|
|
||||||
const role = await lookupRole(msg, argStr, (role) =>
|
const role = await lookupRole(msg, argStr, (role) => roles.includes(role.id));
|
||||||
roles.includes(role.id)
|
|
||||||
);
|
|
||||||
if (role === "No results") return role;
|
if (role === "No results") return role;
|
||||||
|
|
||||||
if (!roles.includes(role.id)) {
|
if (!roles.includes(role.id)) {
|
||||||
|
@ -169,9 +163,7 @@ roleme.callback = async function (msg, line, args) {
|
||||||
|
|
||||||
const roles = await getRoles(msg.guildID);
|
const roles = await getRoles(msg.guildID);
|
||||||
|
|
||||||
const role = await lookupRole(msg, argStr, (role) =>
|
const role = await lookupRole(msg, argStr, (role) => roles.includes(role.id));
|
||||||
roles.includes(role.id)
|
|
||||||
);
|
|
||||||
if (role === "No results") return role;
|
if (role === "No results") return role;
|
||||||
|
|
||||||
if (!roles.includes(role.id)) {
|
if (!roles.includes(role.id)) {
|
||||||
|
@ -225,11 +217,7 @@ roleme.callback = async function (msg, line, args) {
|
||||||
|
|
||||||
const roles = await getRoles(msg.guildID);
|
const roles = await getRoles(msg.guildID);
|
||||||
|
|
||||||
const role = await lookupRole(
|
const role = await lookupRole(msg, (subcommand + " " + argStr).trim(), (role) => roles.includes(role.id));
|
||||||
msg,
|
|
||||||
(subcommand + " " + argStr).trim(),
|
|
||||||
(role) => roles.includes(role.id)
|
|
||||||
);
|
|
||||||
if (role === "No results") return role;
|
if (role === "No results") return role;
|
||||||
|
|
||||||
if (!roles.includes(role.id)) return "Role not in assignable roles.";
|
if (!roles.includes(role.id)) return "Role not in assignable roles.";
|
||||||
|
|
File diff suppressed because it is too large
Load diff
313
src/modules/utility/appinfo.js
Normal file
313
src/modules/utility/appinfo.js
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
const InteractionCommand = require("../../lib/interactionCommand.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
APIEndpoints,
|
||||||
|
API_URL,
|
||||||
|
ApplicationCommandOptionTypes,
|
||||||
|
ApplicationTypes,
|
||||||
|
CDNEndpoints,
|
||||||
|
Games,
|
||||||
|
} = require("../../util/dconstants.js");
|
||||||
|
const {
|
||||||
|
ApplicationFlagNames,
|
||||||
|
Icons,
|
||||||
|
RegExp: {Snowflake: SNOWFLAKE_REGEX},
|
||||||
|
} = require("../../util/constants.js");
|
||||||
|
const {snowflakeToTimestamp} = require("../../util/time.js");
|
||||||
|
const {getGuild, safeString, formatUsername, flagsFromInt} = require("../../util/misc.js");
|
||||||
|
|
||||||
|
const appinfo = new Command("appinfo");
|
||||||
|
appinfo.category = "utility";
|
||||||
|
appinfo.helpText = "Get information on an application";
|
||||||
|
appinfo.usage = "[application id]";
|
||||||
|
appinfo.addAlias("ainfo");
|
||||||
|
appinfo.addAlias("ai");
|
||||||
|
appinfo.callback = async function (msg, line) {
|
||||||
|
if (!line || line === "") return "Arguments required.";
|
||||||
|
if (!SNOWFLAKE_REGEX.test(line)) return "Not a snowflake.";
|
||||||
|
|
||||||
|
const snowflake = line.match(SNOWFLAKE_REGEX)[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const _app = await hf.bot.requestHandler.request("GET", APIEndpoints.APPLICATION_RPC(snowflake), false);
|
||||||
|
|
||||||
|
let app = _app;
|
||||||
|
const game = Games.find((game) => game.id == app.id);
|
||||||
|
if (game) {
|
||||||
|
app = Object.assign(app, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
const assets = await hf.bot.requestHandler.request("GET", CDNEndpoints.APPLICATION_ASSETS(app.id), false);
|
||||||
|
|
||||||
|
const embed = {
|
||||||
|
title: `${app.name}`,
|
||||||
|
description: app.description.length > 0 ? app.description : "*No description*.",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Created",
|
||||||
|
value: `<t:${Math.floor(snowflakeToTimestamp(app.id) / 1000)}:R>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (app.icon) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: CDNEndpoints.APP_ICON(app.id, app.icon),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.type) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Type",
|
||||||
|
value: `${ApplicationTypes[app.type] ?? "<unknown type>"} (\`${app.type}\`)`,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.guild_id) {
|
||||||
|
const guild = await getGuild(app.guild_id);
|
||||||
|
if (guild) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Guild",
|
||||||
|
value: `${guild.data.name} (\`${app.guild_id}\`)`,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Guild ID",
|
||||||
|
value: `\`${app.guild_id}\``,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.tags) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Tags",
|
||||||
|
value: app.tags.join(", "),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.publishers || app.developers) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Game Companies",
|
||||||
|
value: `**Developers:** ${
|
||||||
|
app.developers?.length > 0 ? app.developers.map((x) => x.name).join(", ") : "<unknown>"
|
||||||
|
}\n**Publishers:** ${app.publishers?.length > 0 ? app.publishers.map((x) => x.name).join(", ") : "<unknown>"}`,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.executables) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Game Executables",
|
||||||
|
value: app.executables
|
||||||
|
.map((exe) => `${Icons.os[exe.os] ?? "\u2753"} \`${exe.name}\`${exe.is_launcher ? " (launcher)" : ""}`)
|
||||||
|
.join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.third_party_skus) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Game Distributors",
|
||||||
|
value: app.third_party_skus
|
||||||
|
.map((sku) =>
|
||||||
|
sku.distributor == "steam"
|
||||||
|
? `[Steam](https://steamdb.info/app/${sku.id})`
|
||||||
|
: sku.distributor == "discord"
|
||||||
|
? `[Discord](https://discord.com/store/skus/${sku.id})`
|
||||||
|
: `${sku.distributor
|
||||||
|
.split("_")
|
||||||
|
.map((x) => x[0].toUpperCase() + x.substring(1).toLowerCase())
|
||||||
|
.join(" ")
|
||||||
|
.replace(" Net", ".net")}: \`${sku.id}\``
|
||||||
|
)
|
||||||
|
.join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.bot_public != null || app.integration_public != null) {
|
||||||
|
if (
|
||||||
|
(app.bot_public && !app.bot_require_code_grant) ||
|
||||||
|
(app.integration_public && !app.integration_require_code_grant)
|
||||||
|
) {
|
||||||
|
let scope = "bot";
|
||||||
|
let permissions = "";
|
||||||
|
if (app.install_params) {
|
||||||
|
if (app.install_params.scopes) {
|
||||||
|
scope = app.install_params.scopes.join("+");
|
||||||
|
}
|
||||||
|
if (app.install_params.permissions) {
|
||||||
|
permissions = "&permissions=" + app.install_params.permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
embed.url = `https://discord.com/oauth2/authorize?client_id=${app.id}&scope=${scope}${permissions}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bot = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(app.id), true);
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Bot",
|
||||||
|
value: formatUsername(bot) + ((bot.flags & 65536) != 0 ? " \u2713" : ""),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Bot",
|
||||||
|
value: "<app id and bot id mismatch or other error>",
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.custom_install_url) {
|
||||||
|
embed.url = app.custom_install_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.flags > 0) {
|
||||||
|
const flags = flagsFromInt(app.flags, ApplicationFlagNames, false).split("\n");
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Flags",
|
||||||
|
value: "- " + flags.slice(0, Math.ceil(flags.length / 2)).join("\n- "),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
if (flags.length > 1)
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: "- " + flags.slice(Math.ceil(flags.length / 2), flags.length).join("\n- "),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const images = [];
|
||||||
|
if (app.icon) {
|
||||||
|
images.push(`[Icon](${embed.thumbnail.url})`);
|
||||||
|
}
|
||||||
|
if (app.cover_image) {
|
||||||
|
images.push(`[Cover](${CDNEndpoints.APP_ICON(app.id, app.cover_image)}?size=4096)`);
|
||||||
|
}
|
||||||
|
if (app.splash) {
|
||||||
|
images.push(`[Splash](${CDNEndpoints.APP_ICON(app.id, app.splash)}?size=4096)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = [];
|
||||||
|
if (app.terms_of_service_url) {
|
||||||
|
links.push(`[Terms of Service](${app.terms_of_service_url})`);
|
||||||
|
}
|
||||||
|
if (app.privacy_policy_url) {
|
||||||
|
links.push(`[Privacy Policy](${app.privacy_policy_url})`);
|
||||||
|
}
|
||||||
|
if (assets.length > 0) {
|
||||||
|
links.push(`[Assets](${API_URL + CDNEndpoints.APPLICATION_ASSETS(app.id)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.length > 0 || links.length > 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: (images.join(" | ") + "\n" + links.join(" | ")).trim(),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assets.length > 0) {
|
||||||
|
if (images.length == 0 && links.length == 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: "\u200b",
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappedAssets = assets.map(
|
||||||
|
(asset) =>
|
||||||
|
`[${asset.name.length > 32 ? asset.name.substring(0, 32) + "\u2026" : asset.name}](${CDNEndpoints.APP_ASSET(
|
||||||
|
app.id,
|
||||||
|
asset.id
|
||||||
|
)})`
|
||||||
|
);
|
||||||
|
|
||||||
|
let left = "- " + mappedAssets.slice(0, Math.ceil(mappedAssets.length / 2)).join("\n- ");
|
||||||
|
let right = "- " + mappedAssets.slice(Math.ceil(mappedAssets.length / 2), mappedAssets.length).join("\n- ");
|
||||||
|
|
||||||
|
if (left.length > 1024 || right.length > 1024) {
|
||||||
|
const linklessAssets = assets.map((asset) =>
|
||||||
|
asset.name.length > 32 ? asset.name.substring(0, 32) + "\u2026" : asset.name
|
||||||
|
);
|
||||||
|
left = "- " + linklessAssets.slice(0, Math.ceil(linklessAssets.length / 2)).join("\n- ");
|
||||||
|
right = "- " + linklessAssets.slice(Math.ceil(linklessAssets.length / 2), linklessAssets.length).join("\n- ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.length <= 1024 && right.length <= 1024) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Assets (${assets.length})`,
|
||||||
|
value: left,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
if (mappedAssets.length > 1)
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: right,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const assetList = assets
|
||||||
|
.map((asset) => (asset.name.length > 32 ? asset.name.substring(0, 31) + "\u2026" : asset.name))
|
||||||
|
.join(", ");
|
||||||
|
if (assetList.length <= 1024) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Assets (${assets.length})`,
|
||||||
|
value: assetList,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Assets (${assets.length})`,
|
||||||
|
value: "*Exceeds 1024 characters.*",
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {embed};
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message === "Unknown Application") {
|
||||||
|
try {
|
||||||
|
const bot = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(snowflake), true);
|
||||||
|
if (bot) {
|
||||||
|
return "Application has been deleted.";
|
||||||
|
} else {
|
||||||
|
return "ID provided does not point to a valid application.";
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return "ID provided does not point to a valid application.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return `:warning: Got error \`${safeString(error)}\``;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(appinfo);
|
||||||
|
|
||||||
|
const appinfoInteraction = new InteractionCommand("appinfo");
|
||||||
|
appinfoInteraction.helpText = "Get information on an application";
|
||||||
|
appinfoInteraction.options.id = {
|
||||||
|
name: "id",
|
||||||
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
description: "Application ID to get info for",
|
||||||
|
required: true,
|
||||||
|
default: "",
|
||||||
|
};
|
||||||
|
appinfoInteraction.callback = async function (interaction) {
|
||||||
|
const id = this.getOption(interaction, "id");
|
||||||
|
|
||||||
|
return appinfo.callback(interaction, id);
|
||||||
|
};
|
||||||
|
hf.registerCommand(appinfoInteraction);
|
98
src/modules/utility/avatar.js
Normal file
98
src/modules/utility/avatar.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const {CDNEndpoints} = require("../../util/dconstants.js");
|
||||||
|
|
||||||
|
const {formatUsername, getDefaultAvatar} = require("../../util/misc.js");
|
||||||
|
const {lookupUser} = require("../../util/selection.js");
|
||||||
|
|
||||||
|
const avatar = new Command("avatar");
|
||||||
|
avatar.category = "utility";
|
||||||
|
avatar.helpText = "Get avatar of a user";
|
||||||
|
avatar.usage = "<user>";
|
||||||
|
avatar.callback = async function (msg, line, [user], {server, guild}) {
|
||||||
|
if (server || guild) {
|
||||||
|
if (!msg.guildID) {
|
||||||
|
return "`--server/--guild` can only be used within guilds.";
|
||||||
|
} else {
|
||||||
|
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
||||||
|
const url = CDNEndpoints.GUILD_ICON(guild.id, guild.icon);
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: "Server Icon",
|
||||||
|
url,
|
||||||
|
image: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (user) {
|
||||||
|
const lookup = await lookupUser(msg, user);
|
||||||
|
if (lookup == "No results" || lookup == "Canceled" || lookup == "Request timed out") {
|
||||||
|
return lookup;
|
||||||
|
} else {
|
||||||
|
let member = lookup;
|
||||||
|
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
||||||
|
if (guild) {
|
||||||
|
if (guild.members.has(lookup.id)) {
|
||||||
|
member = guild.members.get(lookup.id);
|
||||||
|
} else {
|
||||||
|
const fetched = await guild.fetchMembers({
|
||||||
|
userIDs: [lookup.id],
|
||||||
|
});
|
||||||
|
member = fetched[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseEmbed = {
|
||||||
|
title: `Avatar for \`${formatUsername(member)}\``,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultAvatar = getDefaultAvatar(member.id, member.user?.discriminator ?? member.discriminator ?? 0);
|
||||||
|
const normalAvatar = member.user ? member.user.avatar ?? defaultAvatar : member.avatar ?? defaultAvatar;
|
||||||
|
const guildAvatar = guild ? member.avatar : null;
|
||||||
|
|
||||||
|
const normalUrl = CDNEndpoints.USER_AVATAR(member.id, normalAvatar);
|
||||||
|
const guildUrl = CDNEndpoints.GUILD_MEMBER_AVATAR(guild.id, member.id, guildAvatar);
|
||||||
|
|
||||||
|
baseEmbed.description =
|
||||||
|
`[Normal avatar](${normalUrl})` + (guild && guildAvatar ? `\n[Guild avatar](${guildUrl})` : "");
|
||||||
|
baseEmbed.url = normalUrl;
|
||||||
|
|
||||||
|
const guildEmbed = {...baseEmbed};
|
||||||
|
baseEmbed.image = {url: normalUrl};
|
||||||
|
guildEmbed.image = {url: guildUrl};
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [baseEmbed, guildAvatar && guildEmbed].filter((x) => x != null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const guild = msg.channel.guild ?? hf.bot.guilds.get(msg.guildID);
|
||||||
|
|
||||||
|
const baseEmbed = {
|
||||||
|
title: `Avatar for \`${formatUsername(msg.author)}\``,
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalAvatar = msg.author?.avatar ?? getDefaultAvatar(msg.author.id, msg.author.discriminator);
|
||||||
|
const guildAvatar = msg.member?.avatar;
|
||||||
|
|
||||||
|
const normalUrl = CDNEndpoints.USER_AVATAR(msg.author.id, normalAvatar);
|
||||||
|
const guildUrl = CDNEndpoints.GUILD_MEMBER_AVATAR(guild.id, msg.author.id, guildAvatar);
|
||||||
|
|
||||||
|
baseEmbed.description =
|
||||||
|
`[Normal avatar](${normalUrl})` + (guild && guildAvatar ? `\n[Guild avatar](${guildUrl})` : "");
|
||||||
|
baseEmbed.url = normalUrl;
|
||||||
|
|
||||||
|
const guildEmbed = {...baseEmbed};
|
||||||
|
baseEmbed.image = {url: normalUrl};
|
||||||
|
guildEmbed.image = {url: guildUrl};
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [baseEmbed, guildAvatar && guildEmbed].filter((x) => x != null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(avatar);
|
76
src/modules/utility/banner.js
Normal file
76
src/modules/utility/banner.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const {APIEndpoints, CDNEndpoints} = require("../../util/dconstants.js");
|
||||||
|
|
||||||
|
const {formatUsername} = require("../../util/misc.js");
|
||||||
|
const {lookupUser} = require("../../util/selection.js");
|
||||||
|
|
||||||
|
const banner = new Command("banner");
|
||||||
|
banner.category = "utility";
|
||||||
|
banner.helpText = "Get banner of a user";
|
||||||
|
banner.usage = "<user>";
|
||||||
|
banner.callback = async function (msg, line, [user], {server, guild}) {
|
||||||
|
let id = msg.author.id;
|
||||||
|
|
||||||
|
if (server || guild) {
|
||||||
|
if (!msg.guildID) {
|
||||||
|
return "`--server/--guild` can only be used within guilds.";
|
||||||
|
} else {
|
||||||
|
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
||||||
|
|
||||||
|
if (!guild.banner) return "This guild does not have a banner.";
|
||||||
|
|
||||||
|
const url = CDNEndpoints.BANNER(guild.id, guild.banner);
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: "Server Banner",
|
||||||
|
url,
|
||||||
|
image: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (user) {
|
||||||
|
const lookup = await lookupUser(msg, user);
|
||||||
|
if (lookup == "No results" || lookup == "Canceled" || lookup == "Request timed out") {
|
||||||
|
return lookup;
|
||||||
|
} else {
|
||||||
|
id = lookup.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userObj = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(id), true);
|
||||||
|
|
||||||
|
let memberObj;
|
||||||
|
if (msg.guildID) {
|
||||||
|
memberObj = await hf.bot.requestHandler.request("GET", APIEndpoints.GUILD_MEMBER(msg.guildID, id), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userObj.banner && !memberObj?.banner) return "This user does not have a banner.";
|
||||||
|
|
||||||
|
const url = userObj.banner && CDNEndpoints.BANNER(userObj.id, userObj.banner);
|
||||||
|
|
||||||
|
const guildUrl = memberObj?.banner && CDNEndpoints.GUILD_MEMBER_BANNER(msg.guildID, userObj.id, memberObj.banner);
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
url && {
|
||||||
|
title: `Banner for \`${formatUsername(userObj)}\``,
|
||||||
|
url,
|
||||||
|
image: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
guildUrl && {
|
||||||
|
title: `Server banner for \`${formatUsername(userObj)}\``,
|
||||||
|
url: guildUrl,
|
||||||
|
image: {
|
||||||
|
url: guildUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
].filter((x) => !!x),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
hf.registerCommand(banner);
|
71
src/modules/utility/charinfo.js
Normal file
71
src/modules/utility/charinfo.js
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
const InteractionCommand = require("../../lib/interactionCommand.js");
|
||||||
|
|
||||||
|
const {ApplicationCommandOptionTypes} = require("../../util/dconstants.js");
|
||||||
|
const {getNamesFromString} = require("../../util/unicode.js");
|
||||||
|
|
||||||
|
const charinfo = new Command("charinfo");
|
||||||
|
charinfo.category = "utility";
|
||||||
|
charinfo.helpText = "Get information about a set of characters.";
|
||||||
|
charinfo.usage = "[characters]";
|
||||||
|
charinfo.addAlias("char");
|
||||||
|
charinfo.callback = async function (msg, line) {
|
||||||
|
if (!line || line == "") {
|
||||||
|
if (msg.messageReference?.messageID) {
|
||||||
|
const reply = await msg.channel.getMessage(msg.messageReference.messageID);
|
||||||
|
line = reply.content;
|
||||||
|
} else {
|
||||||
|
return "Arguments or reply required.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const names = await getNamesFromString(line);
|
||||||
|
const chars = [...line];
|
||||||
|
const lines = names
|
||||||
|
.map(
|
||||||
|
([code, name], index) =>
|
||||||
|
`[\`\\u${code}${
|
||||||
|
chars[index].length > 1
|
||||||
|
? ` (${chars[index]
|
||||||
|
.split("")
|
||||||
|
.map((c) => `\\u${c.codePointAt().toString(16)}`)
|
||||||
|
.join("")})`
|
||||||
|
: ""
|
||||||
|
}\`](<http://www.fileformat.info/info/unicode/char/${code}>): ${name} - ${chars[index]
|
||||||
|
.replace("_", "\\_")
|
||||||
|
.replace("`", "\\`")
|
||||||
|
.replace("*", "\\*")}`
|
||||||
|
)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
if (lines.length > 2000) {
|
||||||
|
return {
|
||||||
|
content: "Output too long:",
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
file: lines,
|
||||||
|
filename: "message.txt",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(charinfo);
|
||||||
|
|
||||||
|
const charinfoInteraction = new InteractionCommand("charinfo");
|
||||||
|
charinfoInteraction.helpText = "Get information about a set of characters.";
|
||||||
|
charinfoInteraction.options.content = {
|
||||||
|
name: "content",
|
||||||
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
description: "Characters to get info for",
|
||||||
|
required: true,
|
||||||
|
default: "",
|
||||||
|
};
|
||||||
|
charinfoInteraction.callback = async function (interaction) {
|
||||||
|
const content = this.getOption(interaction, "content");
|
||||||
|
|
||||||
|
return charinfo.callback(interaction, content);
|
||||||
|
};
|
||||||
|
hf.registerCommand(charinfoInteraction);
|
98
src/modules/utility/decoration.js
Normal file
98
src/modules/utility/decoration.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const {APIEndpoints, CDNEndpoints} = require("../../util/dconstants.js");
|
||||||
|
|
||||||
|
const {formatUsername} = require("../../util/misc.js");
|
||||||
|
const {lookupUser} = require("../../util/selection.js");
|
||||||
|
|
||||||
|
const decoration = new Command("decoration");
|
||||||
|
decoration.category = "utility";
|
||||||
|
decoration.helpText = "Get decoration of a user";
|
||||||
|
decoration.usage = "<user>";
|
||||||
|
decoration.addAlias("decor");
|
||||||
|
decoration.callback = async function (msg, line, [user]) {
|
||||||
|
let id = msg.author.id;
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
const lookup = await lookupUser(msg, user);
|
||||||
|
if (lookup == "No results" || lookup == "Canceled" || lookup == "Request timed out") {
|
||||||
|
return lookup;
|
||||||
|
} else {
|
||||||
|
id = lookup.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userObj = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(id), true);
|
||||||
|
|
||||||
|
let decor, decorRes;
|
||||||
|
try {
|
||||||
|
decorRes = await fetch(`https://decor.fieryflames.dev/api/users/${id}/decoration`, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "HiddenPhox/decoration (https://gitdab.com/Cynosphere/HiddenPhox)",
|
||||||
|
},
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
|
if (decorRes.presetId) {
|
||||||
|
// horror
|
||||||
|
const presets = await fetch(`https://decor.fieryflames.dev/api/decorations/presets`, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "HiddenPhox/decoration (https://gitdab.com/Cynosphere/HiddenPhox)",
|
||||||
|
},
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
|
decorRes.preset = presets.find((p) => p.id === decorRes.presetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
decor = `https://decorcdn.fieryflames.dev/${decorRes.animated ? "a_" : ""}${decorRes.hash}.png`;
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userObj.avatar_decoration_data && !decor) return "This user does not have a decoration.";
|
||||||
|
|
||||||
|
const normalUrl =
|
||||||
|
userObj.avatar_decoration_data && CDNEndpoints.AVATAR_DECORATION(userObj.avatar_decoration_data.asset);
|
||||||
|
|
||||||
|
const baseEmbed = {
|
||||||
|
title: `Decoration for \`${formatUsername(userObj)}\``,
|
||||||
|
};
|
||||||
|
|
||||||
|
baseEmbed.fields = [];
|
||||||
|
|
||||||
|
if (normalUrl) {
|
||||||
|
const listing = await hf.bot.requestHandler
|
||||||
|
.request("GET", APIEndpoints.STORE_PUBLISHED_LISTING(userObj.avatar_decoration_data.sku_id), true)
|
||||||
|
.catch(() => {});
|
||||||
|
|
||||||
|
baseEmbed.fields.push({
|
||||||
|
name: `Discord ${userObj.avatar_decoration_data.asset.startsWith("a_") ? "(Animated)" : ""}`,
|
||||||
|
value: `**${
|
||||||
|
listing?.sku
|
||||||
|
? `[${listing?.sku?.name}](https://discord.com/shop#itemSkuId=${userObj.avatar_decoration_data.sku_id})`
|
||||||
|
: "Unknown"
|
||||||
|
}** (\`${userObj.avatar_decoration_data.sku_id}\`)\n[Image](${normalUrl})`,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decor) {
|
||||||
|
baseEmbed.fields.push({
|
||||||
|
name: `Decor ${decor.indexOf("/a_") > -1 ? "(Animated)" : ""}`,
|
||||||
|
value: `**${decorRes.alt}**\n${
|
||||||
|
decorRes.preset ? `\u3000from preset **${decorRes.preset.name}**\n` : ""
|
||||||
|
}[Image](${decor})`,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
baseEmbed.url = normalUrl || decor;
|
||||||
|
|
||||||
|
const decorEmbed = {...baseEmbed};
|
||||||
|
baseEmbed.image = {url: normalUrl || decor};
|
||||||
|
decorEmbed.image = {url: decor};
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [normalUrl && baseEmbed, decor && decorEmbed].filter((x) => x != null),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
hf.registerCommand(decoration);
|
53
src/modules/utility/flagdump.js
Normal file
53
src/modules/utility/flagdump.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const {UserFlags} = require("../../util/dconstants.js");
|
||||||
|
const {flagsFromInt, formatUsername} = require("../../util/misc.js");
|
||||||
|
|
||||||
|
const _UserFlags = Object.entries(UserFlags).filter(([name]) => name != "NONE");
|
||||||
|
for (const set of _UserFlags) {
|
||||||
|
const bits = Object.entries(set[1].toString("2").split("").reverse().map(Number));
|
||||||
|
const index = bits.find(([index, bit]) => bit == 1)[0];
|
||||||
|
set[1] = Number(index);
|
||||||
|
}
|
||||||
|
const UserFlagsMapped = Object.fromEntries(_UserFlags.map((x) => x.reverse()));
|
||||||
|
|
||||||
|
const flagdump = new Command("flagdump");
|
||||||
|
flagdump.category = "utility";
|
||||||
|
flagdump.helpText = "Dumps Discord user flags.";
|
||||||
|
flagdump.usage = "[flags or user mention]";
|
||||||
|
flagdump.callback = async function (msg, line, [numOrMention], {id, list}) {
|
||||||
|
if (!line || line == "") numOrMention = `<@${msg.author.id}>`;
|
||||||
|
|
||||||
|
const num = Number(numOrMention);
|
||||||
|
if (list) {
|
||||||
|
let allFlags = 0n;
|
||||||
|
for (const index in UserFlagsMapped) {
|
||||||
|
if (UserFlagsMapped[index] == undefined) continue;
|
||||||
|
allFlags += 1n << BigInt(index);
|
||||||
|
}
|
||||||
|
return `All flags:\n\`\`\`${flagsFromInt(allFlags, UserFlagsMapped)}\`\`\``;
|
||||||
|
} else if (/<@!?(\d+)>/.test(numOrMention) || !isNaN(id)) {
|
||||||
|
const targetId = id ?? numOrMention.match(/<@!?(\d+)>/)?.[1];
|
||||||
|
if (!targetId) return "Got null ID.";
|
||||||
|
let user = hf.bot.users.get(id);
|
||||||
|
|
||||||
|
if (!user) user = await hf.bot.requestHandler.request("GET", `/users/${id}`, true).catch(() => {});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return "Failed to get user.";
|
||||||
|
} else {
|
||||||
|
return `\`${formatUsername(user)}\`'s public flags:\n\`\`\`${flagsFromInt(
|
||||||
|
user.public_flags ?? user.publicFlags,
|
||||||
|
UserFlagsMapped
|
||||||
|
)}\`\`\``;
|
||||||
|
}
|
||||||
|
} else if (!isNaN(num)) {
|
||||||
|
return `\`\`\`\n${flagsFromInt(num, UserFlagsMapped)}\`\`\``;
|
||||||
|
} else {
|
||||||
|
return `\`${formatUsername(msg.author)}\`'s public flags:\n\`\`\`${flagsFromInt(
|
||||||
|
msg.author.publicFlags,
|
||||||
|
UserFlagsMapped
|
||||||
|
)}\`\`\``;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(flagdump);
|
636
src/modules/utility/guildinfo.js
Normal file
636
src/modules/utility/guildinfo.js
Normal file
|
@ -0,0 +1,636 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
const InteractionCommand = require("../../lib/interactionCommand.js");
|
||||||
|
|
||||||
|
const {VoiceChannel} = require("@projectdysnomia/dysnomia");
|
||||||
|
|
||||||
|
const {
|
||||||
|
APIEndpoints,
|
||||||
|
ApplicationCommandOptionTypes,
|
||||||
|
CDNEndpoints,
|
||||||
|
ClanPlaystyle,
|
||||||
|
ExplicitContentFilterStrings,
|
||||||
|
Permissions,
|
||||||
|
VerificationLevelStrings,
|
||||||
|
} = require("../../util/dconstants.js");
|
||||||
|
const {
|
||||||
|
RegExp: {Snowflake: SNOWFLAKE_REGEX},
|
||||||
|
Icons,
|
||||||
|
ChannelTypeNames,
|
||||||
|
} = require("../../util/constants.js");
|
||||||
|
const {snowflakeToTimestamp} = require("../../util/time.js");
|
||||||
|
const {getGuild, formatGuildFeatures} = require("../../util/misc.js");
|
||||||
|
|
||||||
|
const guildinfo = new Command("guildinfo");
|
||||||
|
guildinfo.category = "utility";
|
||||||
|
guildinfo.helpText = "Get information on a guild";
|
||||||
|
guildinfo.usage = "<guild id>";
|
||||||
|
guildinfo.addAlias("guild");
|
||||||
|
guildinfo.addAlias("ginfo");
|
||||||
|
guildinfo.addAlias("gi");
|
||||||
|
guildinfo.addAlias("serverinfo");
|
||||||
|
guildinfo.addAlias("server");
|
||||||
|
guildinfo.addAlias("sinfo");
|
||||||
|
guildinfo.addAlias("si");
|
||||||
|
guildinfo.callback = async function (msg, line) {
|
||||||
|
let _guild, clanEmbed, id;
|
||||||
|
if (!line || line == "") {
|
||||||
|
if (!msg.guildID) return "Not in a guild.";
|
||||||
|
const __guild = msg.channel.guild ?? hf.bot.guilds.get(msg.guildID);
|
||||||
|
if (__guild) {
|
||||||
|
_guild = {source: "local", data: __guild};
|
||||||
|
} else {
|
||||||
|
_guild = await getGuild(msg.guildID);
|
||||||
|
}
|
||||||
|
id = msg.guildID;
|
||||||
|
} else {
|
||||||
|
if (!SNOWFLAKE_REGEX.test(line)) return "Not a snowflake.";
|
||||||
|
const snowflake = line.match(SNOWFLAKE_REGEX)[1];
|
||||||
|
_guild = await getGuild(snowflake);
|
||||||
|
id = snowflake;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clan = await hf.bot.requestHandler.request("GET", APIEndpoints.CLAN(snowflake), true);
|
||||||
|
|
||||||
|
if (clan) {
|
||||||
|
const images = [];
|
||||||
|
|
||||||
|
clanEmbed = {
|
||||||
|
title: _guild != null ? "Clan data" : clan.name,
|
||||||
|
description: clan.description ?? "*No description*",
|
||||||
|
fields: [
|
||||||
|
!_guild && {
|
||||||
|
name: "Member Count",
|
||||||
|
value: clan.member_count,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Tag",
|
||||||
|
value: clan.tag,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Playstyle",
|
||||||
|
value: ClanPlaystyle[clan.playstyle] ?? `<unknown value: ${clan.playstyle}>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Descriptors",
|
||||||
|
value: clan.wildcard_descriptors.join(", "),
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Interests/Topics/Traits",
|
||||||
|
value: `\`${clan.search_terms.map((x) => `"${x}"`).join(", ")}\``,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Associated Game IDs",
|
||||||
|
value: `\`${clan.game_ids.map((x) => `"${x}"`).join(", ")}\``,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Badge Colors",
|
||||||
|
value: `${clan.badge_color_primary}, ${clan.badge_color_secondary}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Banner/Brand Colors",
|
||||||
|
value: `${clan.brand_color_primary}, ${clan.brand_color_secondary}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
].filter((x) => !!x),
|
||||||
|
footer: !_guild ? {text: "Fetched from clan"} : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (clan.badge_hash) {
|
||||||
|
const url = CDNEndpoints.CLAN_BADGE(clan.id, clan.badge_hash);
|
||||||
|
images.push(`[Badge](${url})`);
|
||||||
|
clanEmbed.thumbnail = {url};
|
||||||
|
}
|
||||||
|
if (clan.banner_hash) {
|
||||||
|
const url = CDNEndpoints.CLAN_BANNER(clan.id, clan.banner_hash);
|
||||||
|
images.push(`[Banner](${url})`);
|
||||||
|
clanEmbed.image = {url};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.length > 0) {
|
||||||
|
clanEmbed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: images.join("\u3000\u3000"),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_guild) {
|
||||||
|
if (clanEmbed) {
|
||||||
|
return {embeds: [clanEmbed]};
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await hf.bot.requestHandler.request("GET", APIEndpoints.GUILD_WIDGET(id), false);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code == 50004) {
|
||||||
|
return `Guild \`${id}\` is private.`;
|
||||||
|
} else {
|
||||||
|
return "Guild not found.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = _guild.data;
|
||||||
|
switch (_guild.source) {
|
||||||
|
case "local": {
|
||||||
|
const roles = Array.from(guild.roles.values());
|
||||||
|
|
||||||
|
const channelTypeCounts = {};
|
||||||
|
let nsfwChannels = 0;
|
||||||
|
let hiddenChannels = 0;
|
||||||
|
for (const channel of guild.channels.values()) {
|
||||||
|
if (!channelTypeCounts[channel.type]) channelTypeCounts[channel.type] = 0;
|
||||||
|
channelTypeCounts[channel.type]++;
|
||||||
|
|
||||||
|
if (channel.nsfw) nsfwChannels++;
|
||||||
|
|
||||||
|
let defaultPermissions = channel.permissionOverwrites.get(guild.id);
|
||||||
|
if (!defaultPermissions && channel.parentID) {
|
||||||
|
defaultPermissions = guild.channels.get(channel.parentID).permissionOverwrites.get(guild.id);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
defaultPermissions &&
|
||||||
|
(channel instanceof VoiceChannel
|
||||||
|
? (defaultPermissions.deny & Permissions.voiceConnect) === Permissions.voiceConnect
|
||||||
|
: (defaultPermissions.deny & Permissions.viewChannel) === Permissions.viewChannel)
|
||||||
|
) {
|
||||||
|
hiddenChannels++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = {
|
||||||
|
title: guild.name,
|
||||||
|
description: guild.description ?? "*No description.*",
|
||||||
|
fields: [
|
||||||
|
guild.ownerID && {
|
||||||
|
name: "Owner",
|
||||||
|
value: `<@${guild.ownerID}>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.vanityURL && {
|
||||||
|
name: "Vanity URL",
|
||||||
|
value: `https://discord.gg/${guild.vanityURL}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Created",
|
||||||
|
value: `<t:${Math.floor(snowflakeToTimestamp(guild.id) / 1000)}:R>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Max Members",
|
||||||
|
value: guild.maxMembers,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Max Video Channel Users",
|
||||||
|
value: `Normal: ${guild.maxVideoChannelUsers}\nStage: ${guild.maxStageVideoChannelUsers}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Verification Level",
|
||||||
|
value: VerificationLevelStrings[guild.verificationLevel],
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Content Filter",
|
||||||
|
value: ExplicitContentFilterStrings[guild.explicitContentFilter],
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Moderation 2FA",
|
||||||
|
value: guild.mfaLevel == 0 ? "Off" : "On",
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Boost Status",
|
||||||
|
value: `**Level ${guild.premiumTier}**, ${guild.premiumSubscriptionCount} Boosts`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Locale",
|
||||||
|
value: guild.preferredLocale,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Default Notifications",
|
||||||
|
value: guild.defaultNotifications == 0 ? "All Messages" : "Only Mentions",
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.rulesChannelID && {
|
||||||
|
name: "Rules",
|
||||||
|
value: `<#${guild.rulesChannelID}>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.systemChannelID && {
|
||||||
|
name: "System Messages",
|
||||||
|
value: `<#${guild.systemChannelID}>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.publicUpdatesChannelID && {
|
||||||
|
name: "Community Updates",
|
||||||
|
value: `<#${guild.publicUpdatesChannelID}>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.safetyAlertsChannelID && {
|
||||||
|
name: "Safety Alerts",
|
||||||
|
value: `<#${guild.safetyAlertsChannelID}>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Channels (${guild.channels.size})`,
|
||||||
|
value:
|
||||||
|
Object.entries(channelTypeCounts)
|
||||||
|
.map(([type, count]) => `${count} ${ChannelTypeNames[type]}`)
|
||||||
|
.join(", ") + `\n${nsfwChannels} age restricted, ${hiddenChannels} hidden`,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Roles (${guild.roles.size})`,
|
||||||
|
value: `${roles.filter((role) => role.managed).length} managed, ${
|
||||||
|
roles.filter((role) => role.tags?.guild_connections).length
|
||||||
|
} linked, ${roles.filter((role) => role.tags?.integration_id != null).length} integration`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.emojis.length > 0 && {
|
||||||
|
name: `Emotes (${guild.emojis.length})`,
|
||||||
|
value: `${guild.emojis.filter((e) => e.animated).length} animated, ${
|
||||||
|
guild.emojis.filter((e) => e.managed).length
|
||||||
|
} managed\n${guild.emojis.filter((e) => !e.available).length} unavailable`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.stickers.length > 0 && {
|
||||||
|
name: `Stickers (${guild.stickers.length})`,
|
||||||
|
value: `${guild.stickers.filter((s) => s.format_type == 1).length} PNG, ${
|
||||||
|
guild.stickers.filter((s) => s.format_type == 2).length
|
||||||
|
} APNG, ${guild.stickers.filter((s) => s.format_type == 4).length} GIF, ${
|
||||||
|
guild.stickers.filter((s) => s.format_type == 3).length
|
||||||
|
} Lottie\n${guild.stickers.filter((s) => !s.available).length} unavailable`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
].filter((x) => !!x),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (guild.icon) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: CDNEndpoints.GUILD_ICON(guild.id, guild.icon),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const members = Array.from(guild.members.values());
|
||||||
|
const online = members.filter((member) => member.status != null && member.status != "offline").length;
|
||||||
|
const bots = members.filter((member) => member.bot).length;
|
||||||
|
const verfifiedBots = members.filter((member) => member.bot && (member.user.publicFlags & 65536) != 0).length;
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Member Count",
|
||||||
|
value: `${Icons.online}${online} online${Icons.blank}${Icons.blank}${Icons.offline}${guild.memberCount} members\n${Icons.silk.cog} ${bots}${Icons.blank}${Icons.silk.tick} ${verfifiedBots}`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const features = formatGuildFeatures(guild.features);
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Features (${features.length})`,
|
||||||
|
value: features.length > 0 ? features.slice(0, Math.ceil(features.length / 2)).join("\n") : "None",
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
if (features.length > 1)
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: features.slice(Math.ceil(features.length / 2), features.length).join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const images = [];
|
||||||
|
if (guild.icon) {
|
||||||
|
images.push(`[Icon](${embed.thumbnail.url})`);
|
||||||
|
}
|
||||||
|
if (guild.banner) {
|
||||||
|
images.push(`[Banner](${CDNEndpoints.BANNER(guild.id, guild.banner)})`);
|
||||||
|
}
|
||||||
|
if (guild.splash) {
|
||||||
|
images.push(`[Invite Splash](${CDNEndpoints.GUILD_SPLASH(guild.id, guild.splash)})`);
|
||||||
|
}
|
||||||
|
if (guild.discoverySplash) {
|
||||||
|
images.push(`[Discovery Splash](${CDNEndpoints.DISCOVERY_SPLASH(guild.id, guild.discoverySplash)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.length > 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: images.join("\u3000\u3000"),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clanEmbed) {
|
||||||
|
return {embeds: [embed, clanEmbed]};
|
||||||
|
} else {
|
||||||
|
return {embed};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "preview":
|
||||||
|
case "verification": {
|
||||||
|
const embed = {
|
||||||
|
title: guild.name,
|
||||||
|
description: guild.description ?? "*No description.*",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Created",
|
||||||
|
value: `<t:${Math.floor(snowflakeToTimestamp(guild.id) / 1000)}:R>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
guild.verification_level && {
|
||||||
|
name: "Verification Level",
|
||||||
|
value: VerificationLevelStrings[guild.verification_level],
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
(guild.emojis?.length ?? 0) > 0 && {
|
||||||
|
name: `Emotes (${guild.emojis.length})`,
|
||||||
|
value: `${guild.emojis.filter((e) => e.animated).length} animated, ${
|
||||||
|
guild.emojis.filter((e) => e.managed).length
|
||||||
|
} managed\n${guild.emojis.filter((e) => !e.available).length} unavailable`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
(guild.stickers?.length ?? 0) > 0 && {
|
||||||
|
name: `Stickers (${guild.stickers.length})`,
|
||||||
|
value: `${guild.stickers.filter((s) => s.format_type == 1).length} PNG, ${
|
||||||
|
guild.stickers.filter((s) => s.format_type == 2).length
|
||||||
|
} APNG, ${guild.stickers.filter((s) => s.format_type == 4).length} GIF, ${
|
||||||
|
guild.stickers.filter((s) => s.format_type == 3).length
|
||||||
|
} Lottie\n${guild.stickers.filter((s) => !s.available).length} unavailable`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
].filter((x) => !!x),
|
||||||
|
footer: {
|
||||||
|
text: `Fetched from ${_guild.source === "verification" ? "membership screening" : "guild preview"}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (guild.icon) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: CDNEndpoints.GUILD_ICON(guild.id, guild.icon),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Member Count",
|
||||||
|
value: `${Icons.online}${guild.approximate_presence_count} online${Icons.blank}${Icons.blank}${Icons.offline}${guild.approximate_member_count} members`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const features = formatGuildFeatures(guild.features);
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Features (${features.length})`,
|
||||||
|
value: features.length > 0 ? features.slice(0, Math.ceil(features.length / 2)).join("\n") : "None",
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
if (features.length > 1)
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: features.slice(Math.ceil(features.length / 2), features.length).join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const images = [];
|
||||||
|
if (guild.icon) {
|
||||||
|
images.push(`[Icon](${embed.thumbnail.url})`);
|
||||||
|
}
|
||||||
|
if (guild.splash) {
|
||||||
|
images.push(`[Invite Splash](${CDNEndpoints.GUILD_SPLASH(guild.id, guild.splash)})`);
|
||||||
|
}
|
||||||
|
if (guild.discovery_splash) {
|
||||||
|
images.push(`[Discovery Splash](${CDNEndpoints.DISCOVERY_SPLASH(guild.id, guild.discoverySplash)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.length > 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: images.join("\u3000\u3000"),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clanEmbed) {
|
||||||
|
return {embeds: [embed, clanEmbed]};
|
||||||
|
} else {
|
||||||
|
return {embed};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "discovery": {
|
||||||
|
if (!guild.store_page) {
|
||||||
|
return "Got discovery data but no store page.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildObj = guild.store_page.guild;
|
||||||
|
|
||||||
|
let invite;
|
||||||
|
if (guildObj.invite?.code || guild.store_page.role_subscription.purchase_page_invite?.code) {
|
||||||
|
const code = guildObj.invite?.code ?? guild.store_page.role_subscription.purchase_page_invite?.code;
|
||||||
|
invite = await hf.bot.requestHandler.request(
|
||||||
|
"GET",
|
||||||
|
`${APIEndpoints.INVITE(code)}?with_counts=true&with_expiration=true`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = {
|
||||||
|
title: guildObj.name,
|
||||||
|
description: invite?.guild?.description ?? "*No description.*",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Created",
|
||||||
|
value: `<t:${Math.floor(snowflakeToTimestamp(guildObj.id) / 1000)}:R>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
footer: {
|
||||||
|
text: "Fetched from discovery" + (invite ? " + invite" : ""),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (guildObj.icon_hash) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: CDNEndpoints.GUILD_ICON(guildObj.id, guildObj.icon_hash),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const invites = [];
|
||||||
|
if (guildObj.invite?.code) invites.push(guildObj.invite.code);
|
||||||
|
if (guild.store_page.role_subscription.purchase_page_invite?.code)
|
||||||
|
invites.push(guild.store_page.role_subscription.purchase_page_invite.code);
|
||||||
|
|
||||||
|
if (invites.length > 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Invites",
|
||||||
|
value: invites.map((code) => "https://discord.gg/" + code).join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Member Count",
|
||||||
|
value: `${Icons.online}${guildObj.approximate_presence_count} online\t\t<:offline:1152111682886316042>${guildObj.approximate_member_count} members`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invite?.guild?.features) {
|
||||||
|
const features = formatGuildFeatures(invite.guild.features);
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Features (${features.length})`,
|
||||||
|
value: features.length > 0 ? features.slice(0, Math.ceil(features.length / 2)).join("\n") : "None",
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
if (features.length > 1)
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: features.slice(Math.ceil(features.length / 2), features.length).join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const images = [];
|
||||||
|
if (guildObj.icon_hash) {
|
||||||
|
images.push(`[Icon](${embed.thumbnail.url})`);
|
||||||
|
}
|
||||||
|
if (invite?.guild?.splash) {
|
||||||
|
images.push(`[Invite Splash](${CDNEndpoints.GUILD_SPLASH(guild.id, guild.splash)})`);
|
||||||
|
}
|
||||||
|
if (invite?.guild?.banner) {
|
||||||
|
images.push(`[Banner](${CDNEndpoints.BANNER(guild.id, guild.banner)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.length > 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: images.join("\u3000\u3000"),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clanEmbed) {
|
||||||
|
return {embeds: [embed, clanEmbed]};
|
||||||
|
} else {
|
||||||
|
return {embed};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "widget": {
|
||||||
|
let invite;
|
||||||
|
if (guild.instant_invite) {
|
||||||
|
invite = await hf.bot.requestHandler.request(
|
||||||
|
"GET",
|
||||||
|
`/invites/${guild.instant_invite.replace(
|
||||||
|
/(https?:\/\/)?discord(\.gg|(app)?.com\/invite)\//,
|
||||||
|
""
|
||||||
|
)}?with_counts=true&with_expiration=true`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = {
|
||||||
|
title: guild.name,
|
||||||
|
description: invite?.guild?.description ?? "*No description.*",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Created",
|
||||||
|
value: `<t:${Math.floor(snowflakeToTimestamp(guild.id) / 1000)}:R>`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
footer: {
|
||||||
|
text: "Fetched from widget" + (invite ? " + invite" : ""),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (invite) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Invite",
|
||||||
|
value: guild.instant_invite,
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Member Count",
|
||||||
|
value: `<:online:1152111668856361010>${invite.approximate_presence_count} online\t\t<:offline:1152111682886316042>${invite.approximate_member_count} members`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const features = formatGuildFeatures(invite.guild.features);
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: `Features (${features.length})`,
|
||||||
|
value: features.length > 0 ? features.slice(0, Math.ceil(features.length / 2)).join("\n") : "None",
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
if (features.length > 1)
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: features.slice(Math.ceil(features.length / 2), features.length).join("\n"),
|
||||||
|
inline: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const images = [];
|
||||||
|
if (invite.guild.icon) {
|
||||||
|
images.push(`[Icon](${CDNEndpoints.GUILD_ICON(invite.guild.id, invite.guild.icon)})`);
|
||||||
|
}
|
||||||
|
if (invite.guild.splash) {
|
||||||
|
images.push(`[Invite Splash](${CDNEndpoints.GUILD_SPLASH(invite.guild.id, invite.guild.splash)})`);
|
||||||
|
}
|
||||||
|
if (invite.guild.banner) {
|
||||||
|
images.push(`[Banner](${CDNEndpoints.BANNER(invite.guild.id, invite.guild.banner)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.length > 0) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: images.join("\u3000\u3000"),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Member Count",
|
||||||
|
value: `${Icons.online}${guild.presence_count} online`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clanEmbed) {
|
||||||
|
return {embeds: [embed, clanEmbed]};
|
||||||
|
} else {
|
||||||
|
return {embed};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "Guild not found.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(guildinfo);
|
||||||
|
|
||||||
|
const guildinfoInteraction = new InteractionCommand("guildinfo");
|
||||||
|
guildinfoInteraction.helpText = "Get information on an guild";
|
||||||
|
guildinfoInteraction.options.id = {
|
||||||
|
name: "id",
|
||||||
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
description: "Guild ID to get info for",
|
||||||
|
required: false,
|
||||||
|
default: "",
|
||||||
|
};
|
||||||
|
guildinfoInteraction.callback = async function (interaction) {
|
||||||
|
const id = this.getOption(interaction, "id");
|
||||||
|
|
||||||
|
return guildinfo.callback(interaction, id);
|
||||||
|
};
|
||||||
|
hf.registerCommand(guildinfoInteraction);
|
145
src/modules/utility/jumbo.js
Normal file
145
src/modules/utility/jumbo.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
const InteractionCommand = require("../../lib/interactionCommand.js");
|
||||||
|
|
||||||
|
const sharp = require("sharp");
|
||||||
|
|
||||||
|
const {
|
||||||
|
RegExp: {Emote: CUSTOM_EMOTE_REGEX},
|
||||||
|
EmojiSets,
|
||||||
|
EmojiNames,
|
||||||
|
} = require("../../util/constants.js");
|
||||||
|
const {ApplicationCommandOptionTypes, CDNEndpoints} = require("../../util/dconstants.js");
|
||||||
|
const {getNamesFromString} = require("../../util/unicode.js");
|
||||||
|
|
||||||
|
const jumbo = new Command("jumbo");
|
||||||
|
jumbo.category = "utility";
|
||||||
|
jumbo.helpText = "Gets the raw image of an emoji.";
|
||||||
|
jumbo.usage = "<emoji>";
|
||||||
|
jumbo.addAlias("e");
|
||||||
|
jumbo.addAlias("emote");
|
||||||
|
jumbo.addAlias("emoji");
|
||||||
|
jumbo.callback = async function (msg, line) {
|
||||||
|
if (!line || line === "") return "Arguments required.";
|
||||||
|
|
||||||
|
if (CUSTOM_EMOTE_REGEX.test(line)) {
|
||||||
|
const [, animatedFlag, name, id] = line.match(CUSTOM_EMOTE_REGEX);
|
||||||
|
const animated = animatedFlag === "a";
|
||||||
|
|
||||||
|
const url = CDNEndpoints.EMOJI(id, animated);
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `:${name}: - \`${id}\``,
|
||||||
|
url,
|
||||||
|
image: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let setName = "twemoji";
|
||||||
|
for (const set in EmojiSets) {
|
||||||
|
if (line.startsWith(`--${set} `)) {
|
||||||
|
setName = set;
|
||||||
|
line = line.replace(`--${set} `, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const set = EmojiSets[setName];
|
||||||
|
|
||||||
|
const emoji = Array.from(line)
|
||||||
|
.map((char) => char.codePointAt().toString(16))
|
||||||
|
.join(set.sep);
|
||||||
|
const url = set.prefix + emoji + set.suffix;
|
||||||
|
|
||||||
|
const name = EmojiNames[line]
|
||||||
|
? `\\:${EmojiNames[line]}\\:`
|
||||||
|
: await getNamesFromString(line).then((name) => name.map((x) => x[1]).join(", "));
|
||||||
|
|
||||||
|
const emojiFound = await fetch(url, {method: "HEAD"}).then((res) => res.ok);
|
||||||
|
|
||||||
|
if (!emojiFound) {
|
||||||
|
return "Emoji not found. The emoji set chosen might not have this emote as an image.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set.suffix == ".svg") {
|
||||||
|
const svg = await fetch(url)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((b) => Buffer.from(b));
|
||||||
|
const converted = await sharp(svg, {density: 2400}).resize(1024).toBuffer();
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `${name} (${emoji.toUpperCase().replace(/[-_]/g, ", ")})`,
|
||||||
|
url,
|
||||||
|
image: {
|
||||||
|
url: "attachment://emoji.png",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
file: {
|
||||||
|
file: converted,
|
||||||
|
name: "emoji.png",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `${name} (${emoji.toUpperCase().replace(/[-_]/g, ", ")})`,
|
||||||
|
url,
|
||||||
|
image: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(jumbo);
|
||||||
|
|
||||||
|
const jumboInteraction = new InteractionCommand("jumbo");
|
||||||
|
jumboInteraction.helpText = "Gets the raw image of an emoji.";
|
||||||
|
jumboInteraction.options.content = {
|
||||||
|
name: "content",
|
||||||
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
description: "Emoji to get image of",
|
||||||
|
required: true,
|
||||||
|
default: "",
|
||||||
|
};
|
||||||
|
jumboInteraction.options.set = {
|
||||||
|
name: "set",
|
||||||
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
description: "Emoji set for non-custom emoji",
|
||||||
|
required: false,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: "Twemoji (Twitter/Discord)",
|
||||||
|
value: "twemoji",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Noto (Google)",
|
||||||
|
value: "noto",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Noto Old (Blobs)",
|
||||||
|
value: "blobs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mutant Standard",
|
||||||
|
value: "mustd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: "twemoji",
|
||||||
|
};
|
||||||
|
jumboInteraction.callback = async function (interaction) {
|
||||||
|
const content = this.getOption(interaction, "content");
|
||||||
|
const set = this.getOption(interaction, "set");
|
||||||
|
|
||||||
|
return jumbo.callback(interaction, `--${set} ${content}`);
|
||||||
|
};
|
||||||
|
hf.registerCommand(jumboInteraction);
|
245
src/modules/utility/lookupinvite.js
Normal file
245
src/modules/utility/lookupinvite.js
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
const InteractionCommand = require("../../lib/interactionCommand.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
APIEndpoints,
|
||||||
|
ApplicationCommandOptionTypes,
|
||||||
|
CDNEndpoints,
|
||||||
|
DEFAULT_GROUP_DM_AVATARS,
|
||||||
|
} = require("../../util/dconstants.js");
|
||||||
|
const {Icons} = require("../../util/constants.js");
|
||||||
|
|
||||||
|
const {formatUsername} = require("../../util/misc.js");
|
||||||
|
const {safeString, formatGuildFeatures} = require("../../util/selection.js");
|
||||||
|
const {snowflakeToTimestamp} = require("../../util/time.js");
|
||||||
|
|
||||||
|
const lookupinvite = new Command("lookupinvite");
|
||||||
|
lookupinvite.category = "utility";
|
||||||
|
lookupinvite.helpText = "Lookup an invite";
|
||||||
|
lookupinvite.usage = "<invite code>";
|
||||||
|
lookupinvite.addAlias("linvite");
|
||||||
|
lookupinvite.addAlias("inviteinfo");
|
||||||
|
lookupinvite.addAlias("iinfo");
|
||||||
|
lookupinvite.addAlias("ii");
|
||||||
|
lookupinvite.callback = async function (msg, line) {
|
||||||
|
if (!line || line == "") return "Arguments required.";
|
||||||
|
|
||||||
|
line = line.replace(/(https?:\/\/)?discord(\.gg|(app)?.com\/invite)\//, "");
|
||||||
|
|
||||||
|
if (decodeURI(line).indexOf("../") > -1) return "nuh uh";
|
||||||
|
|
||||||
|
let bail = false;
|
||||||
|
let error;
|
||||||
|
let invite;
|
||||||
|
try {
|
||||||
|
invite = await hf.bot.requestHandler.request(
|
||||||
|
"GET",
|
||||||
|
`${APIEndpoints.INVITE(line)}?with_counts=true&with_expiration=true`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
bail = true;
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
if (bail && error) {
|
||||||
|
if (error.message.includes("Unknown Invite")) {
|
||||||
|
return "Invite provided is not valid.";
|
||||||
|
} else {
|
||||||
|
return `:warning: Got error \`${safeString(error)}\``;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!invite) return ":warning: No data returned.";
|
||||||
|
|
||||||
|
if (invite.message) {
|
||||||
|
if (invite.message == "Unknown Invite") {
|
||||||
|
return "Invite provided is not valid.";
|
||||||
|
} else {
|
||||||
|
return `:warning: Got error \`${invite.code}: "${invite.message}"\``;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const embed = {
|
||||||
|
title: `Invite Info: \`${invite.code}\``,
|
||||||
|
description: invite.description,
|
||||||
|
fields: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const expires = {
|
||||||
|
name: "Expires",
|
||||||
|
value: invite.expires_at == null ? "Never" : `<t:${Math.floor(new Date(invite.expires_at).getTime() / 1000)}>`,
|
||||||
|
inline: true,
|
||||||
|
};
|
||||||
|
const inviter = invite.inviter
|
||||||
|
? {
|
||||||
|
name: "Inviter",
|
||||||
|
value: `**${formatUsername(invite.inviter)}** (${invite.inviter.id})`,
|
||||||
|
inline: false,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const features = formatGuildFeatures(invite.guild.features);
|
||||||
|
|
||||||
|
switch (invite.type) {
|
||||||
|
case 0: {
|
||||||
|
embed.fields.push(
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
name: "Guild",
|
||||||
|
value: `**${invite.guild.name}** (${invite.guild.id})`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Channel",
|
||||||
|
value: `**${invite.channel.name}** (${invite.channel.id})`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Boosts",
|
||||||
|
value: invite.guild.premium_subscription_count ?? 0,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
expires,
|
||||||
|
{
|
||||||
|
name: "Member Count",
|
||||||
|
value: `${Icons.online}${invite.approximate_presence_count} online${Icons.blank}${Icons.blank}${Icons.offline}${invite.approximate_member_count} members`,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
inviter,
|
||||||
|
invite.guild.welcome_screen && {
|
||||||
|
name: "Welcome Screen",
|
||||||
|
value: `"${invite.guild.welcome_screen.description}"\n${invite.guild.welcome_screen.welcome_channels
|
||||||
|
.map(
|
||||||
|
(c) =>
|
||||||
|
`${
|
||||||
|
c.emoji_id ? `[:${c.emoji_name}:](${CDNEndpoints.EMOJI(c.emoji_id, false, true)})` : c.emoji_name
|
||||||
|
} ${c.description} \`(${c.channel_id})\``
|
||||||
|
)
|
||||||
|
.join("\n")}`,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `Features (${features.length})`,
|
||||||
|
value: features.length > 0 ? features.slice(0, Math.ceil(features.length / 2)).join("\n") : "None",
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
features.length > 1
|
||||||
|
? {
|
||||||
|
name: "\u200b",
|
||||||
|
value: features.slice(Math.ceil(features.length / 2), features.length).join("\n"),
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
].filter((x) => !!x)
|
||||||
|
);
|
||||||
|
|
||||||
|
const guildIcon = invite.guild?.icon && CDNEndpoints.GUILD_ICON(invite.guild.id, invite.guild.icon);
|
||||||
|
const guildSplash = invite.guild?.splash && CDNEndpoints.GUILD_SPLASH(invite.guild.id, invite.guild.splash);
|
||||||
|
|
||||||
|
if (guildIcon)
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: guildIcon,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (invite.guild && (invite.guild.icon || invite.guild.splash || invite.guild.banner)) {
|
||||||
|
const imageLinks = [];
|
||||||
|
if (guildIcon) imageLinks.push(`[Icon](${guildIcon})`);
|
||||||
|
if (invite.guild.splash) imageLinks.push(`[Splash](${guildSplash})`);
|
||||||
|
if (invite.guild.banner)
|
||||||
|
imageLinks.push(`[Splash](${CDNEndpoints.BANNER(invite.guild.id, invite.guild.banner)})`);
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: imageLinks.join("\u3000\u3000"),
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guildSplash)
|
||||||
|
embed.image = {
|
||||||
|
url: guildSplash,
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
embed.title += " (Group DM)";
|
||||||
|
embed.fields.push(
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
name: "Channel",
|
||||||
|
value: `**${
|
||||||
|
invite.channel.name ?? invite.channel.recipients.map((member) => member.username).join(", ")
|
||||||
|
}** (${invite.channel.id})`,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Member Count",
|
||||||
|
value: `<:offline:1152111682886316042>${invite.approximate_member_count} members`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
expires,
|
||||||
|
invite.channel.name != null && {
|
||||||
|
name: "Recipients",
|
||||||
|
value: invite.channel.recipients.map((member) => member.username).join(", "),
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
inviter,
|
||||||
|
].filter((x) => !!x)
|
||||||
|
);
|
||||||
|
|
||||||
|
const groupIcon = invite.channel.icon
|
||||||
|
? CDNEndpoints.GDM_ICON(invite.channel.id, invite.channel.icon)
|
||||||
|
: DEFAULT_GROUP_DM_AVATARS[snowflakeToTimestamp(invite.channel.id) % DEFAULT_GROUP_DM_AVATARS.length];
|
||||||
|
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: groupIcon,
|
||||||
|
};
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: `[Icon](${groupIcon})`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
embed.title += " (Friend)";
|
||||||
|
embed.fields.push(expires, inviter);
|
||||||
|
|
||||||
|
const avatarURL = invite.inviter?.avatar && CDNEndpoints.USER_AVATAR(invite.inviter.id, invite.inviter.avatar);
|
||||||
|
|
||||||
|
if (avatarURL) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: avatarURL,
|
||||||
|
};
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: `[Avatar](${avatarURL})`,
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return `Unhandled invite type: \`${invite.type}\``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {embed};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(lookupinvite);
|
||||||
|
|
||||||
|
const inviteinfoInteraction = new InteractionCommand("inviteinfo");
|
||||||
|
inviteinfoInteraction.helpText = "Get information on an invite code";
|
||||||
|
inviteinfoInteraction.options.invite = {
|
||||||
|
name: "invite",
|
||||||
|
type: ApplicationCommandOptionTypes.STRING,
|
||||||
|
description: "Invite code to get info for",
|
||||||
|
required: true,
|
||||||
|
default: "",
|
||||||
|
};
|
||||||
|
inviteinfoInteraction.callback = async function (interaction) {
|
||||||
|
const invite = this.getOption(interaction, "invite");
|
||||||
|
|
||||||
|
return lookupinvite.callback(interaction, invite);
|
||||||
|
};
|
||||||
|
hf.registerCommand(inviteinfoInteraction);
|
46
src/modules/utility/pomelo.js
Normal file
46
src/modules/utility/pomelo.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const {APIEndpoints} = require("../../util/dconstants.js");
|
||||||
|
const {
|
||||||
|
RegExp: {Pomelo: POMELO_REGEX},
|
||||||
|
} = require("../../util/constants.js");
|
||||||
|
|
||||||
|
const pomelo = new Command("pomelo");
|
||||||
|
pomelo.category = "utility";
|
||||||
|
pomelo.helpText = "Check to see if a username is taken or not";
|
||||||
|
pomelo.usage = "[username] <...username>";
|
||||||
|
pomelo.callback = async function (msg, line) {
|
||||||
|
if (!line || line === "") return "Arguments required.";
|
||||||
|
|
||||||
|
const usernames = line.toLowerCase().split(" ");
|
||||||
|
|
||||||
|
if (usernames.length == 1) {
|
||||||
|
const name = usernames[0];
|
||||||
|
if (name.length > 32 || !POMELO_REGEX.test(name)) return {reaction: "\u{1f6ab}"};
|
||||||
|
|
||||||
|
const res = await hf.bot.requestHandler.request("POST", APIEndpoints.POMELO_UNAUTHED, false, {
|
||||||
|
username: name,
|
||||||
|
});
|
||||||
|
return {reaction: res.taken ? "\u274c" : "\u2705"};
|
||||||
|
} else {
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
for (const name of usernames) {
|
||||||
|
if (name.length > 32 || !POMELO_REGEX.test(name)) {
|
||||||
|
lines.push(`\u{1f6ab} \`${name}\``);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const res = await hf.bot.requestHandler.request("POST", APIEndpoints.POMELO_UNAUTHED, false, {
|
||||||
|
username: name,
|
||||||
|
});
|
||||||
|
lines.push(`${res.taken ? "\u274c" : "\u2705"} \`${name}\``);
|
||||||
|
} catch {
|
||||||
|
lines.push(`\u26a0\ufe0f \`${name}\``);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join("\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(pomelo);
|
340
src/modules/utility/presence.js
Normal file
340
src/modules/utility/presence.js
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const sharp = require("sharp");
|
||||||
|
|
||||||
|
const {
|
||||||
|
ActivityTypeNames,
|
||||||
|
CDNEndpoints,
|
||||||
|
Games,
|
||||||
|
HangStatusStrings,
|
||||||
|
HANG_STATUS_ICONS,
|
||||||
|
} = require("../../util/dconstants.js");
|
||||||
|
const {Icons} = require("../../util/constants.js");
|
||||||
|
const {formatUsername} = require("../../util/misc.js");
|
||||||
|
const {lookupUser} = require("../../util/selection.js");
|
||||||
|
const {formatTime} = require("../../util/time.js");
|
||||||
|
|
||||||
|
const HangStatusImages = {};
|
||||||
|
(async () => {
|
||||||
|
for (const key of Object.keys(HANG_STATUS_ICONS)) {
|
||||||
|
const svg = await fetch(HANG_STATUS_ICONS[key])
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((b) => Buffer.from(b));
|
||||||
|
HangStatusImages[key] = await sharp(svg, {density: 2400}).resize(128).toBuffer();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const NOWPLAYING_BAR_LENGTH = 30;
|
||||||
|
|
||||||
|
function fixMediaProxyURL(url) {
|
||||||
|
if (url.includes("media.discordapp.net")) {
|
||||||
|
if (url.includes("/external/")) {
|
||||||
|
const split = url.replace("https://media.discordapp.net/external/", "").split("/");
|
||||||
|
split.shift();
|
||||||
|
|
||||||
|
let query = "";
|
||||||
|
if (split[0].startsWith("%")) {
|
||||||
|
query = decodeURIComponent(split.shift());
|
||||||
|
}
|
||||||
|
split[0] = split[0] + ":/";
|
||||||
|
|
||||||
|
url = split.join("/") + query;
|
||||||
|
} else {
|
||||||
|
url = url.replace("media.discordapp.net", "cdn.discordapp.com");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const presence = new Command("presence");
|
||||||
|
presence.category = "utility";
|
||||||
|
presence.helpText = "Get presences of a user.";
|
||||||
|
presence.usage = "<user>";
|
||||||
|
presence.addAlias("status");
|
||||||
|
presence.callback = async function (msg, line) {
|
||||||
|
if (!msg.guildID) return "Can only be used in guilds.";
|
||||||
|
|
||||||
|
let target;
|
||||||
|
if (line) {
|
||||||
|
const user = await lookupUser(msg, line);
|
||||||
|
if (user == "No results" || user == "Canceled" || user == "Request timed out") {
|
||||||
|
return user;
|
||||||
|
} else {
|
||||||
|
let member = user;
|
||||||
|
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
||||||
|
if (guild) {
|
||||||
|
if (guild.members.has(user.id)) {
|
||||||
|
member = guild.members.get(user.id);
|
||||||
|
} else {
|
||||||
|
const fetched = await guild.fetchMembers({
|
||||||
|
userIDs: [user.id],
|
||||||
|
});
|
||||||
|
member = fetched[0];
|
||||||
|
}
|
||||||
|
target = member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target = msg.member;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
if (!target.clientStatus) return `**${formatUsername(target)}** is offline.`;
|
||||||
|
|
||||||
|
const icons = [];
|
||||||
|
for (const platform of Object.keys(target.clientStatus)) {
|
||||||
|
const status = target.clientStatus[platform];
|
||||||
|
if (status == "offline") continue;
|
||||||
|
|
||||||
|
icons.push(Icons.presence[platform][status]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const embeds = [];
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
for (const index in target.activities) {
|
||||||
|
const activity = target.activities[index];
|
||||||
|
if (activity.type == 4) {
|
||||||
|
const embed = {};
|
||||||
|
|
||||||
|
if (activity.emoji) {
|
||||||
|
if (activity.emoji.id) {
|
||||||
|
const url = CDNEndpoints.EMOJI(activity.emoji.id, activity.emoji.animated);
|
||||||
|
embed.author = {
|
||||||
|
url,
|
||||||
|
icon_url: url,
|
||||||
|
name: activity.state ?? "\u200b",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
embed.title = `${activity.emoji.name} ${activity.state ?? ""}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
embed.title = activity.state ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
embeds.push(embed);
|
||||||
|
} else if (activity.type == 6) {
|
||||||
|
const embed = {};
|
||||||
|
|
||||||
|
embed.title = "Hang Status";
|
||||||
|
embed.description = `Right now I'm \u2013\n**${
|
||||||
|
activity.state == "custom" ? activity.details : HangStatusStrings[activity.state]
|
||||||
|
}**`;
|
||||||
|
|
||||||
|
if (activity.emoji) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: CDNEndpoints.EMOJI(activity.emoji.id, activity.emoji.animated),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
files.push({
|
||||||
|
contents: HANG_STATUS_ICONS[activity.state],
|
||||||
|
name: `${activity.state}.png`,
|
||||||
|
});
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: `attachment://${activity.state}.png`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
embeds.push(embed);
|
||||||
|
} else {
|
||||||
|
const embed = {
|
||||||
|
title: `${ActivityTypeNames[activity.type]} **${activity.name}**`,
|
||||||
|
fields: [],
|
||||||
|
};
|
||||||
|
const descLines = [];
|
||||||
|
if (activity.type == 2) {
|
||||||
|
if (activity.details) {
|
||||||
|
let details = activity.details;
|
||||||
|
if (activity.name == "Spotify" && activity.sync_id) {
|
||||||
|
details = `[${details}](https://open.spotify.com/track/${activity.sync_id})`;
|
||||||
|
}
|
||||||
|
descLines.push(`**${details}**`);
|
||||||
|
}
|
||||||
|
if (activity.state) {
|
||||||
|
let stateLine = activity.state;
|
||||||
|
if (activity.name == "Spotify") stateLine = "by " + stateLine.split("; ").join(", ");
|
||||||
|
descLines.push(stateLine);
|
||||||
|
}
|
||||||
|
if (activity.assets?.large_text) {
|
||||||
|
let albumLine = activity.assets.large_text;
|
||||||
|
if (activity.name == "Spotify") albumLine = "on " + albumLine;
|
||||||
|
|
||||||
|
if (activity.party?.size) {
|
||||||
|
albumLine += ` (${activity.party.size[0]} of ${activity.party.size[1]})`;
|
||||||
|
}
|
||||||
|
descLines.push(albumLine);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (activity.details) descLines.push(activity.details);
|
||||||
|
if (activity.state) {
|
||||||
|
let stateLine = activity.state;
|
||||||
|
if (activity.party?.size) {
|
||||||
|
stateLine += ` (${activity.party.size[0]} of ${activity.party.size[1]})`;
|
||||||
|
}
|
||||||
|
descLines.push(stateLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.timestamps) {
|
||||||
|
if (activity.timestamps.start && !activity.timestamps.end) {
|
||||||
|
descLines.push(formatTime(Date.now() - activity.timestamps.start) + " elapsed");
|
||||||
|
} else if (!activity.timestamps.start && activity.timestamps.end) {
|
||||||
|
descLines.push(formatTime(activity.timestamps.end - Date.now()) + " remaining");
|
||||||
|
} else if (activity.timestamps.start != null && activity.timestamps.end != null) {
|
||||||
|
const position = Date.now() - activity.timestamps.start;
|
||||||
|
const length = activity.timestamps.end - activity.timestamps.start;
|
||||||
|
|
||||||
|
const timeEnd = formatTime(length);
|
||||||
|
const timePos = formatTime(position);
|
||||||
|
|
||||||
|
const progress = position >= length ? 1 : position / length;
|
||||||
|
const barLength = Math.round(progress * NOWPLAYING_BAR_LENGTH);
|
||||||
|
|
||||||
|
const bar = `\`[${"=".repeat(barLength)}${" ".repeat(NOWPLAYING_BAR_LENGTH - barLength)}]\``;
|
||||||
|
const time = `\`${timePos}${" ".repeat(
|
||||||
|
NOWPLAYING_BAR_LENGTH + 2 - timePos.length - timeEnd.length
|
||||||
|
)}${timeEnd}\``;
|
||||||
|
|
||||||
|
descLines.push(bar);
|
||||||
|
descLines.push(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.assets?.large_text && activity.type != 2) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Large Text",
|
||||||
|
value: activity.assets.large_text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.assets?.small_text) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Small Text",
|
||||||
|
value: activity.assets.small_text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.application_id) {
|
||||||
|
embed.fields.push({
|
||||||
|
name: "Application ID",
|
||||||
|
value: `\`${activity.application_id}\``,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
embed.description = descLines.join("\n");
|
||||||
|
|
||||||
|
if (activity.assets) {
|
||||||
|
if (activity.assets.large_image != null) {
|
||||||
|
const image_links = [];
|
||||||
|
|
||||||
|
let largeUrl;
|
||||||
|
if (activity.assets.large_image.startsWith("mp:")) {
|
||||||
|
largeUrl = activity.assets.large_image.replace("mp:", "https://media.discordapp.net/");
|
||||||
|
} else if (activity.assets.large_image.startsWith("spotify:")) {
|
||||||
|
largeUrl = activity.assets.large_image.replace("spotify:", "https://i.scdn.co/image/");
|
||||||
|
} else {
|
||||||
|
largeUrl = CDNEndpoints.APP_ASSET(activity.application_id, activity.assets.large_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
image_links.push(`[Large Image](${fixMediaProxyURL(largeUrl)})`);
|
||||||
|
|
||||||
|
let smallUrl;
|
||||||
|
if (activity.assets.small_image != null) {
|
||||||
|
if (activity.assets.small_image.startsWith("mp:")) {
|
||||||
|
smallUrl = activity.assets.small_image.replace("mp:", "https://media.discordapp.net/");
|
||||||
|
} else if (activity.assets.small_image.startsWith("spotify:")) {
|
||||||
|
smallUrl = activity.assets.small_image.replace("spotify:", "https://i.scdn.co/image/");
|
||||||
|
} else {
|
||||||
|
smallUrl = CDNEndpoints.APP_ASSET(activity.application_id, activity.assets.small_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
image_links.push(`[Small Image](${fixMediaProxyURL(smallUrl)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const largeImage = await fetch(largeUrl)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((b) => Buffer.from(b));
|
||||||
|
const presenceImage = sharp(largeImage).resize(60, 60);
|
||||||
|
if (smallUrl) {
|
||||||
|
const smallImage = await fetch(smallUrl)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((b) => Buffer.from(b));
|
||||||
|
const smallImageBuffer = await sharp(smallImage).resize(20, 20).toBuffer();
|
||||||
|
|
||||||
|
presenceImage.composite([
|
||||||
|
{
|
||||||
|
input: smallImageBuffer,
|
||||||
|
gravity: "southeast",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push({
|
||||||
|
contents: await presenceImage.toBuffer(),
|
||||||
|
name: `${index}.png`,
|
||||||
|
});
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: `attachment://${index}.png`,
|
||||||
|
};
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: image_links.join("\u3000\u3000"),
|
||||||
|
});
|
||||||
|
} else if (!activity.assets.large_image && activity.assets.small_image != null) {
|
||||||
|
let smallUrl;
|
||||||
|
if (activity.assets.small_image.startsWith("mp:")) {
|
||||||
|
smallUrl = activity.assets.small_image.replace("mp:", "https://media.discordapp.net/");
|
||||||
|
} else if (activity.assets.small_image.startsWith("spotify:")) {
|
||||||
|
smallUrl = activity.assets.small_image.replace("spotify:", "https://i.scdn.co/image/");
|
||||||
|
} else {
|
||||||
|
smallUrl = CDNEndpoints.APP_ASSET(activity.application_id, activity.assets.small_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
const smallImage = await fetch(smallUrl)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((b) => Buffer.from(b));
|
||||||
|
const presenceImage = await sharp(smallImage).resize(40, 40).toBuffer();
|
||||||
|
|
||||||
|
files.push({
|
||||||
|
contents: presenceImage,
|
||||||
|
name: `${index}.png`,
|
||||||
|
});
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: `attachment://${index}.png`,
|
||||||
|
};
|
||||||
|
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: `[Small Image](${fixMediaProxyURL(smallUrl)})`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.application_id && !activity.assets?.large_image && !activity.assets?.small_image) {
|
||||||
|
const game = Games.find((game) => game.id == activity.application_id);
|
||||||
|
if (game?.icon) {
|
||||||
|
embed.thumbnail = {
|
||||||
|
url: `${CDNEndpoints.APP_ICON(game.id, game.icon)}?size=40&keep_aspect_ratio=false`,
|
||||||
|
};
|
||||||
|
embed.fields.push({
|
||||||
|
name: "\u200b",
|
||||||
|
value: `[App Icon](${embed.thumbnail.url.replace("size=40&", "")})`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
embeds.push(embed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: `Presence for **${formatUsername(target)}**: ${icons.join(" ")}`,
|
||||||
|
embeds,
|
||||||
|
files,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return ":warning: Could not get user???";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(presence);
|
18
src/modules/utility/snowflake.js
Normal file
18
src/modules/utility/snowflake.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
const Command = require("../../lib/command.js");
|
||||||
|
|
||||||
|
const {snowflakeToTimestamp} = require("../../util/time.js");
|
||||||
|
|
||||||
|
const snowflake = new Command("snowflake");
|
||||||
|
snowflake.category = "utility";
|
||||||
|
snowflake.helpText = "Converts a snowflake ID into readable time.";
|
||||||
|
snowflake.usage = "<--twitter> [snowflake]";
|
||||||
|
snowflake.callback = function (msg, line, [snowflake], {twitter}) {
|
||||||
|
const num = Number(snowflake);
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
const timestamp = Math.floor(snowflakeToTimestamp(num, twitter) / 1000);
|
||||||
|
return `The timestamp for \`${snowflake}\` is <t:${timestamp}:F> (<t:${timestamp}:R>)`;
|
||||||
|
} else {
|
||||||
|
return "Argument provided is not a number.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hf.registerCommand(snowflake);
|
550
src/util/constants.js
Normal file
550
src/util/constants.js
Normal file
|
@ -0,0 +1,550 @@
|
||||||
|
module.exports = {};
|
||||||
|
|
||||||
|
const SILK_ICONS = Object.fromEntries(
|
||||||
|
Object.entries({
|
||||||
|
joystick: "1250544500246577172",
|
||||||
|
film: "1250544523730223277",
|
||||||
|
shield: "1250543253472673875",
|
||||||
|
vcard: "1250544496765308948",
|
||||||
|
photo: "1250543277267222639",
|
||||||
|
world_add: "1250544495435452547",
|
||||||
|
ruby: "1250544494739325001",
|
||||||
|
emoticon_smile: "1250543500471046288",
|
||||||
|
bug: "1250544574104080507",
|
||||||
|
comments_star: "1250544493841616988",
|
||||||
|
comments_delete: "1250544492709412946",
|
||||||
|
flag_blue: "1250544491471831131",
|
||||||
|
flag_red: "1250544490507141312",
|
||||||
|
controller: "1250544489156710541",
|
||||||
|
cog_delete: "1250544595666997288",
|
||||||
|
cog: "1250543252747321468",
|
||||||
|
basket: "1250544573088923722",
|
||||||
|
group: "1250543273726967900",
|
||||||
|
check_checked_green: "1250544572203929815",
|
||||||
|
world: "1250543499489706195",
|
||||||
|
world_delete: "1250544571180515408",
|
||||||
|
transmit_blue: "1250544569448136857",
|
||||||
|
comments: "1250543256052301924",
|
||||||
|
status_away: "1250544568772853801",
|
||||||
|
page_link: "1250544567665561631",
|
||||||
|
book_open_mark: "1250544632064901240",
|
||||||
|
house: "1250543270342426644",
|
||||||
|
lock: "1250543498470359121",
|
||||||
|
sitemap: "1250544630907535361",
|
||||||
|
eye: "1250544630043381872",
|
||||||
|
link: "1250543266806628412",
|
||||||
|
time: "1250544629053522030",
|
||||||
|
delete: "1250543248909406248",
|
||||||
|
money: "1250544627665211404",
|
||||||
|
money_delete: "1250544627178803313",
|
||||||
|
emoticon_smile_add: "1250544626054594560",
|
||||||
|
image_add: "1250544625068806215",
|
||||||
|
newspaper: "1250543497010872485",
|
||||||
|
bell: "1250544670178803722",
|
||||||
|
application_view_tile: "1250544666080837703",
|
||||||
|
star: "1250543254869381170",
|
||||||
|
compress: "1250544665091113080",
|
||||||
|
bell_delete: "1250544686121095289",
|
||||||
|
stop: "1250544664000467056",
|
||||||
|
creditcards: "1250544663014936798",
|
||||||
|
tag_blue: "1250543263656574996",
|
||||||
|
calendar: "1250543496255897660",
|
||||||
|
book_go: "1250544662037528717",
|
||||||
|
palette: "1250544661035225189",
|
||||||
|
sound: "1250543260196147323",
|
||||||
|
key: "1250543495286882375",
|
||||||
|
book_tabs: "1250544659940380672",
|
||||||
|
book_tabs_delete: "1250555962364919808",
|
||||||
|
wand: "1250544659038601266",
|
||||||
|
tick: "1250543251375653007",
|
||||||
|
microphone: "1250543257398542436",
|
||||||
|
user_add: "1250544566503739412",
|
||||||
|
controller_delete: "1262137043417563256",
|
||||||
|
}).map(([key, val]) => [key, `<:i:${val}>`])
|
||||||
|
);
|
||||||
|
|
||||||
|
// https://docs.discord.sex/resources/guild#guild-features
|
||||||
|
module.exports.GuildFeaturesFormatted = {
|
||||||
|
ACTIVITIES_ALPHA: {icon: SILK_ICONS.joystick, name: "Activities (Alpha)"},
|
||||||
|
ACTIVITIES_EMPLOYEE: {icon: SILK_ICONS.joystick, name: "Activities (Staff)"},
|
||||||
|
ACTIVITIES_INTERNAL_DEV: {
|
||||||
|
icon: SILK_ICONS.joystick,
|
||||||
|
name: "Activities (Internal Dev)",
|
||||||
|
},
|
||||||
|
ACTIVITY_FEED_ENABLED_BY_USER: {icon: SILK_ICONS.controller},
|
||||||
|
ACTIVITY_FEED_DISABLED_BY_USER: {icon: SILK_ICONS.controller_delete},
|
||||||
|
ANIMATED_BANNER: {icon: SILK_ICONS.film},
|
||||||
|
ANIMATED_ICON: {icon: SILK_ICONS.film},
|
||||||
|
APPLICATION_COMMAND_PERMISSIONS_V2: {
|
||||||
|
icon: SILK_ICONS.cog,
|
||||||
|
name: "Command Permissions V2",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
AUTO_MODERATION: {icon: SILK_ICONS.shield, name: "AutoMod"},
|
||||||
|
AUTOMOD_TRIGGER_KEYWORD_FILTER: {
|
||||||
|
icon: SILK_ICONS.shield,
|
||||||
|
name: "AutoMod: Keywords",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
AUTOMOD_TRIGGER_ML_SPAM_FILTER: {
|
||||||
|
icon: SILK_ICONS.shield,
|
||||||
|
name: "AutoMod: Spam",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
AUTOMOD_TRIGGER_SPAM_LINK_FILTER: {
|
||||||
|
icon: SILK_ICONS.shield,
|
||||||
|
name: "AutoMod: Spam Links",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
AUTOMOD_TRIGGER_USER_PROFILE: {
|
||||||
|
icon: SILK_ICONS.vcard,
|
||||||
|
name: "AutoMod: User Profiles",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
BANNER: {icon: SILK_ICONS.photo},
|
||||||
|
BFG: {icon: SILK_ICONS.world_add, name: "BFG"},
|
||||||
|
BOOSTING_TIERS_EXPERIMENT_MEDIUM_GUILD: {
|
||||||
|
icon: SILK_ICONS.ruby,
|
||||||
|
name: "Boosting: Medium Guild",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
BOOSTING_TIERS_EXPERIMENT_SMALL_GUILD: {
|
||||||
|
icon: SILK_ICONS.ruby,
|
||||||
|
name: "Boosting: Small Guild",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
BOT_DEVELOPER_EARLY_ACCESS: {icon: SILK_ICONS.cog},
|
||||||
|
BURST_REACTIONS: {icon: SILK_ICONS.wand, name: "Super Reactions"},
|
||||||
|
CHANNEL_BANNER: {icon: SILK_ICONS.photo, deprecated: true},
|
||||||
|
CHANNEL_ICON_EMOJIS_GENERATED: {
|
||||||
|
icon: SILK_ICONS.emoticon_smile,
|
||||||
|
name: "Channel Icon Emojis",
|
||||||
|
},
|
||||||
|
CHANNEL_HIGHLIGHTS: {icon: SILK_ICONS.comments_star},
|
||||||
|
CHANNEL_HIGHLIGHTS_DISABLED: {icon: SILK_ICONS.comments_delete},
|
||||||
|
CLAN: {icon: SILK_ICONS.flag_blue},
|
||||||
|
CLAN_DISCOVERY_DISABLED: {icon: SILK_ICONS.flag_red},
|
||||||
|
CLAN_PILOT_GENSHIN: {
|
||||||
|
icon: SILK_ICONS.flag_blue,
|
||||||
|
name: "Clan Pilot: Genshin Impact",
|
||||||
|
},
|
||||||
|
CLAN_PILOT_VALORANT: {
|
||||||
|
icon: SILK_ICONS.flag_blue,
|
||||||
|
name: "Clan Pilot: Valorant",
|
||||||
|
},
|
||||||
|
CLAN_PREPILOT_GENSHIN: {
|
||||||
|
icon: SILK_ICONS.flag_blue,
|
||||||
|
name: "Clan Pre-Pilot: Genshin Impact",
|
||||||
|
},
|
||||||
|
CLAN_PREPILOT_VALORANT: {
|
||||||
|
icon: SILK_ICONS.flag_blue,
|
||||||
|
name: "Clan Pre-Pilot: Valorant",
|
||||||
|
},
|
||||||
|
CLYDE_DISABLED: {icon: SILK_ICONS.cog_delete, deprecated: true},
|
||||||
|
CLYDE_ENABLED: {icon: SILK_ICONS.cog, deprecated: true},
|
||||||
|
CLYDE_EXPERIMENT_ENABLED: {icon: SILK_ICONS.bug, deprecated: true},
|
||||||
|
COMMERCE: {icon: SILK_ICONS.basket},
|
||||||
|
COMMUNITY: {icon: SILK_ICONS.group},
|
||||||
|
COMMUNITY_CANARY: {icon: SILK_ICONS.bug, name: "Community (Canary)"},
|
||||||
|
COMMUNITY_EXP_LARGE_GATED: {
|
||||||
|
icon: SILK_ICONS.group,
|
||||||
|
name: "Community: Large - Gated",
|
||||||
|
},
|
||||||
|
COMMUNITY_EXP_LARGE_UNGATED: {
|
||||||
|
icon: SILK_ICONS.group,
|
||||||
|
name: "Community: Large - Ungated",
|
||||||
|
},
|
||||||
|
COMMUNITY_EXP_MEDIUM: {icon: SILK_ICONS.group, name: "Community: Medium"},
|
||||||
|
CREATOR_ACCEPTED_NEW_TERMS: {icon: SILK_ICONS.check_checked_green},
|
||||||
|
CREATOR_MONETIZABLE: {icon: SILK_ICONS.money, name: "Monetization"},
|
||||||
|
CREATOR_MONETIZABLE_DISABLED: {
|
||||||
|
icon: SILK_ICONS.money_delete,
|
||||||
|
name: "Monetization Disabled",
|
||||||
|
},
|
||||||
|
CREATOR_MONETIZABLE_PENDING_NEW_OWNER_ONBOARDING: {
|
||||||
|
icon: SILK_ICONS.money,
|
||||||
|
name: "Monetization: Pending Onboarding",
|
||||||
|
},
|
||||||
|
CREATOR_MONETIZABLE_PROVISIONAL: {
|
||||||
|
icon: SILK_ICONS.money,
|
||||||
|
name: "Monetization (Provisional)",
|
||||||
|
},
|
||||||
|
CREATOR_MONETIZABLE_RESTRICTED: {
|
||||||
|
icon: SILK_ICONS.money_delete,
|
||||||
|
name: "Monetization Restricted",
|
||||||
|
},
|
||||||
|
CREATOR_MONETIZABLE_WHITEGLOVE: {
|
||||||
|
icon: SILK_ICONS.bug,
|
||||||
|
name: "Monetization White Glove",
|
||||||
|
},
|
||||||
|
CREATOR_MONETIZATION_APPLICATION_ALLOWLIST: {
|
||||||
|
icon: SILK_ICONS.money,
|
||||||
|
name: "Monetization Allow List",
|
||||||
|
},
|
||||||
|
CREATOR_STORE_PAGE: {icon: SILK_ICONS.basket},
|
||||||
|
DEVELOPER_SUPPORT_SERVER: {icon: SILK_ICONS.cog},
|
||||||
|
DISCOVERABLE: {icon: SILK_ICONS.world},
|
||||||
|
DISCOVERABLE_DISABLED: {icon: SILK_ICONS.world_delete},
|
||||||
|
ENABLED_DISCOVERABLE_BEFORE: {icon: SILK_ICONS.world},
|
||||||
|
EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT: {
|
||||||
|
icon: SILK_ICONS.bug,
|
||||||
|
name: "Activities: WTP Experiment",
|
||||||
|
},
|
||||||
|
EXPOSED_TO_BOOSTING_TIERS_EXPERIMENT: {
|
||||||
|
icon: SILK_ICONS.bug,
|
||||||
|
name: "Boosting: Tiers Experiment",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
FEATURABLE: {icon: SILK_ICONS.world, deprecated: true},
|
||||||
|
FORCE_RELAY: {icon: SILK_ICONS.transmit_blue, deprecated: true},
|
||||||
|
GENSHIN_L30: {icon: SILK_ICONS.controller, name: "Clans: Genshin Impact L30"},
|
||||||
|
GUESTS_ENABLED: {icon: SILK_ICONS.status_away},
|
||||||
|
GUILD_AUTOMOD_DEFAULT_LIST: {
|
||||||
|
icon: SILK_ICONS.shield,
|
||||||
|
name: "AutoMod: Default List",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_COMMUNICATION_DISABLED_GUILDS: {
|
||||||
|
icon: SILK_ICONS.time,
|
||||||
|
name: "Member Timeouts",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_HOME_DEPRECATION_OVERRIDE: {
|
||||||
|
icon: SILK_ICONS.house,
|
||||||
|
name: "Home Tab Deprecation Hidden",
|
||||||
|
},
|
||||||
|
GUILD_HOME_OVERRIDE: {icon: SILK_ICONS.house, name: "Home Tab (Override)"},
|
||||||
|
GUILD_HOME_TEST: {icon: SILK_ICONS.bug, name: "Home Tab (Testing)"},
|
||||||
|
GUILD_MEMBER_VERIFICATION_EXPERIMENT: {icon: SILK_ICONS.bug},
|
||||||
|
GUILD_ONBOARDING: {
|
||||||
|
icon: SILK_ICONS.application_view_tile,
|
||||||
|
name: "Onboarding",
|
||||||
|
},
|
||||||
|
GUILD_ONBOARDING_ADMIN_ONLY: {
|
||||||
|
icon: SILK_ICONS.application_view_tile,
|
||||||
|
name: "Onboarding: Admin Only",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_ONBOARDING_EVER_ENABLED: {
|
||||||
|
icon: SILK_ICONS.application_view_tile,
|
||||||
|
name: "Onboarding: Ever Enabled",
|
||||||
|
},
|
||||||
|
GUILD_ONBOARDING_HAS_PROMPTS: {
|
||||||
|
icon: SILK_ICONS.application_view_tile,
|
||||||
|
name: "Onboarding: Has Prompts",
|
||||||
|
},
|
||||||
|
GUILD_PRODUCTS: {icon: SILK_ICONS.basket, name: "Products"},
|
||||||
|
GUILD_PRODUCTS_ALLOW_ARCHIVED_FILE: {
|
||||||
|
icon: SILK_ICONS.compress,
|
||||||
|
name: "Products: Allow Archives",
|
||||||
|
},
|
||||||
|
GUILD_ROLE_SUBSCRIPTIONS: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_ROLE_SUBSCRIPTION_PURCHASE_FEEDBACK_LOOP: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions: Feedback Loop",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_ROLE_SUBSCRIPTION_TIER_TEMPLATE: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions: Tier Template",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_ROLE_SUBSCRIPTION_TRIALS: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions: Trials",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
GUILD_SERVER_GUIDE: {icon: SILK_ICONS.book_go, name: "Server Guide"},
|
||||||
|
GUILD_WEB_PAGE_VANITY_URL: {
|
||||||
|
icon: SILK_ICONS.page_link,
|
||||||
|
name: "Guild Web Page Vanity URL",
|
||||||
|
},
|
||||||
|
HAD_EARLY_ACTIVITIES_ACCESS: {
|
||||||
|
icon: SILK_ICONS.joystick,
|
||||||
|
name: "Activities: Had Early Access",
|
||||||
|
},
|
||||||
|
HAS_DIRECTORY_ENTRY: {icon: SILK_ICONS.book_open_mark},
|
||||||
|
HIDE_FROM_EXPERIMENT_UI: {
|
||||||
|
icon: SILK_ICONS.bug,
|
||||||
|
name: "Hidden from Experiment UI",
|
||||||
|
},
|
||||||
|
HUB: {icon: SILK_ICONS.sitemap, name: "Student Hub"},
|
||||||
|
INCREASED_THREAD_LIMIT: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads: Increased Limit",
|
||||||
|
},
|
||||||
|
INTERNAL_EMPLOYEE_ONLY: {
|
||||||
|
icon: SILK_ICONS.key,
|
||||||
|
name: "Staff: Internal Employee Only",
|
||||||
|
},
|
||||||
|
INVITE_SPLASH: {icon: SILK_ICONS.photo},
|
||||||
|
INVITES_DISABLED: {icon: SILK_ICONS.lock},
|
||||||
|
LINKED_TO_HUB: {icon: SILK_ICONS.sitemap, name: "Student Hub: Linked to Hub"},
|
||||||
|
LURKABLE: {icon: SILK_ICONS.eye, deprecated: true},
|
||||||
|
MARKETPLACES_CONNECTION_ROLES: {icon: SILK_ICONS.link, deprecated: true},
|
||||||
|
MEDIA_CHANNEL_ALPHA: {icon: SILK_ICONS.photo, deprecated: true},
|
||||||
|
MEMBER_LIST_DISABLED: {icon: SILK_ICONS.delete, deprecated: true},
|
||||||
|
MEMBER_PROFILES: {icon: SILK_ICONS.vcard, deprecated: true},
|
||||||
|
MEMBER_SAFETY_PAGE_ROLLOUT: {icon: SILK_ICONS.shield},
|
||||||
|
MEMBER_VERIFICATION_GATE_ENABLED: {
|
||||||
|
icon: SILK_ICONS.shield,
|
||||||
|
name: "Member Verification Gate",
|
||||||
|
},
|
||||||
|
MEMBER_VERIFICATION_MANUAL_APPROVAL: {
|
||||||
|
icon: SILK_ICONS.user_add,
|
||||||
|
name: "Member Join Requests",
|
||||||
|
},
|
||||||
|
MOBILE_WEB_ROLE_SUBSCRIPTION_PURCHASE_PAGE: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions: Mobile Page",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
MONETIZATION_ENABLED: {icon: SILK_ICONS.money, deprecated: true},
|
||||||
|
MORE_EMOJI: {icon: SILK_ICONS.emoticon_smile_add},
|
||||||
|
MORE_STICKERS: {icon: SILK_ICONS.photo},
|
||||||
|
NEWS: {icon: SILK_ICONS.newspaper, name: "Announcement Channels"},
|
||||||
|
NEW_THREAD_PERMISSIONS: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads: New Permissions",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
NON_COMMUNITY_RAID_ALERTS: {icon: SILK_ICONS.bell},
|
||||||
|
PARTNERED: {icon: SILK_ICONS.star},
|
||||||
|
PREMIUM_TIER_3_OVERRIDE: {icon: SILK_ICONS.ruby},
|
||||||
|
PREVIEW_ENABLED: {icon: SILK_ICONS.eye},
|
||||||
|
PRIVATE_THREADS: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads: Private Threads",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
PRODUCTS_AVAILABLE_FOR_PURCHASE: {
|
||||||
|
icon: SILK_ICONS.basket,
|
||||||
|
name: "Products: Has Purchasable",
|
||||||
|
},
|
||||||
|
PUBLIC: {icon: SILK_ICONS.world, deprecated: true},
|
||||||
|
PUBLIC_DISABLED: {icon: SILK_ICONS.world_delete, deprecated: true},
|
||||||
|
RAID_ALERTS_DISABLED: {icon: SILK_ICONS.bell_delete},
|
||||||
|
RAID_ALERTS_ENABLED: {icon: SILK_ICONS.bell, deprecated: true},
|
||||||
|
RELAY_ENABLED: {icon: SILK_ICONS.transmit_blue, name: "Sharded"},
|
||||||
|
RESTRICT_SPAM_RISK_GUILDS: {icon: SILK_ICONS.stop, deprecated: true},
|
||||||
|
ROLE_ICONS: {icon: SILK_ICONS.tag_blue},
|
||||||
|
ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions: Has Purchasable",
|
||||||
|
},
|
||||||
|
ROLE_SUBSCRIPTIONS_ENABLED: {icon: SILK_ICONS.creditcards},
|
||||||
|
ROLE_SUBSCRIPTIONS_ENABLED_FOR_PURCHASE: {
|
||||||
|
icon: SILK_ICONS.creditcards,
|
||||||
|
name: "Role Subscriptions: Has Purchasable",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
SEVEN_DAY_THREAD_ARCHIVE: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads: Seven Day Archive",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
SHARD: {icon: SILK_ICONS.sitemap, name: "Student Hub: Shard"},
|
||||||
|
SHARED_CANVAS_FRIENDS_AND_FAMILY_TEST: {
|
||||||
|
icon: SILK_ICONS.palette,
|
||||||
|
name: "Shared Canvas (Testing)",
|
||||||
|
},
|
||||||
|
SOUNDBOARD: {icon: SILK_ICONS.sound},
|
||||||
|
SUMMARIES_ENABLED: {icon: SILK_ICONS.book_tabs, deprecated: true},
|
||||||
|
SUMMARIES_ENABLED_GA: {
|
||||||
|
icon: SILK_ICONS.book_tabs,
|
||||||
|
name: "Summaries (General Access)",
|
||||||
|
},
|
||||||
|
SUMMARIES_DISABLED_BY_USER: {icon: SILK_ICONS.book_tabs_delete},
|
||||||
|
SUMMARIES_ENABLED_BY_USER: {icon: SILK_ICONS.book_tabs},
|
||||||
|
SUMMARIES_LONG_LOOKBACK: {
|
||||||
|
icon: SILK_ICONS.book_tabs,
|
||||||
|
name: "Summaries: Long Lookback",
|
||||||
|
},
|
||||||
|
STAFF_LEVEL_COLLABORATOR_REQUIRED: {
|
||||||
|
icon: SILK_ICONS.key,
|
||||||
|
name: "Staff: Collaborators Only",
|
||||||
|
},
|
||||||
|
STAFF_LEVEL_RESTRICTED_COLLABORATOR_REQUIRED: {
|
||||||
|
icon: SILK_ICONS.key,
|
||||||
|
name: "Staff: Restricted Collaborators Only",
|
||||||
|
},
|
||||||
|
TEXT_IN_STAGE_ENABLED: {icon: SILK_ICONS.comments, deprecated: true},
|
||||||
|
TEXT_IN_VOICE_ENABLED: {icon: SILK_ICONS.comments, deprecated: true},
|
||||||
|
THREADS_ENABLED: {icon: SILK_ICONS.comments, deprecated: true},
|
||||||
|
THREADS_ENABLED_TESTING: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads Enabled (Testing)",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
THREAD_DEFAULT_AUTO_ARCHIVE_DURATION: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads: Default Auto Archive",
|
||||||
|
},
|
||||||
|
THREADS_ONLY_CHANNEL: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Forum Channels",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
THREE_DAY_THREAD_ARCHIVE: {
|
||||||
|
icon: SILK_ICONS.comments,
|
||||||
|
name: "Threads: Three Day Archive",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
TICKETED_EVENTS_ENABLED: {
|
||||||
|
icon: SILK_ICONS.calendar,
|
||||||
|
name: "Scheduled Events",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
TICKETING_ENABLED: {
|
||||||
|
icon: SILK_ICONS.calendar,
|
||||||
|
name: "Scheduled Events",
|
||||||
|
deprecated: true,
|
||||||
|
},
|
||||||
|
VALORANT_L30: {icon: SILK_ICONS.controller, name: "Clans: Valorant L30"},
|
||||||
|
VANITY_URL: {icon: SILK_ICONS.link, name: "Vanity URL"},
|
||||||
|
VERIFIED: {icon: SILK_ICONS.tick},
|
||||||
|
VIP_REGIONS: {icon: SILK_ICONS.microphone, name: "VIP Voice Regions"},
|
||||||
|
VOICE_CHANNEL_EFFECTS: {icon: SILK_ICONS.wand, deprecated: true},
|
||||||
|
VOICE_IN_THREADS: {icon: SILK_ICONS.microphone},
|
||||||
|
WELCOME_SCREEN_ENABLED: {
|
||||||
|
icon: SILK_ICONS.application_view_tile,
|
||||||
|
name: "Onboarding: Welcome Screen",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const PRESENCE_ICONS = {
|
||||||
|
desktop: {
|
||||||
|
online: "<:i:1028887024670871552>",
|
||||||
|
idle: "<:i:1028887022938624033>",
|
||||||
|
dnd: "<:i:1028887021848121364>",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
online: "<:i:1028887017637036043>",
|
||||||
|
idle: "<:i:1028887019226669116>",
|
||||||
|
dnd: "<:i:1028887020560449637>",
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
online: "<:i:1104972136730345552>",
|
||||||
|
idle: "<:i:1104972138735218729>",
|
||||||
|
dnd: "<:i:1104972140685570150>",
|
||||||
|
},
|
||||||
|
embedded: {
|
||||||
|
online: "<:i:1104972131265167411>",
|
||||||
|
idle: "<:i:1104972132687024189>",
|
||||||
|
dnd: "<:i:1104972134964543518>",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const OS_ICONS = {
|
||||||
|
darwin: "\u{1f34e}",
|
||||||
|
win32: "\u{1fa9f}",
|
||||||
|
linux: "\u{1f427}",
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Icons = {
|
||||||
|
silk: SILK_ICONS,
|
||||||
|
presence: PRESENCE_ICONS,
|
||||||
|
os: OS_ICONS,
|
||||||
|
online: "<:i:1152111668856361010>",
|
||||||
|
offline: "<:i:1152111682886316042>",
|
||||||
|
blank: "<:i:1250561747476152460>",
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.ChannelTypeNames = {
|
||||||
|
0: "text",
|
||||||
|
2: "voice",
|
||||||
|
4: "category",
|
||||||
|
5: "announcement",
|
||||||
|
13: "stage",
|
||||||
|
14: "hub directory",
|
||||||
|
15: "forum",
|
||||||
|
16: "media",
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMOJI_SETS = {
|
||||||
|
blobs: {
|
||||||
|
prefix: "https://cdn.jsdelivr.net/gh/googlefonts/noto-emoji@e456654119cc3a5f9bebb7bbd00512456f983d2d/svg/emoji_u",
|
||||||
|
sep: "_",
|
||||||
|
suffix: ".svg",
|
||||||
|
},
|
||||||
|
noto: {
|
||||||
|
prefix: "https://cdn.jsdelivr.net/gh/googlefonts/noto-emoji@master/svg/emoji_u",
|
||||||
|
sep: "_",
|
||||||
|
suffix: ".svg",
|
||||||
|
},
|
||||||
|
twemoji: {
|
||||||
|
prefix: "https://jdecked.github.io/twemoji/v/latest/svg/",
|
||||||
|
sep: "-",
|
||||||
|
suffix: ".svg",
|
||||||
|
},
|
||||||
|
mustd: {
|
||||||
|
prefix:
|
||||||
|
"https://cdn.jsdelivr.net/gh/Mstrodl/mutant-standard-mirror@0435227d9d8c0d6a346c8ae4c12b08a5cdc37041/emoji/",
|
||||||
|
sep: "-",
|
||||||
|
suffix: ".svg",
|
||||||
|
},
|
||||||
|
/*apple: {
|
||||||
|
prefix: "https://intrnl.github.io/assetsEmoji/AppleColor/emoji_u",
|
||||||
|
sep: "_",
|
||||||
|
suffix: ".png",
|
||||||
|
},
|
||||||
|
facebook: {
|
||||||
|
prefix: "https://intrnl.github.io/assetsEmoji/facebook/emoji_u",
|
||||||
|
sep: "_",
|
||||||
|
suffix: ".png",
|
||||||
|
},*/
|
||||||
|
};
|
||||||
|
EMOJI_SETS["noto-old"] = EMOJI_SETS.blobs;
|
||||||
|
EMOJI_SETS.mutant = EMOJI_SETS.mustd;
|
||||||
|
EMOJI_SETS.mutstd = EMOJI_SETS.mustd;
|
||||||
|
//EMOJI_SETS.ms = EMOJI_SETS.mustd;
|
||||||
|
EMOJI_SETS.twitter = EMOJI_SETS.twemoji;
|
||||||
|
//EMOJI_SETS.fb = EMOJI_SETS.facebook;
|
||||||
|
module.exports.EmojiSets = EMOJI_SETS;
|
||||||
|
|
||||||
|
const EmojiData = require("../../data/emoji.json");
|
||||||
|
const EMOJI_NAMES = [];
|
||||||
|
for (const emoji of EmojiData) {
|
||||||
|
EMOJI_NAMES[emoji.char] = emoji.name.replace(/ /g, "_");
|
||||||
|
}
|
||||||
|
module.exports.EmojiNames = EMOJI_NAMES;
|
||||||
|
|
||||||
|
module.exports.RegExp = {
|
||||||
|
Emote: /<(?:\u200b|&)?(a)?:(\w+):(\d+)>/,
|
||||||
|
Pomelo: /^[a-z0-9._]{1,32}$/,
|
||||||
|
Snowflake: /([0-9]{17,21})/,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.ApplicationFlagNames = [
|
||||||
|
undefined,
|
||||||
|
"~~Embedded Released~~",
|
||||||
|
"Create Managed Emoji",
|
||||||
|
"Embedded In-App Purchases",
|
||||||
|
"Create Group DMs",
|
||||||
|
"~~RPC Private Beta~~",
|
||||||
|
"Uses AutoMod",
|
||||||
|
undefined,
|
||||||
|
"~~Allow Assets~~",
|
||||||
|
"~~Allow Spectate~~",
|
||||||
|
"~~Allow Join Requests~~",
|
||||||
|
"~~Has Used RPC~~",
|
||||||
|
"Presence Intent",
|
||||||
|
"Presence Intent (Unverified)",
|
||||||
|
"Guild Members Intent",
|
||||||
|
"Guild Members Intent (Unverified)",
|
||||||
|
"Suspicious Growth",
|
||||||
|
"Embedded",
|
||||||
|
"Message Content Intent",
|
||||||
|
"Message Content Intent (Unverified)",
|
||||||
|
"Embedded (First Party)",
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
"Supports Commands",
|
||||||
|
"Active",
|
||||||
|
undefined,
|
||||||
|
"iframe Modals",
|
||||||
|
"Social Layer Integration",
|
||||||
|
];
|
163
src/util/dconstants.js
Normal file
163
src/util/dconstants.js
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
const {Constants} = require("@projectdysnomia/dysnomia");
|
||||||
|
const Endpoints = require("@projectdysnomia/dysnomia/lib/rest/Endpoints.js");
|
||||||
|
|
||||||
|
module.exports = {...Constants};
|
||||||
|
|
||||||
|
module.exports.ActivityTypeNames = [
|
||||||
|
"Playing",
|
||||||
|
"Streaming",
|
||||||
|
"Listening to",
|
||||||
|
"Watching",
|
||||||
|
"Custom Status",
|
||||||
|
"Competing in",
|
||||||
|
"Hang Status",
|
||||||
|
];
|
||||||
|
|
||||||
|
const APIEndpoints = {...Endpoints};
|
||||||
|
|
||||||
|
delete APIEndpoints.BASE_URL;
|
||||||
|
delete APIEndpoints.CDN_URL;
|
||||||
|
delete APIEndpoints.CLIENT_URL;
|
||||||
|
|
||||||
|
delete APIEndpoints.APPLICATION_ASSET;
|
||||||
|
delete APIEndpoints.APPLICATION_ICON;
|
||||||
|
delete APIEndpoints.BANNER;
|
||||||
|
delete APIEndpoints.CHANNEL_ICON;
|
||||||
|
delete APIEndpoints.CUSTOM_EMOJI;
|
||||||
|
delete APIEndpoints.DEFAULT_USER_AVATAR;
|
||||||
|
delete APIEndpoints.GUILD_AVATAR;
|
||||||
|
delete APIEndpoints.GUILD_DISCOVERY_SPLASH;
|
||||||
|
delete APIEndpoints.GUILD_ICON;
|
||||||
|
delete APIEndpoints.GUILD_SCHEDULED_EVENT_COVER;
|
||||||
|
delete APIEndpoints.GUILD_SPLASH;
|
||||||
|
delete APIEndpoints.ROLE_ICON;
|
||||||
|
delete APIEndpoints.TEAM_ICON;
|
||||||
|
delete APIEndpoints.USER_AVATAR;
|
||||||
|
delete APIEndpoints.USER_AVATAR_DECORATION_PRESET;
|
||||||
|
|
||||||
|
delete APIEndpoints.MESSAGE_LINK;
|
||||||
|
|
||||||
|
APIEndpoints.APPLICATION_ASSETS = (applicationID) => `/oauth2/applications/${applicationID}/assets`; // prettier-ignore
|
||||||
|
APIEndpoints.APPLICATION_RPC = (applicationID) => `/applications/${applicationID}/rpc`; // prettier-ignore
|
||||||
|
APIEndpoints.ATTACHMENT_REFRESH = "/attachments/refresh-urls"; // prettier-ignore
|
||||||
|
APIEndpoints.CLAN = (guildID) => `/discovery/${guildID}/clan`; // prettier-ignore
|
||||||
|
APIEndpoints.DISCOVERY_SLUG = (guildID) => `/discovery/${guildID}`; // prettier-ignore
|
||||||
|
APIEndpoints.GUILD_MEMBER_VERIFICATION = (guildID) => `/guilds/${guildID}/member-verification`; // prettier-ignore
|
||||||
|
APIEndpoints.POMELO_UNAUTHED = "/unique-username/username-attempt-unauthed"; // prettier-ignore
|
||||||
|
APIEndpoints.STORE_PUBLISHED_LISTING = (skuID) => `/store/published-listings/skus/${skuID}`; // prettier-ignore
|
||||||
|
|
||||||
|
module.exports.APIEndpoints = APIEndpoints;
|
||||||
|
|
||||||
|
module.exports.API_URL = Endpoints.CLIENT_URL + Endpoints.BASE_URL;
|
||||||
|
|
||||||
|
module.exports.ApplicationFlags.EMBEDDED_RELEASED = 1 << 1; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.MANAGED_EMOJI = 1 << 2; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.EMBEDDED_IAP = 1 << 3; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.GROUP_DM_CREATE = 1 << 4; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.RPC_PRIVATE_BETA = 1 << 5; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.ALLOW_ASSETS = 1 << 8; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.ALLOW_ACTIVITY_ACTION_SPECTATE = 1 << 9; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.ALLOW_ACTIVITY_ACTION_JOIN_REQUEST = 1 << 10; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.RPC_HAS_CONNECTED = 1 << 11; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.EMBEDDED_FIRST_PARTY = 1 << 20; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.ACTIVE = 1 << 24; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.IFRAME_MODAL = 1 << 26; // prettier-ignore
|
||||||
|
module.exports.ApplicationFlags.SOCIAL_LAYER_INTEGRATION = 1 << 27; // prettier-ignore
|
||||||
|
|
||||||
|
module.exports.ApplicationTypes = ["Normal", "Game", "Music", "Ticketed Event", "Creator Monetization"];
|
||||||
|
|
||||||
|
module.exports.BASE_URL = Endpoints.BASE_URL;
|
||||||
|
|
||||||
|
const CDNEndpoints = {};
|
||||||
|
CDNEndpoints.APP_ASSET = (applicationID, asset) => `${Endpoints.CDN_URL}/app-assets/${applicationID}/${asset}.png`; // prettier-ignore
|
||||||
|
CDNEndpoints.APP_ICON = (applicationID, icon, size = 4096) => `${Endpoints.CDN_URL}/app-icons/${applicationID}/${icon}.png?size=${size}`; // prettier-ignore
|
||||||
|
CDNEndpoints.AVATAR_DECORATION = (decoration, size = 4096) => `${Endpoints.CDN_URL}/avatar-decoration-presets/${decoration}.png?size=${size}&passthrough=true`; // prettier-ignore
|
||||||
|
CDNEndpoints.BANNER = (id, banner, size = 4096) => `${Endpoints.CDN_URL}/banners/${id}/${banner}.${banner.startsWith("a_") ? `gif?size=${size}&_=.gif` : `png?size=${size}`}`; // prettier-ignore
|
||||||
|
CDNEndpoints.CLAN_BADGE = (guildID, badge, size = 4096) => `${Endpoints.CDN_URL}/clan-badges/${guildID}/${badge}.png?size=${size}`; // prettier-ignore
|
||||||
|
CDNEndpoints.CLAN_BANNER = (guildID, banner, size = 4096) => `${Endpoints.CDN_URL}/clan-banners/${guildID}/${banner}.png?size=${size}`; // prettier-ignore
|
||||||
|
CDNEndpoints.DEFAULT_AVATAR = (index, size = 4096) => `${Endpoints.CDN_URL}/embed/avatars/${index}.png?size=${size}`; // prettier-ignore
|
||||||
|
CDNEndpoints.DISCOVERY_SPLASH = (guildID, splash, size = 4096) => `${Endpoints.CDN_URL}/discovery-splashes/${guildID}/${splash}.png?size=${size}`; // prettier-ignore
|
||||||
|
CDNEndpoints.EMOJI = (emojiID, animated = false, webp = false) => `${Endpoints.CDN_URL}/emojis/${emojiID}.${webp ? "webp" : animated ? "gif?_=.gif" : "png"}`; // prettier-ignore
|
||||||
|
CDNEndpoints.GDM_ICON = (channelID, icon, size = 4096) => `${Endpoints.CDN_URL}/channel-icons/${channelID}/${icon}.${icon.startsWith("a_") ? `gif?size=${size}&_=.gif` : `png?size=${size}`}`; // prettier-ignore
|
||||||
|
CDNEndpoints.GUILD_ICON = (guildID, icon, size = 4096) => `${Endpoints.CDN_URL}/icons/${guildID}/${icon}.${icon.startsWith("a_") ? `gif?size=${size}&_=.gif` : `png?size=${size}`}`; // prettier-ignore
|
||||||
|
CDNEndpoints.GUILD_MEMBER_AVATAR = (guildID, userID, avatar, size = 4096) => `${Endpoints.CDN_URL}/guilds/${guildID}/users/${userID}/avatars/${avatar}.${avatar.startsWith("a_") ? `gif?size=${size}&_=.gif` : `png?size=${size}`}`; // prettier-ignore
|
||||||
|
CDNEndpoints.GUILD_MEMBER_BANNER = (guildID, userID, banner, size = 4096) => `${Endpoints.CDN_URL}/guilds/${guildID}/users/${userID}/banners/${banner}.${banner.startsWith("a_") ? `gif?size=${size}&_=.gif` : `png?size=${size}`}`; // prettier-ignore
|
||||||
|
CDNEndpoints.GUILD_SPLASH = (guildID, splash, size = 4096) => `${Endpoints.CDN_URL}/splashes/${guildID}/${splash}.png?size=${size}`; // prettier-ignore
|
||||||
|
CDNEndpoints.USER_AVATAR = (userID, avatar, size = 4096) => `${Endpoints.CDN_URL}/avatars/${userID}/${avatar}.${avatar.startsWith("a_") ? `gif?size=${size}&_=.gif` : `png?size=${size}`}`; // prettier-ignore
|
||||||
|
module.exports.CDNEndpoints = CDNEndpoints;
|
||||||
|
|
||||||
|
module.exports.CDN_URL = Endpoints.CDN_URL;
|
||||||
|
module.exports.CLIENT_URL = Endpoints.CLIENT_URL;
|
||||||
|
|
||||||
|
module.exports.ClanPlaystyle = ["None", "Very Casual", "Casual", "Competitive", "Creative", "Very Competitive"];
|
||||||
|
|
||||||
|
module.exports.DEFAULT_GROUP_DM_AVATARS = [
|
||||||
|
"/assets/ee9275c5a437f7dc7f9430ba95f12ebd.png",
|
||||||
|
"/assets/9baf45aac2a0ec2e2dab288333acb9d9.png",
|
||||||
|
"/assets/7ba11ffb1900fa2b088cb31324242047.png",
|
||||||
|
"/assets/f90fca70610c4898bc57b58bce92f587.png",
|
||||||
|
"/assets/e2779af34b8d9126b77420e5f09213ce.png",
|
||||||
|
"/assets/c6851bd0b03f1cca5a8c1e720ea6ea17.png",
|
||||||
|
"/assets/f7e38ac976a2a696161c923502a8345b.png",
|
||||||
|
"/assets/3cb840d03313467838d658bbec801fcd.png",
|
||||||
|
].map((i) => Endpoints.CLIENT_URL + i);
|
||||||
|
|
||||||
|
module.exports.ExplicitContentFilterStrings = ["Disabled", "Members without roles", "All members"];
|
||||||
|
|
||||||
|
module.exports.Games = require("../../data/games.json");
|
||||||
|
|
||||||
|
module.exports.HANG_STATUS_ICONS = Object.fromEntries(
|
||||||
|
Object.entries({
|
||||||
|
chilling: "/assets/d5df7edf0b2f38954140.svg",
|
||||||
|
gaming: "/assets/0c7aa94c8471f12c1583.svg",
|
||||||
|
focusing: "/assets/5ba60eea3fdfa7b63f2e.svg",
|
||||||
|
brb: "/assets/194dda5f6443bc84d703.svg",
|
||||||
|
eating: "/assets/5f41131fa82e3a74fbf7.svg",
|
||||||
|
"in-transit": "/assets/0cb37cec5b33d664373a.svg",
|
||||||
|
watching: "/assets/b7832fa2d0b75e26e279.svg",
|
||||||
|
}).map((x) => ((x[1] = Endpoints.CLIENT_URL + x[1]), x))
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports.HangStatusStrings = {
|
||||||
|
chilling: "Chilling",
|
||||||
|
gaming: "GAMING",
|
||||||
|
focusing: "In the zone",
|
||||||
|
brb: "Gonna BRB",
|
||||||
|
eating: "Grubbin",
|
||||||
|
"in-transit": "Wandering IRL",
|
||||||
|
watching: "Watchin' stuff",
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.UPLOAD_LIMIT = 26214400;
|
||||||
|
module.exports.UPLOAD_LIMIT_TIER_2 = 52428800;
|
||||||
|
module.exports.UPLOAD_LIMIT_TIER_3 = 104857600;
|
||||||
|
|
||||||
|
module.exports.UserFlags.MFA_SMS = 1 << 4; // prettier-ignore
|
||||||
|
module.exports.UserFlags.PREMIUM_PROMO_DISMISSED = 1 << 5; // prettier-ignore
|
||||||
|
module.exports.UserFlags.INTERNAL_APPLICATION = 1 << 11; // prettier-ignore
|
||||||
|
module.exports.UserFlags.HAS_UNREAD_URGENT_MESSAGES = 1 << 13; // prettier-ignore
|
||||||
|
module.exports.UserFlags.UNDERAGE_DELETED = 1 << 15; // prettier-ignore
|
||||||
|
module.exports.UserFlags.DISABLE_PREMIUM = 1 << 21; // prettier-ignore
|
||||||
|
module.exports.UserFlags.PROVISIONAL_ACCOUNT = 1 << 23; // prettier-ignore
|
||||||
|
module.exports.UserFlags.QUARANTINED = Number(1n << 44n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.COLLABORATOR = Number(1n << 50n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.RESTRICTED_COLLABORATOR = Number(1n << 51n); // prettier-ignore
|
||||||
|
|
||||||
|
// admin panel leak aug 2022
|
||||||
|
module.exports.UserFlags.HIGH_GLOBAL_RATE_LIMIT = Number(1n << 33n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.DELETED = Number(1n << 34n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.DISABLED_SUSPICIOUS_ACTIVITY = Number(1n << 35n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.SELF_DELETED = Number(1n << 36n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.PREMIUM_DISCRIMINATOR = Number(1n << 37n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.USED_DESKTOP_CLIENT = Number(1n << 38n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.USED_WEB_CLIENT = Number(1n << 39n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.USED_MOBILE_CLIENT = Number(1n << 40n); // prettier-ignore
|
||||||
|
module.exports.UserFlags.VERIFIED_EMAIL = Number(1n << 43n); // prettier-ignore
|
||||||
|
|
||||||
|
module.exports.VerificationLevelStrings = [
|
||||||
|
"None",
|
||||||
|
"Low",
|
||||||
|
"Medium",
|
||||||
|
"(╯°□°)╯︵ ┻━┻ (High)",
|
||||||
|
"┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻ (Very High/Phone)",
|
||||||
|
];
|
103
src/util/html.js
Normal file
103
src/util/html.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// https://stackoverflow.com/a/39243641
|
||||||
|
const htmlEntities = {
|
||||||
|
nbsp: " ",
|
||||||
|
cent: "¢",
|
||||||
|
pound: "£",
|
||||||
|
yen: "¥",
|
||||||
|
euro: "€",
|
||||||
|
copy: "©",
|
||||||
|
reg: "®",
|
||||||
|
lt: "<",
|
||||||
|
gt: ">",
|
||||||
|
quot: '"',
|
||||||
|
amp: "&",
|
||||||
|
apos: "'",
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseHtmlEntities(str) {
|
||||||
|
return str.replace(/&([^;]+);/g, function (entity, entityCode) {
|
||||||
|
let match;
|
||||||
|
|
||||||
|
if (entityCode in htmlEntities) {
|
||||||
|
return htmlEntities[entityCode];
|
||||||
|
} else if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
||||||
|
return String.fromCharCode(parseInt(match[1], 16));
|
||||||
|
} else if ((match = entityCode.match(/^#(\d+)$/))) {
|
||||||
|
return String.fromCharCode(~~match[1]);
|
||||||
|
} else {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlToMarkdown(str, images = true, embed = true) {
|
||||||
|
str = str.replaceAll("\\", "\\\\");
|
||||||
|
str = str.replaceAll("#", "\\#");
|
||||||
|
str = str.replace(/<style(\s+[^>]+)?>(.|\n)*?<\/style>/gi, "");
|
||||||
|
str = str.replace(
|
||||||
|
/<a (\s+[^>]+)?href="([^"]+?)"(\s+[^>]+)?>(.+?)<\/a>/gi,
|
||||||
|
(_, __, url, ___, text) => {
|
||||||
|
url = url.replace(/^\/\//, "https://").replace("\\#", "#");
|
||||||
|
return url == text
|
||||||
|
? url
|
||||||
|
: `[${text}](${embed ? "" : "<"}${url}${embed ? "" : ">"})`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (images)
|
||||||
|
str = str.replace(
|
||||||
|
/<img (\s+[^>]+)?src="([^"]+?)"(\s+[^>]+)?(alt|title)="([^"]+?)"(\s+[^>]+)?\/>/gi,
|
||||||
|
`[$5](${embed ? "" : "<"}$2${embed ? "" : ">"})`
|
||||||
|
);
|
||||||
|
str = str.replace(/<\/?\s*br\s*\/?>/gi, "\n");
|
||||||
|
str = str.replace(
|
||||||
|
/<blockquote[^>]*?>((.|\n)*?)<\/blockquote>/gi,
|
||||||
|
(_, quote) => "> " + quote.split("\n").join("\n> ")
|
||||||
|
);
|
||||||
|
str = str.replace(/<\/?p>/gi, "\n");
|
||||||
|
str = str.replace(
|
||||||
|
/<dd>((.|\n)*?)<\/dd>/gi,
|
||||||
|
(_, inner) => "\u3000\u3000" + inner.split("\n").join("\n\u3000\u3000")
|
||||||
|
);
|
||||||
|
str = str.replace(/<ol(\s+[^>]+)?>((.|\n)*?)<\/ol>/gi, (_, __, inner) => {
|
||||||
|
let index = 0;
|
||||||
|
return inner
|
||||||
|
.replace(/<li>/gi, () => {
|
||||||
|
index++;
|
||||||
|
return `${index}. `;
|
||||||
|
})
|
||||||
|
.replace(/<\/li>/gi, "\n")
|
||||||
|
.replaceAll("\n\n", "\n");
|
||||||
|
});
|
||||||
|
str = str.replace(/<ul(\s+[^>]+)?>((.|\n)*?)<\/ul>/gi, (_, __, inner) => {
|
||||||
|
let index = 0;
|
||||||
|
return inner
|
||||||
|
.replace(/<li>/gi, () => {
|
||||||
|
index++;
|
||||||
|
return `${index}. `;
|
||||||
|
})
|
||||||
|
.replace(/<\/li>/gi, "\n")
|
||||||
|
.replaceAll("\n\n", "\n");
|
||||||
|
});
|
||||||
|
str = str.replace(/<\/?code(\s+[^>]+)?>/gi, "`");
|
||||||
|
str = str.replace(/<\/?em(\s+[^>]+)?>/gi, "_");
|
||||||
|
str = str.replace(/<\/?i(\s+[^>]+)?>/gi, "_");
|
||||||
|
str = str.replace(/<\/?b(\s+[^>]+)?>/gi, "**");
|
||||||
|
str = str.replace(/<\/?u(\s+[^>]+)?>/gi, "__");
|
||||||
|
str = str.replace(/<\/?s(\s+[^>]+)?>/gi, "~~");
|
||||||
|
str = str.replace(/<h1(\s+[^>]+)?>/gi, "# ");
|
||||||
|
str = str.replace(/<h2(\s+[^>]+)?>/gi, "## ");
|
||||||
|
str = str.replace(/<h3(\s+[^>]+)?>/gi, "### ");
|
||||||
|
str = str.replace(/<\/?h4(\s+[^>]+)?>/gi, "**");
|
||||||
|
str = str.replace(
|
||||||
|
/<(math|noscript)(\s+[^>]+)?>((.|\n)*?)<\/(math|noscript)>/gi,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
str = str.replace(/<[^>]+?>/gi, "");
|
||||||
|
str = parseHtmlEntities(str);
|
||||||
|
// whyyyyyyyyyyyy
|
||||||
|
str = str.replace(/\[https?:\/\/.+?\]\(<?(https?:\/\/.+?)>?\)/gi, "$1");
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {parseHtmlEntities, htmlToMarkdown};
|
178
src/util/image.js
Normal file
178
src/util/image.js
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
async function findSuitableImage(msg) {
|
||||||
|
const out = {};
|
||||||
|
|
||||||
|
const attachments = [...msg.attachments.values()];
|
||||||
|
const attachment = attachments[0];
|
||||||
|
|
||||||
|
if (attachment) {
|
||||||
|
const url = attachment.url;
|
||||||
|
const parsed = new URL(url);
|
||||||
|
if (/(jpe?g|png|gif|webp)$/.test(parsed.pathname)) {
|
||||||
|
out.url = url;
|
||||||
|
} else if (/(mp4|webm|mov)$/.test(parsed.pathname)) {
|
||||||
|
out.video = true;
|
||||||
|
out.attachment = true;
|
||||||
|
out.url = "attachment://thumb.jpg";
|
||||||
|
|
||||||
|
let thumbUrl = attachment.proxyURL;
|
||||||
|
if (thumbUrl.includes("media.discordapp.net") && thumbUrl.includes("?")) {
|
||||||
|
if (thumbUrl.endsWith("&")) {
|
||||||
|
thumbUrl = thumbUrl += "format=jpeg";
|
||||||
|
} else {
|
||||||
|
thumbUrl = thumbUrl += "&format=jpeg";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thumbUrl = thumbUrl += "?format=jpeg";
|
||||||
|
}
|
||||||
|
|
||||||
|
out.file = await fetch(thumbUrl)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((buf) => Buffer.from(buf));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const embed of msg.embeds) {
|
||||||
|
if (!embed.url) continue;
|
||||||
|
const parsed = new URL(embed.url);
|
||||||
|
if (embed.url && /(jpe?g|png|gif|webp)$/.test(parsed.pathname)) {
|
||||||
|
out.url = embed.url;
|
||||||
|
} else if (embed.image) {
|
||||||
|
out.url = embed.image.url;
|
||||||
|
break;
|
||||||
|
} else if (embed.video) {
|
||||||
|
out.video = true;
|
||||||
|
out.url = "attachment://thumb.jpg";
|
||||||
|
|
||||||
|
let thumbUrl =
|
||||||
|
embed.video.proxyURL ??
|
||||||
|
embed.video.url.replace("cdn.discordapp.com", "media.discordapp.net");
|
||||||
|
if (
|
||||||
|
thumbUrl.includes("media.discordapp.net") &&
|
||||||
|
thumbUrl.includes("?")
|
||||||
|
) {
|
||||||
|
if (thumbUrl.endsWith("&")) {
|
||||||
|
thumbUrl = thumbUrl += "format=jpeg";
|
||||||
|
} else {
|
||||||
|
thumbUrl = thumbUrl += "&format=jpeg";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thumbUrl = thumbUrl += "?format=jpeg";
|
||||||
|
}
|
||||||
|
if (embed.thumbnail?.url) thumbUrl = embed.thumbnail.url;
|
||||||
|
|
||||||
|
let valid = await fetch(thumbUrl, {method: "HEAD"}).then(
|
||||||
|
(res) => res.ok
|
||||||
|
);
|
||||||
|
if (!valid && thumbUrl.includes("media.discordapp.net")) {
|
||||||
|
thumbUrl = await hf.bot.requestHandler
|
||||||
|
.request("POST", "/attachments/refresh-urls", true, {
|
||||||
|
attachment_urls: [thumbUrl],
|
||||||
|
})
|
||||||
|
.then((res) => res.refreshed_urls[0].refreshed);
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
out.file = await fetch(thumbUrl)
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((buf) => Buffer.from(buf));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isGif(url) {
|
||||||
|
const type = await fetch(url, {method: "HEAD"}).then((res) =>
|
||||||
|
res.headers.get("Content-Type")
|
||||||
|
);
|
||||||
|
return type == "image/gif";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findLastImage(msg, gif = false) {
|
||||||
|
const messages = await msg.channel.getMessages(20);
|
||||||
|
let img;
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
if (message.attachments.size > 0) {
|
||||||
|
img = [...msg.attachments.values()][0].url;
|
||||||
|
if (gif && (await isGif(img))) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (message.embeds.length > 0) {
|
||||||
|
img = message.embeds[0]?.thumbnail?.url || message.embeds[0]?.image?.url;
|
||||||
|
if (img) {
|
||||||
|
if (gif && (await isGif(img))) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
if (!img) {
|
||||||
|
reject("Image not found in last 20 messages.");
|
||||||
|
} else {
|
||||||
|
resolve(img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlRegex = /((https?):\/)?\/?([^:/\s]+)((\/\w+)*\/)([\w\-.]+)/;
|
||||||
|
|
||||||
|
async function getImage(msg, str) {
|
||||||
|
const refMsg = msg.referencedMessage;
|
||||||
|
if (refMsg) {
|
||||||
|
const attachments = [...refMsg.attachments.values()];
|
||||||
|
if (attachments[0]?.url) {
|
||||||
|
return attachments[0].url;
|
||||||
|
} else if (/<a?:[a-zA-Z0-9_]+:([0-9]+)>/.test(refMsg.content)) {
|
||||||
|
const id = refMsg.content.match(/<a?:[a-zA-Z0-9_]+:([0-9]+)>/)[1];
|
||||||
|
return `https://cdn.discordapp.com/emojis/${id}.png?v=1`;
|
||||||
|
} else if (/<@!?([0-9]+)>/.test(refMsg.content)) {
|
||||||
|
const id = refMsg.content.match(/<@!?([0-9]+)>/)[1];
|
||||||
|
const user = await hf.bot.requestHandler.request(
|
||||||
|
"GET",
|
||||||
|
"/users/" + id,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (user)
|
||||||
|
return `https://cdn.discordapp.com/avatars/${id}/${user.avatar}.png?size=1024`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const img = await findLastImage(msg, false);
|
||||||
|
if (!str) {
|
||||||
|
if (img) return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attachments = [...msg.attachments.values()];
|
||||||
|
if (attachments[0]?.url) {
|
||||||
|
return attachments[0]?.url;
|
||||||
|
} else if (urlRegex.test(str)) {
|
||||||
|
return str;
|
||||||
|
} else if (/<a?:[a-zA-Z0-9_]+:([0-9]+)>/.test(str)) {
|
||||||
|
const id = str.match(/<a?:[a-zA-Z0-9_]+:([0-9]+)>/)[1];
|
||||||
|
return `https://cdn.discordapp.com/emojis/${id}.png?v=1`;
|
||||||
|
} else if (/<@!?([0-9]+)>/.test(str)) {
|
||||||
|
const id = str.match(/<@!?([0-9]+)>/)[1];
|
||||||
|
const user = await hf.bot.requestHandler.request(
|
||||||
|
"GET",
|
||||||
|
"/users/" + id,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (user)
|
||||||
|
return `https://cdn.discordapp.com/avatars/${id}/${user.avatar}.png?size=1024`;
|
||||||
|
} else if (img) {
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {findSuitableImage, isGif, findLastImage, getImage};
|
172
src/util/misc.js
Normal file
172
src/util/misc.js
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
const murmurhash = require("murmurhash").v3;
|
||||||
|
const {tinycolor} = require("@ctrl/tinycolor");
|
||||||
|
|
||||||
|
const {APIEndpoints, CDNEndpoints, UPLOAD_LIMIT, UPLOAD_LIMIT_TIER_2, UPLOAD_LIMIT_TIER_3} = require("./dconstants.js");
|
||||||
|
const {GuildFeaturesFormatted, Icons} = require("./constants.js");
|
||||||
|
|
||||||
|
function pastelize(id) {
|
||||||
|
const hue = murmurhash(id) % 360;
|
||||||
|
const hex = tinycolor(`hsl(${hue},75%,60%)`).toHex();
|
||||||
|
return parseInt(hex, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatUsername(user) {
|
||||||
|
return user.discriminator && user.discriminator != "0"
|
||||||
|
? `${user.username}#${user.discriminator}`
|
||||||
|
: `@${user.username}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTopColor(msg, id, fallback = 0x7289da) {
|
||||||
|
if (!msg.guildID) return fallback;
|
||||||
|
const guild = msg.channel?.guild ?? hf.bot.guilds.get(msg.guildID);
|
||||||
|
if (!guild) return fallback;
|
||||||
|
|
||||||
|
const roles = guild.members
|
||||||
|
.get(id)
|
||||||
|
.roles.map((role) => guild.roles.get(role))
|
||||||
|
.filter((role) => role.color);
|
||||||
|
roles.sort((a, b) => b.position - a.position);
|
||||||
|
|
||||||
|
return roles[0]?.color || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeString(string, newLines = true) {
|
||||||
|
string = string ? string.toString() : "";
|
||||||
|
string = string.replace(/`/g, "'");
|
||||||
|
string = string.replace(/<@/g, "<@\u200b");
|
||||||
|
string = string.replace(/<#/g, "<#\u200b");
|
||||||
|
string = string.replace(/<&/g, "<&\u200b");
|
||||||
|
if (newLines) string = string.replace(/\n/g, " ");
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hastebin(body) {
|
||||||
|
const res = await fetch(`${hf.config.haste_provider}/documents`, {
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
}).then((r) => r.json());
|
||||||
|
return `<${hf.config.haste_provider}/${res.key}>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUploadLimit(guild) {
|
||||||
|
if (!guild) return UPLOAD_LIMIT;
|
||||||
|
|
||||||
|
if (guild.premiumTier == 2) return UPLOAD_LIMIT_TIER_2;
|
||||||
|
if (guild.premiumTier == 3) return UPLOAD_LIMIT_TIER_3;
|
||||||
|
|
||||||
|
return UPLOAD_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGuild(id) {
|
||||||
|
if (hf.bot.guilds.has(id)) {
|
||||||
|
return {source: "local", data: hf.bot.guilds.get(id)};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const preview = await hf.bot.requestHandler.request("GET", APIEndpoints.GUILD_PREVIEW(id), true);
|
||||||
|
if (preview) return {source: "preview", data: preview};
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
const widget = await hf.bot.requestHandler.request("GET", APIEndpoints.GUILD_WIDGET(id), false);
|
||||||
|
if (widget) return {source: "widget", data: widget};
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
const discovery = await hf.bot.requestHandler.request("GET", APIEndpoints.DISCOVERY_SLUG(id), false);
|
||||||
|
if (discovery) return {source: "discovery", data: discovery};
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
const verification = await hf.bot.requestHandler.request(
|
||||||
|
"GET",
|
||||||
|
`${APIEndpoints.GUILD_MEMBER_VERIFICATION(id)}?with_guild=true`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (verification?.guild) return {source: "verification", data: verification.guild};
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enumKeyToName(key) {
|
||||||
|
return key
|
||||||
|
.split("_")
|
||||||
|
.map((x) => x[0] + x.substring(1).toLowerCase())
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatGuildFeatures(features) {
|
||||||
|
return features
|
||||||
|
.sort((a, b) => {
|
||||||
|
const feature_a = GuildFeaturesFormatted[a];
|
||||||
|
const feature_b = GuildFeaturesFormatted[b];
|
||||||
|
|
||||||
|
return (feature_a?.name ?? enumKeyToName(a)).localeCompare(feature_b?.name ?? enumKeyToName(b));
|
||||||
|
})
|
||||||
|
.map(
|
||||||
|
(feature) =>
|
||||||
|
`${GuildFeaturesFormatted[feature]?.icon ?? Icons.blank} ${
|
||||||
|
GuildFeaturesFormatted[feature]?.deprecated ? "~~" : ""
|
||||||
|
}${
|
||||||
|
GuildFeaturesFormatted[feature]?.name ??
|
||||||
|
feature
|
||||||
|
.split("_")
|
||||||
|
.map((x) => x[0] + x.substring(1).toLowerCase())
|
||||||
|
.join(" ")
|
||||||
|
}${GuildFeaturesFormatted[feature]?.deprecated ? "~~" : ""}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultAvatar(id, discriminator = null, size = 4096) {
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
if (discriminator && discriminator > 0) {
|
||||||
|
index = discriminator % 5;
|
||||||
|
} else if (id != null) {
|
||||||
|
index = (Number(id) << 22) % 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CDNEndpoints.DEFAULT_AVATAR(index, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flagsFromInt(int, flags, withRaw = true) {
|
||||||
|
const bits = int.toString(2);
|
||||||
|
const splitBits = bits.split("").reverse();
|
||||||
|
|
||||||
|
const reassignedBits = {};
|
||||||
|
|
||||||
|
for (const shift in splitBits) {
|
||||||
|
reassignedBits[shift] = splitBits[shift];
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignedFlags = Object.keys(reassignedBits).filter((bit) => reassignedBits[bit] == 1);
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
for (const flag of assignedFlags) {
|
||||||
|
out.push(
|
||||||
|
`${flags[flag] ?? "<Undocumented Flag>"}${
|
||||||
|
withRaw == true || flags[flag] == null ? ` (1 << ${flag}, ${1n << BigInt(flag)})` : ""
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
pastelize,
|
||||||
|
formatUsername,
|
||||||
|
getTopColor,
|
||||||
|
safeString,
|
||||||
|
hastebin,
|
||||||
|
getUploadLimit,
|
||||||
|
getGuild,
|
||||||
|
enumKeyToName,
|
||||||
|
formatGuildFeatures,
|
||||||
|
getDefaultAvatar,
|
||||||
|
flagsFromInt,
|
||||||
|
};
|
173
src/util/selection.js
Normal file
173
src/util/selection.js
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
const {Collection} = require("@projectdysnomia/dysnomia");
|
||||||
|
|
||||||
|
const logger = require("../lib/logger.js");
|
||||||
|
const {formatUsername} = require("./misc.js");
|
||||||
|
const {APIEndpoints} = require("./dconstants.js");
|
||||||
|
const {
|
||||||
|
RegExp: {Snowflake: REGEX_SNOWFLAKE},
|
||||||
|
} = require("./constants.js");
|
||||||
|
|
||||||
|
if (!hf.selectionMessages) hf.selectionMessages = new Collection();
|
||||||
|
async function selectionMessage(msg, heading, options, timeout = 30000, maxItems = 1) {
|
||||||
|
const data = {
|
||||||
|
content: heading,
|
||||||
|
allowedMentions: {
|
||||||
|
repliedUser: false,
|
||||||
|
},
|
||||||
|
messageReference: {
|
||||||
|
messageID: msg.id,
|
||||||
|
},
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 3,
|
||||||
|
custom_id: msg.id,
|
||||||
|
options: [],
|
||||||
|
max_values: maxItems,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 4,
|
||||||
|
label: "Cancel",
|
||||||
|
custom_id: "cancel",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
options.slice(0, 25).forEach((value) => {
|
||||||
|
data.components[0].components[0].options.push({
|
||||||
|
label: value.display,
|
||||||
|
value: value.key,
|
||||||
|
description: value.description,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (options.length > 25) {
|
||||||
|
data.content += `\n\nDisplaying 25/${options.length} results`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayMessage = await msg.channel
|
||||||
|
.createMessage(data)
|
||||||
|
.catch((err) => logger.error("selectionMessage", "Failed to create selection message: " + err));
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
function listener(interaction) {
|
||||||
|
const user = interaction.member.user || interaction.user;
|
||||||
|
|
||||||
|
if (
|
||||||
|
user.id == msg.author.id &&
|
||||||
|
interaction.channel.id == msg.channel.id &&
|
||||||
|
interaction.message.components[0].components[0].custom_id == msg.id
|
||||||
|
) {
|
||||||
|
if (interaction.data.custom_id == "cancel") {
|
||||||
|
hf.events.remove("interactionCreate", `selection.${msg.id}`);
|
||||||
|
clearTimeout(hf.selectionMessages.get(msg.id));
|
||||||
|
hf.selectionMessages.delete(msg.id);
|
||||||
|
|
||||||
|
interaction.deferUpdate();
|
||||||
|
|
||||||
|
displayMessage.delete();
|
||||||
|
|
||||||
|
reject("Canceled");
|
||||||
|
} else {
|
||||||
|
hf.events.remove("interactionCreate", `selection.${msg.id}`);
|
||||||
|
clearTimeout(hf.selectionMessages.get(msg.id));
|
||||||
|
hf.selectionMessages.delete(msg.id);
|
||||||
|
|
||||||
|
interaction.deferUpdate();
|
||||||
|
|
||||||
|
displayMessage.delete();
|
||||||
|
|
||||||
|
let result;
|
||||||
|
|
||||||
|
if (maxItems > 1) {
|
||||||
|
result = options.filter((opt) => interaction.data.values.includes(opt.key)).map((opt) => opt.key);
|
||||||
|
} else {
|
||||||
|
result = options.filter((opt) => opt.key == interaction.data.values[0])[0].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hf.events.add("interactionCreate", `selection.${msg.id}`, listener);
|
||||||
|
hf.selectionMessages.set(
|
||||||
|
msg.id,
|
||||||
|
setTimeout(() => {
|
||||||
|
hf.events.remove("interactionCreate", `selection.${msg.id}`);
|
||||||
|
hf.selectionMessages.delete(msg.id);
|
||||||
|
|
||||||
|
reject("Request timed out");
|
||||||
|
}, timeout)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function lookupUser(msg, str, filter) {
|
||||||
|
if (REGEX_SNOWFLAKE.test(str)) {
|
||||||
|
return await hf.bot.requestHandler.request("GET", APIEndpoints.USER(str.match(REGEX_SNOWFLAKE)[1]), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let users;
|
||||||
|
if (filter) {
|
||||||
|
users = hf.bot.users.filter(filter).values();
|
||||||
|
} else if (msg.guildID) {
|
||||||
|
const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID);
|
||||||
|
users = guild.members.values();
|
||||||
|
} else {
|
||||||
|
users = hf.bot.users.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/(.+?)#([0-9]{4})/.test(str)) {
|
||||||
|
const [_, name, discrim] = str.match(/(.+?)#([0-9]{4})/);
|
||||||
|
for (const user of users) {
|
||||||
|
if (user.username.toLowerCase() == name.toLowerCase() && user.discriminator == discrim) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = [];
|
||||||
|
for (const user of users) {
|
||||||
|
if (user.username.toLowerCase() == str.toLowerCase() || (user.nickname && user.nickname == str.toLowerCase())) {
|
||||||
|
return user;
|
||||||
|
} else if (
|
||||||
|
user.username.toLowerCase().indexOf(str.toLowerCase()) > -1 ||
|
||||||
|
(user.nick && user.nick.toLowerCase().indexOf(str.toLowerCase()) > -1) ||
|
||||||
|
(user.globalName && user.globalName.toLowerCase().indexOf(str.toLowerCase()) > -1)
|
||||||
|
) {
|
||||||
|
selection.push({
|
||||||
|
value: user,
|
||||||
|
key: user.id,
|
||||||
|
display: `${formatUsername(user)}${
|
||||||
|
user.nick ? ` (${user.nick})` : user.globalName ? ` (${user.globalName})` : ""
|
||||||
|
}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.sort((a, b) => a.display - b.display);
|
||||||
|
|
||||||
|
if (selection.length == 0) {
|
||||||
|
return "No results";
|
||||||
|
} else if (selection.length == 1) {
|
||||||
|
return selection[0].value;
|
||||||
|
} else {
|
||||||
|
selection.splice(20);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await selectionMessage(msg, "Multiple users found, please pick from this list:", selection);
|
||||||
|
} catch (out) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {selectionMessage, lookupUser};
|
45
src/util/starboard.js
Normal file
45
src/util/starboard.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
const {pastelize} = require("./misc.js");
|
||||||
|
const {findSuitableImage} = require("./image.js");
|
||||||
|
|
||||||
|
async function createBoardMessage(
|
||||||
|
msg,
|
||||||
|
count,
|
||||||
|
threadId = null,
|
||||||
|
fetchAttachment = true
|
||||||
|
) {
|
||||||
|
const name = msg.member?.nick ?? msg.author.globalName ?? msg.author.username;
|
||||||
|
const embed = {
|
||||||
|
title: `${count} \u2b50 - ${msg.jumpLink}`,
|
||||||
|
color: pastelize(name),
|
||||||
|
description: msg.content,
|
||||||
|
timestamp: new Date(msg.timestamp).toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let image;
|
||||||
|
if (fetchAttachment) {
|
||||||
|
image = await findSuitableImage(msg);
|
||||||
|
if (image.url) {
|
||||||
|
if (image.video) {
|
||||||
|
embed.description += `\n(contains video ${
|
||||||
|
image.attachment ? "attachment" : "embed"
|
||||||
|
})`;
|
||||||
|
}
|
||||||
|
embed.image = {
|
||||||
|
url: image.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
avatarURL: msg.member?.avatarURL ?? msg.author.avatarURL,
|
||||||
|
username: name,
|
||||||
|
threadID: threadId,
|
||||||
|
embeds: [embed],
|
||||||
|
attachments: image?.file
|
||||||
|
? [{file: image.file, filename: "thumb.jpg"}]
|
||||||
|
: null,
|
||||||
|
wait: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {createBoardMessage};
|
|
@ -1,4 +1,4 @@
|
||||||
module.exports = class {
|
module.exports = class Table {
|
||||||
constructor(titles) {
|
constructor(titles) {
|
||||||
this._rows = [titles];
|
this._rows = [titles];
|
||||||
this._widths = [];
|
this._widths = [];
|
44
src/util/time.js
Normal file
44
src/util/time.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
function formatTime(number) {
|
||||||
|
let seconds = parseInt(number) / 1000;
|
||||||
|
const days = Math.floor(seconds / 86400);
|
||||||
|
seconds = seconds % 86400;
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
seconds = seconds % 3600;
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
seconds = Math.floor(seconds % 60);
|
||||||
|
|
||||||
|
return (
|
||||||
|
(days !== 0 ? `${days.toString().padStart(2, "0")}:` : "") +
|
||||||
|
(hours !== 0 ? `${hours.toString().padStart(2, "0")}:` : "") +
|
||||||
|
`${minutes.toString().padStart(2, "0")}:${seconds
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readableTime(number) {
|
||||||
|
const seconds = number / 1000;
|
||||||
|
const days = seconds / 60 / 60 / 24;
|
||||||
|
const years = days / 365.25;
|
||||||
|
|
||||||
|
if (years >= 1) {
|
||||||
|
return `${years.toFixed(2)} years`;
|
||||||
|
} else {
|
||||||
|
return `${days.toFixed(2)} days`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TWITTER_EPOCH = 1288834974657;
|
||||||
|
const DISCORD_EPOCH = 1420070400000;
|
||||||
|
function snowflakeToTimestamp(snowflake, twitter = false) {
|
||||||
|
return (
|
||||||
|
Math.floor(Number(snowflake) / Math.pow(2, 22)) +
|
||||||
|
(twitter == true ? TWITTER_EPOCH : DISCORD_EPOCH)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
formatTime,
|
||||||
|
readableTime,
|
||||||
|
snowflakeToTimestamp,
|
||||||
|
};
|
|
@ -20,10 +20,10 @@ async function getNamesFromString(string) {
|
||||||
|
|
||||||
const codes = [...string].map((char) => char.codePointAt().toString(16));
|
const codes = [...string].map((char) => char.codePointAt().toString(16));
|
||||||
|
|
||||||
return codes.map((code) => [
|
return codes.map((code) => {
|
||||||
code.padStart(4, "0"),
|
const padded = code.padStart(4, "0");
|
||||||
global.____unicode_data[code.padStart(4, "0")],
|
return [padded, global.____unicode_data[padded]];
|
||||||
]);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
Loading…
Reference in a new issue