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,
|
||||
"bracketSpacing": false,
|
||||
"endOfLine": "lf"
|
||||
"semi": true,
|
||||
"bracketSpacing": false,
|
||||
"endOfLine": "lf",
|
||||
"printWidth": 120
|
||||
}
|
||||
|
|
97
src/index.js
97
src/index.js
|
@ -1,5 +1,4 @@
|
|||
const Dysnomia = require("@projectdysnomia/dysnomia");
|
||||
const logger = require("./lib/logger.js");
|
||||
const {Client, Collection, Channel, Permission} = require("@projectdysnomia/dysnomia");
|
||||
const fs = require("node:fs");
|
||||
const {resolve} = require("node:path");
|
||||
const sqlite3 = require("sqlite3");
|
||||
|
@ -8,22 +7,25 @@ const {instead, before} = require("spitroast");
|
|||
const config = require("../config.json");
|
||||
const apikeys = require("../apikeys.json");
|
||||
|
||||
const logger = require("./lib/logger.js");
|
||||
const events = require("./lib/events.js");
|
||||
const timer = require("./lib/timer.js");
|
||||
const Command = require("./lib/command.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",
|
||||
defaultImageSize: 1024,
|
||||
gateway: {
|
||||
intents: Object.values(Dysnomia.Constants.Intents),
|
||||
intents: Object.values(Intents),
|
||||
},
|
||||
restMode: true,
|
||||
});
|
||||
|
||||
const commands = new Dysnomia.Collection();
|
||||
const interactionCommands = new Dysnomia.Collection();
|
||||
const commands = new Collection();
|
||||
const interactionCommands = new Collection();
|
||||
|
||||
const database = new sqlite3.Database(resolve(__dirname, "..", "database.db"));
|
||||
|
||||
|
@ -33,9 +35,7 @@ function registerCommand(cmdObj) {
|
|||
const aliases = cmdObj.getAliases();
|
||||
logger.info(
|
||||
"hf:cmd",
|
||||
`Registered command '${cmdObj.name}'${
|
||||
aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : ""
|
||||
}`
|
||||
`Registered command '${cmdObj.name}'${aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : ""}`
|
||||
);
|
||||
} else if (cmdObj instanceof InteractionCommand) {
|
||||
interactionCommands.set(cmdObj.name, cmdObj);
|
||||
|
@ -55,57 +55,51 @@ global.hf = {
|
|||
database,
|
||||
};
|
||||
|
||||
const {formatUsername} = require("./lib/utils.js");
|
||||
const {formatUsername} = require("./util/misc.js");
|
||||
const CommandDispatcher = require("./lib/commandDispatcher.js");
|
||||
const {InteractionDispatcher} = require("./lib/interactionDispatcher.js");
|
||||
const {hasFlag} = require("./lib/guildData.js");
|
||||
|
||||
for (const file of fs.readdirSync(resolve(__dirname, "modules"))) {
|
||||
require(resolve(__dirname, "modules", file));
|
||||
logger.info("hf:modules", `Loaded module: '${file}'`);
|
||||
try {
|
||||
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) => {
|
||||
try {
|
||||
// fix DMs cause of gateway v8 changes
|
||||
if (
|
||||
!(msg.channel instanceof Dysnomia.Channel) &&
|
||||
msg.author.id != bot.user.id &&
|
||||
!msg.guildID
|
||||
) {
|
||||
if (!(msg.channel instanceof Channel) && msg.author.id != bot.user.id && !msg.guildID) {
|
||||
const newChannel = await bot.getDMChannel(msg.author.id);
|
||||
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);
|
||||
} catch (err) {
|
||||
const stack = (err?.stack ?? err.message).split("\n");
|
||||
const error = stack.shift();
|
||||
logger.error(
|
||||
"hf:main",
|
||||
`Failed to dispatch command: ${error}\n\t${stack.join("\n\t")}`
|
||||
);
|
||||
logger.error("hf:main", `Failed to dispatch command: ${error}\n\t${stack.join("\n\t")}`);
|
||||
}
|
||||
});
|
||||
bot.on("messageUpdate", async (msg, oldMsg) => {
|
||||
try {
|
||||
const oneDay = Date.now() - 86400000;
|
||||
if (
|
||||
msg.timestamp > oneDay &&
|
||||
!msg.hasRan &&
|
||||
oldMsg &&
|
||||
oldMsg.content !== msg.content
|
||||
) {
|
||||
if (msg.timestamp > oneDay && !msg.hasRan && oldMsg && oldMsg.content !== msg.content) {
|
||||
await CommandDispatcher(msg);
|
||||
}
|
||||
} catch (err) {
|
||||
const stack = (err?.stack ?? err.message).split("\n");
|
||||
const error = stack.shift();
|
||||
logger.error(
|
||||
"hf:main",
|
||||
`Failed to dispatch command update: ${error}\n\t${stack.join("\n\t")}`
|
||||
);
|
||||
logger.error("hf:main", `Failed to dispatch command update: ${error}\n\t${stack.join("\n\t")}`);
|
||||
}
|
||||
});
|
||||
bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
||||
|
@ -114,7 +108,7 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
|||
|
||||
try {
|
||||
let channel = msg.channel;
|
||||
if (!(channel instanceof Dysnomia.Channel)) {
|
||||
if (!(channel instanceof Channel)) {
|
||||
const newChannel = hf.bot.getChannel(channel.id);
|
||||
if (newChannel) {
|
||||
channel = newChannel;
|
||||
|
@ -137,10 +131,7 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
|
|||
} catch (err) {
|
||||
const stack = (err?.stack ?? err.message).split("\n");
|
||||
const error = stack.shift();
|
||||
logger.error(
|
||||
"hf:main",
|
||||
`Failed to self-delete message: ${error}\n\t${stack.join("\n\t")}`
|
||||
);
|
||||
logger.error("hf:main", `Failed to self-delete message: ${error}\n\t${stack.join("\n\t")}`);
|
||||
}
|
||||
});
|
||||
bot.on("interactionCreate", async (interaction) => {
|
||||
|
@ -149,21 +140,13 @@ bot.on("interactionCreate", async (interaction) => {
|
|||
} catch (err) {
|
||||
const stack = (err?.stack ?? err.message).split("\n");
|
||||
const error = stack.shift();
|
||||
logger.error(
|
||||
"hf:main",
|
||||
`Failed to dispatch interaction command: ${error}\n\t${stack.join(
|
||||
"\n\t"
|
||||
)}`
|
||||
);
|
||||
logger.error("hf:main", `Failed to dispatch interaction command: ${error}\n\t${stack.join("\n\t")}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.once("ready", async () => {
|
||||
logger.info("hf:main", "Connected to Discord.");
|
||||
logger.info(
|
||||
"hf:main",
|
||||
`Logged in as: ${formatUsername(bot.user)} (${bot.user.id})`
|
||||
);
|
||||
logger.info("hf:main", `Logged in as: ${formatUsername(bot.user)} (${bot.user.id})`);
|
||||
|
||||
const channel = await bot.getDMChannel(config.owner_id);
|
||||
if (channel) {
|
||||
|
@ -202,25 +185,18 @@ bot.once("ready", async () => {
|
|||
formattedCommand.contexts.push(1, 2);
|
||||
}
|
||||
|
||||
if (command.type === Dysnomia.Constants.ApplicationCommandTypes.CHAT_INPUT)
|
||||
if (command.type === ApplicationCommandTypes.CHAT_INPUT)
|
||||
formattedCommand.name = formattedCommand.name.toLowerCase();
|
||||
|
||||
if (command.permissions !== undefined) {
|
||||
formattedCommand.default_member_permissions =
|
||||
command.permissions instanceof Dysnomia.Permission
|
||||
? String(command.permissions.allow)
|
||||
: String(command.permissions);
|
||||
command.permissions instanceof Permission ? String(command.permissions.allow) : String(command.permissions);
|
||||
}
|
||||
|
||||
commands.push(formattedCommand);
|
||||
}
|
||||
|
||||
await bot.requestHandler.request(
|
||||
"PUT",
|
||||
`/applications/${bot.application.id}/commands`,
|
||||
true,
|
||||
commands
|
||||
);
|
||||
await bot.requestHandler.request("PUT", APIEndpoints.COMMANDS(bot.application.id), true, commands);
|
||||
});
|
||||
|
||||
bot.on("error", (err) => {
|
||||
|
@ -243,10 +219,7 @@ bot.on("shardReady", (id) => {
|
|||
logger.verbose("hf:shard", `Shard ${id} ready`);
|
||||
});
|
||||
bot.on("unknown", (packet, id) => {
|
||||
logger.verbose(
|
||||
"hf:main",
|
||||
`Shard ${id} caught unknown packet: ${JSON.stringify(packet)}`
|
||||
);
|
||||
logger.verbose("hf:main", `Shard ${id} caught unknown packet: ${JSON.stringify(packet)}`);
|
||||
});
|
||||
|
||||
instead("spawn", bot.shards, function (args, orig) {
|
||||
|
@ -254,7 +227,7 @@ instead("spawn", bot.shards, function (args, orig) {
|
|||
const shard = this.get(args[0]);
|
||||
if (shard) {
|
||||
before("sendWS", shard.__proto__, function ([op, _data]) {
|
||||
if (op === Dysnomia.Constants.GatewayOPCodes.IDENTIFY) {
|
||||
if (op === GatewayOPCodes.IDENTIFY) {
|
||||
_data.properties.browser = "Discord Embedded";
|
||||
delete _data.properties.device;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const logger = require("./logger.js");
|
||||
const {pastelize, getTopColor} = require("./utils.js");
|
||||
const {pastelize, getTopColor} = require("../util/misc.js");
|
||||
|
||||
function convertIfApplicable(val) {
|
||||
if (isNaN(val)) {
|
||||
|
@ -27,10 +27,7 @@ function parseAsArgv(args) {
|
|||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args.shift();
|
||||
const equalsIndex = arg.charAt(0) === "-" ? arg.indexOf("=") : -1;
|
||||
const argName =
|
||||
equalsIndex === -1
|
||||
? removeStartHyphens(arg)
|
||||
: removeStartHyphens(arg.slice(0, equalsIndex));
|
||||
const argName = equalsIndex === -1 ? removeStartHyphens(arg) : removeStartHyphens(arg.slice(0, equalsIndex));
|
||||
|
||||
if (equalsIndex !== -1) {
|
||||
optional[argName] = convertIfApplicable(arg.slice(equalsIndex + 1));
|
||||
|
@ -176,9 +173,7 @@ async function CommandDispatcher(msg) {
|
|||
}
|
||||
if (response.embeds) {
|
||||
for (const embed of response.embeds) {
|
||||
embed.color =
|
||||
embed.color ||
|
||||
getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id));
|
||||
embed.color = embed.color || getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id));
|
||||
}
|
||||
}
|
||||
if (response.reaction) {
|
||||
|
@ -186,17 +181,14 @@ async function CommandDispatcher(msg) {
|
|||
} else {
|
||||
try {
|
||||
const outMessage = await msg.channel.createMessage(
|
||||
Object.assign(
|
||||
typeof response === "string" ? {content: response} : response,
|
||||
{
|
||||
allowedMentions: {
|
||||
repliedUser: false,
|
||||
},
|
||||
messageReference: {
|
||||
messageID: msg.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
Object.assign(typeof response === "string" ? {content: response} : response, {
|
||||
allowedMentions: {
|
||||
repliedUser: false,
|
||||
},
|
||||
messageReference: {
|
||||
messageID: msg.id,
|
||||
},
|
||||
})
|
||||
);
|
||||
if (response.addReactions) {
|
||||
for (const index in response.addReactions) {
|
||||
|
|
|
@ -17,10 +17,7 @@ function add(event, identifier, callback) {
|
|||
try {
|
||||
callback.apply(this, arguments);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"hf:event",
|
||||
`Event "${event}:${identifier}" failed: ${error}`
|
||||
);
|
||||
logger.error("hf:event", `Event "${event}:${identifier}" failed: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
hf.database.run(
|
||||
"CREATE TABLE IF NOT EXISTS guild_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID"
|
||||
);
|
||||
hf.database.run("CREATE TABLE IF NOT EXISTS guild_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID");
|
||||
|
||||
function setGuildData(id, key, value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
@ -2,7 +2,7 @@ const {getGuildData, setGuildData} = require("./guildData.js");
|
|||
|
||||
const flags = Object.freeze({
|
||||
codePreviews: 1 << 0,
|
||||
tweetUnrolling: 1 << 1,
|
||||
replyToBots: 1 << 1,
|
||||
fedimbed: 1 << 2,
|
||||
});
|
||||
|
||||
|
@ -18,9 +18,7 @@ async function getFlags(guildId) {
|
|||
}
|
||||
|
||||
async function hasFlag(guildId, key) {
|
||||
return (
|
||||
((await getGuildData(guildId, "settings_flags", 0)) & flags[key]) !== 0
|
||||
);
|
||||
return ((await getGuildData(guildId, "settings_flags", 0)) & flags[key]) !== 0;
|
||||
}
|
||||
|
||||
async function enableFlag(guildId, key) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const {ApplicationCommandTypes, ApplicationCommandOptionTypes} =
|
||||
require("@projectdysnomia/dysnomia").Constants;
|
||||
const {ApplicationCommandTypes, ApplicationCommandOptionTypes} = require("../util/dconstants.js");
|
||||
|
||||
class InteractionCommand {
|
||||
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() {
|
||||
return "Callback not overwritten.";
|
||||
}
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
const logger = require("./logger.js");
|
||||
const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants;
|
||||
const {pastelize, getTopColor} = require("./utils.js");
|
||||
|
||||
function getOption(interaction, command, key) {
|
||||
return (
|
||||
interaction.data.options?.find((o) => o.name === key)?.value ??
|
||||
command.options[key].default
|
||||
);
|
||||
}
|
||||
const {MessageFlags} = require("../util/dconstants.js");
|
||||
const {pastelize, getTopColor} = require("../util/misc.js");
|
||||
|
||||
async function runCommand(interaction, command) {
|
||||
if (command.ownerOnly && interaction.user.id != hf.config.owner_id) {
|
||||
|
@ -17,10 +10,7 @@ async function runCommand(interaction, command) {
|
|||
};
|
||||
}
|
||||
|
||||
if (
|
||||
command.elevatedOnly &&
|
||||
!hf.config.elevated.includes(interaction.user.id)
|
||||
) {
|
||||
if (command.elevatedOnly && !hf.config.elevated.includes(interaction.user.id)) {
|
||||
return {
|
||||
content: "No\n\nSent from my iPhone",
|
||||
flags: MessageFlags.EPHEMERAL,
|
||||
|
@ -45,7 +35,7 @@ async function runCommand(interaction, command) {
|
|||
|
||||
async function InteractionDispatcher(interaction) {
|
||||
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);
|
||||
|
||||
try {
|
||||
|
@ -86,24 +76,19 @@ async function InteractionDispatcher(interaction) {
|
|||
}
|
||||
if (response.embeds) {
|
||||
for (const embed of response.embeds) {
|
||||
embed.color =
|
||||
embed.color ??
|
||||
getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id));
|
||||
embed.color = embed.color ?? getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await ack;
|
||||
await interaction.createMessage(
|
||||
Object.assign(
|
||||
typeof response === "string" ? {content: response} : response,
|
||||
{
|
||||
flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL,
|
||||
allowedMentions: {
|
||||
repliedUser: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
Object.assign(typeof response === "string" ? {content: response} : response, {
|
||||
flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL,
|
||||
allowedMentions: {
|
||||
repliedUser: false,
|
||||
},
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
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 BRIGHT_BG = "[10";
|
||||
|
||||
const NAMES = [
|
||||
"Black",
|
||||
"Red",
|
||||
"Green",
|
||||
"Yellow",
|
||||
"Blue",
|
||||
"Magenta",
|
||||
"Cyan",
|
||||
"White",
|
||||
];
|
||||
const NAMES = ["Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"];
|
||||
|
||||
const COLORS = {};
|
||||
for (const index in NAMES) {
|
||||
|
@ -73,6 +64,5 @@ function baseLogger(prefix, level, ...message) {
|
|||
|
||||
module.exports = {};
|
||||
for (const level in LEVEL_COLORS) {
|
||||
module.exports[level] = (prefix, ...message) =>
|
||||
baseLogger.apply(null, [prefix, level, ...message]);
|
||||
module.exports[level] = (prefix, ...message) => baseLogger.apply(null, [prefix, level, ...message]);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
hf.database.run(
|
||||
"CREATE TABLE IF NOT EXISTS user_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID"
|
||||
);
|
||||
hf.database.run("CREATE TABLE IF NOT EXISTS user_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID");
|
||||
|
||||
function setUserData(id, key, value) {
|
||||
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() {
|
||||
let playing;
|
||||
try {
|
||||
playing = await fetch("https://anonradio.net/playing").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
playing = await fetch("https://anonradio.net/playing").then((res) => res.text());
|
||||
} catch (err) {
|
||||
try {
|
||||
playing = await fetch("http://anonradio.net/playing").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
playing = await fetch("http://anonradio.net/playing").then((res) => res.text());
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
}
|
||||
let schedule;
|
||||
try {
|
||||
schedule = await fetch("https://anonradio.net/schedule/").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
schedule = await fetch("https://anonradio.net/schedule/").then((res) => res.text());
|
||||
} catch (err) {
|
||||
try {
|
||||
schedule = await fetch("http://anonradio.net/schedule/").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
schedule = await fetch("http://anonradio.net/schedule/").then((res) => res.text());
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
|
@ -68,19 +60,14 @@ async function updateNowPlaying() {
|
|||
} else if (playing.startsWith("Coming up")) {
|
||||
title = playing;
|
||||
} else {
|
||||
const metadataLine = playing.match(
|
||||
/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/
|
||||
);
|
||||
const metadataLine = playing.match(/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/);
|
||||
const current = metadataLine?.[1] ?? "??";
|
||||
const peakDay = metadataLine?.[2] ?? "??";
|
||||
const peakMonth = metadataLine?.[3] ?? "??";
|
||||
const dj = metadataLine?.[4] ?? "unknown";
|
||||
const metadata = metadataLine?.[5] ?? "unknown";
|
||||
|
||||
if (
|
||||
metadata == "https://archives.anonradio.net" ||
|
||||
liveNow.name == "Synth Battle Royale"
|
||||
) {
|
||||
if (metadata == "https://archives.anonradio.net" || liveNow.name == "Synth Battle Royale") {
|
||||
title = `${liveNow.name} (\`${dj}\`)`;
|
||||
} else {
|
||||
title = `${metadata} (\`${dj}\`)`;
|
||||
|
@ -93,24 +80,16 @@ async function updateNowPlaying() {
|
|||
try {
|
||||
const icecast = await fetch("http://anonradio.net:8010/status-json.xsl")
|
||||
.then((res) => res.text())
|
||||
.then((data) =>
|
||||
JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",'))
|
||||
);
|
||||
const streamData = icecast.icestats.source.find(
|
||||
(src) => src.listenurl == "http://anonradio.net:8010/openmic"
|
||||
);
|
||||
.then((data) => JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",')));
|
||||
const streamData = icecast.icestats.source.find((src) => src.listenurl == "http://anonradio.net:8010/openmic");
|
||||
if (streamData && streamData.stream_start_iso8601) {
|
||||
const startTime = new Date(streamData.stream_start_iso8601).getTime();
|
||||
title = `${streamData.title} (\`${
|
||||
streamData.server_name
|
||||
? streamData.server_name + " | " + liveNow.id
|
||||
: liveNow.id
|
||||
streamData.server_name ? streamData.server_name + " | " + liveNow.id : liveNow.id
|
||||
}\`)`;
|
||||
openmicTime = `-\\*- OpenMIC DJ has been streaming since <t:${Math.round(
|
||||
startTime / 1000
|
||||
)}:R> -\\*-\n`;
|
||||
openmicTime = `-\\*- OpenMIC DJ has been streaming since <t:${Math.round(startTime / 1000)}:R> -\\*-\n`;
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +108,7 @@ async function updateNowPlaying() {
|
|||
content: timestamp + "\n" + content,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,10 @@ const {resolve} = require("path");
|
|||
const guildSettings = require("../lib/guildSettings.js");
|
||||
|
||||
function spawn(args) {
|
||||
const shell =
|
||||
process.env.SHELL || (process.platform == "win32" ? "powershell" : "sh");
|
||||
const shell = process.env.SHELL || (process.platform == "win32" ? "pwsh" : "sh");
|
||||
|
||||
const newArgs = [];
|
||||
if (shell.match(/powershell/i) && process.platform == "win32") {
|
||||
if (shell.match(/pwsh/i) && process.platform == "win32") {
|
||||
newArgs.push("-NoLogo", "-Command");
|
||||
} else {
|
||||
newArgs.push("-c");
|
||||
|
@ -52,16 +51,18 @@ reload.callback = function (msg, line) {
|
|||
if (err.code == "MODULE_NOT_FOUND") {
|
||||
return "Module not found.";
|
||||
} else {
|
||||
logger.info("hf:modules", `Failed to resolve "${line}": ${err}`);
|
||||
return `:warning: An error occurred: \`\`\`\n${err}\`\`\``;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info("hf:modules", `Reloading module: '${line}'`);
|
||||
logger.info("hf:modules", `Reloading module: "${line}"`);
|
||||
delete require.cache[require.resolve(`./${line}.js`)];
|
||||
require(`./${line}.js`);
|
||||
return {reaction: "\uD83D\uDC4C"};
|
||||
} catch (err) {
|
||||
logger.info("hf:modules", `Failed to reload "${line}": ${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});
|
||||
|
||||
const token = hf.config.token;
|
||||
out = out.replace(
|
||||
new RegExp(token.replace(/\./g, "\\."), "g"),
|
||||
"lol no key 4 u"
|
||||
);
|
||||
out = out.replace(new RegExp(token.replace(/\./g, "\\."), "g"), "lol no key 4 u");
|
||||
|
||||
if (errored) {
|
||||
return ":warning: Output (errored):\n```js\n" + out + "\n```";
|
||||
|
@ -136,9 +134,7 @@ exec.callback = async function (msg, line) {
|
|||
});
|
||||
|
||||
proc.on("close", async (code) => {
|
||||
out += `\n\x1b[0m\x1b[1m====\x1b[0m\n\x1b[1m${
|
||||
code != 0 ? "\x1b[31m" : ""
|
||||
}Exited with ${code}\x1b[0m`;
|
||||
out += `\n\x1b[0m\x1b[1m====\x1b[0m\n\x1b[1m${code != 0 ? "\x1b[31m" : ""}Exited with ${code}\x1b[0m`;
|
||||
if (out.length > 1980) {
|
||||
msg.channel.createMessage({
|
||||
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\`\`\``;
|
||||
}
|
||||
case "enable": {
|
||||
if (
|
||||
!msg.channel.permissionsOf(msg.author.id).has("manageGuild") &&
|
||||
!hf.config.elevated.includes(msg.author.id)
|
||||
)
|
||||
if (!msg.channel.permissionsOf(msg.author.id).has("manageGuild") && !hf.config.elevated.includes(msg.author.id))
|
||||
return "You do not have `Manage Server` permissions";
|
||||
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"};
|
||||
}
|
||||
case "disable": {
|
||||
if (
|
||||
!msg.channel.permissionsOf(msg.author.id).has("manageGuild") &&
|
||||
!hf.config.elevated.includes(msg.author.id)
|
||||
)
|
||||
if (!msg.channel.permissionsOf(msg.author.id).has("manageGuild") && !hf.config.elevated.includes(msg.author.id))
|
||||
return "You do not have `Manage Server` permissions";
|
||||
if (msg.author.bot) return "Zero-width space your say command.";
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
const {ApplicationCommandOptionTypes, MessageFlags} =
|
||||
require("@projectdysnomia/dysnomia").Constants;
|
||||
const {ApplicationCommandOptionTypes, MessageFlags} = require("../util/dconstants.js");
|
||||
const InteractionCommand = require("../lib/interactionCommand.js");
|
||||
const {getOption} = require("../lib/interactionDispatcher.js");
|
||||
|
||||
const events = require("../lib/events.js");
|
||||
const {hasFlag} = require("../lib/guildSettings.js");
|
||||
|
@ -15,10 +13,7 @@ const REGEX_SPOILER = /(?:\s|^)\|\|([\s\S]+?)\|\|/;
|
|||
|
||||
function unindent(str) {
|
||||
str = str.replace(/\t/g, " ");
|
||||
const minIndent =
|
||||
str
|
||||
.match(/^ *(?=\S)/gm)
|
||||
?.reduce((prev, curr) => Math.min(prev, curr.length), Infinity) ?? 0;
|
||||
const minIndent = str.match(/^ *(?=\S)/gm)?.reduce((prev, curr) => Math.min(prev, curr.length), Infinity) ?? 0;
|
||||
if (!minIndent) return str;
|
||||
return str.replace(new RegExp(`^ {${minIndent}}`, "gm"), "");
|
||||
}
|
||||
|
@ -44,12 +39,7 @@ const fileTypeAliases = {
|
|||
"code-snippets": "json",
|
||||
};
|
||||
|
||||
async function processFile(
|
||||
link,
|
||||
originalLink,
|
||||
spoiler = false,
|
||||
linkFile = false
|
||||
) {
|
||||
async function processFile(link, originalLink, spoiler = false, linkFile = false) {
|
||||
link = link.replaceAll("||", "").trim();
|
||||
const res = await fetch(link);
|
||||
if (!res.ok) return "";
|
||||
|
@ -63,10 +53,7 @@ async function processFile(
|
|||
urlObj.pathname.lastIndexOf("/") + 1,
|
||||
urlObj.pathname.length
|
||||
);
|
||||
const fileType =
|
||||
fileName.lastIndexOf(".") == -1
|
||||
? ""
|
||||
: fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
const fileType = fileName.lastIndexOf(".") == -1 ? "" : fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
|
||||
if (linkFile) {
|
||||
fileName = `[${fileName}](<${originalLink}>)`;
|
||||
|
@ -101,9 +88,7 @@ async function processFile(
|
|||
if (lines.length > 20) return "";
|
||||
}
|
||||
|
||||
let targetLines = (
|
||||
entireFile ? lines : lines.slice(startLine - 1, endLine)
|
||||
).join("\n");
|
||||
let targetLines = (entireFile ? lines : lines.slice(startLine - 1, endLine)).join("\n");
|
||||
|
||||
let warning = "";
|
||||
if (spoiler && targetLines.includes("||")) {
|
||||
|
@ -115,11 +100,9 @@ async function processFile(
|
|||
warning = " - :warning: Zero width spaces present";
|
||||
}
|
||||
|
||||
return `**${fileName}:** ${whichLines}${warning}\n${
|
||||
spoiler ? "||" : ""
|
||||
}\`\`\`${fileTypeAliases[fileType] ?? fileType}\n${unindent(
|
||||
targetLines
|
||||
)}\n\`\`\`${spoiler ? "||" : ""}`;
|
||||
return `**${fileName}:** ${whichLines}${warning}\n${spoiler ? "||" : ""}\`\`\`${
|
||||
fileTypeAliases[fileType] ?? fileType
|
||||
}\n${unindent(targetLines)}\n\`\`\`${spoiler ? "||" : ""}`;
|
||||
}
|
||||
|
||||
events.add("messageCreate", "codePreviews", async function (msg) {
|
||||
|
@ -136,27 +119,21 @@ events.add("messageCreate", "codePreviews", async function (msg) {
|
|||
if (githubLinks?.length) {
|
||||
for (const link of githubLinks) {
|
||||
const spoiler = REGEX_SPOILER.test(link);
|
||||
files.push(
|
||||
await processFile(link.replace("/blob/", "/raw/"), link, spoiler)
|
||||
);
|
||||
files.push(await processFile(link.replace("/blob/", "/raw/"), link, spoiler));
|
||||
}
|
||||
}
|
||||
|
||||
if (gitlabLinks?.length) {
|
||||
for (const link of gitlabLinks) {
|
||||
const spoiler = REGEX_SPOILER.test(link);
|
||||
files.push(
|
||||
await processFile(link.replace("/blob/", "/raw/"), link, spoiler)
|
||||
);
|
||||
files.push(await processFile(link.replace("/blob/", "/raw/"), link, spoiler));
|
||||
}
|
||||
}
|
||||
|
||||
if (giteaLinks?.length) {
|
||||
for (const link of giteaLinks) {
|
||||
const spoiler = REGEX_SPOILER.test(link);
|
||||
files.push(
|
||||
await processFile(link.replace("/src/", "/raw/"), link, spoiler)
|
||||
);
|
||||
files.push(await processFile(link.replace("/src/", "/raw/"), link, spoiler));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,8 +178,7 @@ events.add("messageCreate", "codePreviews", async function (msg) {
|
|||
});
|
||||
|
||||
const codepreviewsCommand = new InteractionCommand("codepreview");
|
||||
codepreviewsCommand.helpText =
|
||||
"Post snippets of codes from files on GitHub, Gitlab and Gitea instances.";
|
||||
codepreviewsCommand.helpText = "Post snippets of codes from files on GitHub, Gitlab and Gitea instances.";
|
||||
codepreviewsCommand.options.url = {
|
||||
name: "url",
|
||||
type: ApplicationCommandOptionTypes.STRING,
|
||||
|
@ -218,8 +194,8 @@ codepreviewsCommand.options.spoiler = {
|
|||
default: false,
|
||||
};
|
||||
codepreviewsCommand.callback = async function (interaction) {
|
||||
const url = getOption(interaction, codepreviewsCommand, "url");
|
||||
const spoiler = getOption(interaction, codepreviewsCommand, "spoiler");
|
||||
const url = this.getOption(interaction, "url");
|
||||
const spoiler = this.getOption(interaction, "spoiler");
|
||||
|
||||
const githubOrGitlab = url.match(REGEX_GITHUB) ?? url.match(REGEX_GITLAB);
|
||||
const gitea = url.match(REGEX_GITEA);
|
||||
|
@ -238,8 +214,7 @@ codepreviewsCommand.callback = async function (interaction) {
|
|||
|
||||
if (out == "") {
|
||||
return {
|
||||
content:
|
||||
"No content was returned. Provided file is either too long, a markdown file, or not plaintext.",
|
||||
content: "No content was returned. Provided file is either too long, a markdown file, or not plaintext.",
|
||||
flags: MessageFlags.EPHEMERAL,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const Dysnomia = require("@projectdysnomia/dysnomia");
|
||||
const {MessageFlags, ApplicationCommandOptionTypes, Permissions} =
|
||||
Dysnomia.Constants;
|
||||
const {Message} = require("@projectdysnomia/dysnomia");
|
||||
const {MessageFlags, ApplicationCommandOptionTypes, Permissions} = require("../util/dconstants.js");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const httpSignature = require("@peertube/http-signature");
|
||||
|
@ -8,22 +7,20 @@ const httpSignature = require("@peertube/http-signature");
|
|||
const events = require("../lib/events.js");
|
||||
const logger = require("../lib/logger.js");
|
||||
const {hasFlag} = require("../lib/guildSettings.js");
|
||||
const {parseHtmlEntities, getUploadLimit} = require("../lib/utils.js");
|
||||
const InteractionCommand = require("../lib/interactionCommand.js");
|
||||
const {getOption} = require("../lib/interactionDispatcher.js");
|
||||
|
||||
const FRIENDLY_USERAGENT =
|
||||
"HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)";
|
||||
const {getUploadLimit} = require("../util/misc.js");
|
||||
const {htmlToMarkdown} = require("../util/html.js");
|
||||
|
||||
const URLS_REGEX =
|
||||
/(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g;
|
||||
const FRIENDLY_USERAGENT = "HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)";
|
||||
|
||||
const URLS_REGEX = /(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g;
|
||||
const SPOILER_REGEX = /(?:\s|^)\|\|([\s\S]+?)\|\|/;
|
||||
|
||||
const PATH_REGEX = {
|
||||
mastodon: /^\/@(.+?)\/(\d+)\/?/,
|
||||
mastodon2: /^\/(.+?)\/statuses\/\d+\/?/,
|
||||
pleroma:
|
||||
/^\/objects\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?/,
|
||||
pleroma: /^\/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]+\/?/,
|
||||
misskey: /^\/notes\/[a-z0-9]+\/?/,
|
||||
gotosocial: /^\/@(.+?)\/statuses\/[0-9A-Z]+\/?/,
|
||||
|
@ -76,10 +73,7 @@ async function resolvePlatform(url) {
|
|||
}).then((res) => res.json());
|
||||
|
||||
if (!nodeinfo?.software?.name) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Got nodeinfo for "${urlObj.hostname}", but missing software name.`
|
||||
);
|
||||
logger.error("fedimbed", `Got nodeinfo for "${urlObj.hostname}", but missing software name.`);
|
||||
domainCache.set(urlObj.hostname, null);
|
||||
return null;
|
||||
}
|
||||
|
@ -89,9 +83,7 @@ async function resolvePlatform(url) {
|
|||
}
|
||||
|
||||
const keyId = "https://hf.c7.pm/actor#main-key";
|
||||
const privKey = fs.readFileSync(
|
||||
path.resolve(__dirname, "../../priv/private.pem")
|
||||
);
|
||||
const privKey = fs.readFileSync(path.resolve(__dirname, "../../priv/private.pem"));
|
||||
async function signedFetch(url, options) {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
|
@ -122,36 +114,6 @@ async function signedFetch(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) {
|
||||
let invalidUrl = false;
|
||||
let urlObj;
|
||||
|
@ -200,10 +162,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
},
|
||||
}).then((res) => res.text());
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Failed to signed fetch "${url}", retrying unsigned: ${err}`
|
||||
);
|
||||
logger.error("fedimbed", `Failed to signed fetch "${url}", retrying unsigned: ${err}`);
|
||||
}
|
||||
if (!rawPostData) {
|
||||
try {
|
||||
|
@ -223,10 +182,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
try {
|
||||
postData = JSON.parse(rawPostData);
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"`
|
||||
);
|
||||
logger.error("fedimbed", `Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"`);
|
||||
}
|
||||
} else {
|
||||
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});
|
||||
headers["Content-Type"] = "application/json";
|
||||
} else {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Missing MastoAPI replacement for "${platform}"`
|
||||
);
|
||||
logger.error("fedimbed", `Missing MastoAPI replacement for "${platform}"`);
|
||||
}
|
||||
|
||||
if (redirUrl) {
|
||||
logger.verbose(
|
||||
"fedimbed",
|
||||
`Redirecting "${url}" to "${redirUrl}": ${JSON.stringify(
|
||||
options
|
||||
)}, ${JSON.stringify(headers)}`
|
||||
`Redirecting "${url}" to "${redirUrl}": ${JSON.stringify(options)}, ${JSON.stringify(headers)}`
|
||||
);
|
||||
let rawPostData2;
|
||||
try {
|
||||
|
@ -304,10 +255,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
})
|
||||
).then((res) => res.text());
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}`
|
||||
);
|
||||
logger.error("fedimbed", `Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}`);
|
||||
}
|
||||
if (!rawPostData2) {
|
||||
try {
|
||||
|
@ -320,10 +268,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
})
|
||||
).then((res) => res.text());
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Failed to fetch "${url}" via MastoAPI: ${err}`
|
||||
);
|
||||
logger.error("fedimbed", `Failed to fetch "${url}" via MastoAPI: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,27 +276,18 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
if (rawPostData2?.startsWith("{")) {
|
||||
postData2 = JSON.parse(rawPostData2);
|
||||
} else {
|
||||
logger.warn(
|
||||
"fedimbed",
|
||||
`Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}`
|
||||
);
|
||||
logger.warn("fedimbed", `Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}`);
|
||||
}
|
||||
|
||||
if (!postData2) {
|
||||
logger.warn(
|
||||
"fedimbed",
|
||||
`Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.`
|
||||
);
|
||||
logger.warn("fedimbed", `Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.`);
|
||||
} else if (postData2.error) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify(
|
||||
postData2.error
|
||||
)}`
|
||||
`Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify(postData2.error)}`
|
||||
);
|
||||
} else {
|
||||
cw =
|
||||
postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw;
|
||||
cw = postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw;
|
||||
content =
|
||||
postData2.akkoma?.source?.content ??
|
||||
postData2.pleroma?.content?.["text/plain"] ??
|
||||
|
@ -364,21 +300,12 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
postData2.user?.name ??
|
||||
postData2.user?.username,
|
||||
handle:
|
||||
postData2.account?.fqn ??
|
||||
`${postData2.account?.username ?? postData2.user?.username}@${
|
||||
urlObj.hostname
|
||||
}`,
|
||||
url:
|
||||
postData2.account?.url ??
|
||||
`${urlObj.origin}/@${
|
||||
postData2.account?.username ?? postData2.user?.username
|
||||
}`,
|
||||
postData2.account?.fqn ?? `${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,
|
||||
};
|
||||
timestamp = postData2.created_at ?? postData2.createdAt;
|
||||
emotes = postData2.emojis
|
||||
.filter((x) => !x.name.endsWith("@."))
|
||||
.map((x) => ({name: `:${x.name}:`, url: x.url}));
|
||||
emotes = postData2.emojis.filter((x) => !x.name.endsWith("@.")).map((x) => ({name: `:${x.name}:`, url: x.url}));
|
||||
sensitive = postData2.sensitive;
|
||||
|
||||
const attachments = postData2.media_attachments ?? postData2.files;
|
||||
|
@ -465,15 +392,12 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
if (realUrlObj.origin != urlObj.origin) {
|
||||
platform = await resolvePlatform(postData.id);
|
||||
color = PLATFORM_COLORS[platform];
|
||||
platformName = platform
|
||||
.replace("gotosocial", "GoToSocial")
|
||||
.replace(/^(.)/, (_, c) => c.toUpperCase());
|
||||
platformName = platform.replace("gotosocial", "GoToSocial").replace(/^(.)/, (_, c) => c.toUpperCase());
|
||||
url = postData.id;
|
||||
}
|
||||
}
|
||||
|
||||
content =
|
||||
postData._misskey_content ?? postData.source?.content ?? postData.content;
|
||||
content = postData._misskey_content ?? postData.source?.content ?? postData.content;
|
||||
cw = postData.summary;
|
||||
timestamp = postData.published;
|
||||
sensitive = postData.sensitive;
|
||||
|
@ -482,36 +406,29 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
let tag = postData.tag;
|
||||
// gts moment
|
||||
if (!Array.isArray(tag)) tag = [tag];
|
||||
emotes = tag
|
||||
.filter((x) => !!x.icon)
|
||||
.map((x) => ({name: x.name, url: x.icon.url}));
|
||||
emotes = tag.filter((x) => !!x.icon).map((x) => ({name: x.name, url: x.icon.url}));
|
||||
}
|
||||
|
||||
// NB: gts doesnt send singular attachments as array
|
||||
const attachments = Array.isArray(postData.attachment)
|
||||
? postData.attachment
|
||||
: [postData.attachment];
|
||||
const attachments = Array.isArray(postData.attachment) ? postData.attachment : [postData.attachment];
|
||||
for (const attachment of attachments) {
|
||||
if (attachment.mediaType) {
|
||||
if (attachment.mediaType.startsWith("video/")) {
|
||||
videos.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: attachment.mediaType,
|
||||
});
|
||||
} else if (attachment.mediaType.startsWith("image/")) {
|
||||
images.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: attachment.mediaType,
|
||||
});
|
||||
} else if (attachment.mediaType.startsWith("audio/")) {
|
||||
audios.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: attachment.mediaType,
|
||||
});
|
||||
}
|
||||
|
@ -524,22 +441,19 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
if (contentType.startsWith("image/")) {
|
||||
images.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: contentType,
|
||||
});
|
||||
} else if (contentType.startsWith("video/")) {
|
||||
videos.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: contentType,
|
||||
});
|
||||
} else if (contentType.startsWith("audio/")) {
|
||||
audios.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: contentType,
|
||||
});
|
||||
}
|
||||
|
@ -551,39 +465,29 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
? type
|
||||
: type +
|
||||
"/" +
|
||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image"
|
||||
? "png"
|
||||
: type == "video"
|
||||
? "mp4"
|
||||
: "mpeg");
|
||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" ? "png" : type == "video" ? "mp4" : "mpeg");
|
||||
if (type.startsWith("image")) {
|
||||
images.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: fileType,
|
||||
});
|
||||
} else if (type.startsWith("video")) {
|
||||
videos.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: fileType,
|
||||
});
|
||||
} else if (type.startsWith("audio")) {
|
||||
audios.push({
|
||||
url: attachment.url,
|
||||
desc:
|
||||
attachment.name ?? attachment.description ?? attachment.comment,
|
||||
desc: attachment.name ?? attachment.description ?? attachment.comment,
|
||||
type: fileType,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
"fedimbed",
|
||||
`Unhandled attachment structure! ${JSON.stringify(attachment)}`
|
||||
);
|
||||
logger.warn("fedimbed", `Unhandled attachment structure! ${JSON.stringify(attachment)}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,30 +503,23 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
images.push({
|
||||
url: postData.image.url,
|
||||
desc: "",
|
||||
type:
|
||||
contentType ??
|
||||
"image/" +
|
||||
imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1),
|
||||
type: contentType ?? "image/" + imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1),
|
||||
});
|
||||
}
|
||||
|
||||
if (postData.name) title = postData.name;
|
||||
|
||||
// Author data is not sent with the post with AS2
|
||||
const authorData = await signedFetch(
|
||||
postData.actor ?? postData.attributedTo,
|
||||
{
|
||||
headers: {
|
||||
"User-Agent": FRIENDLY_USERAGENT,
|
||||
Accept: "application/activity+json",
|
||||
},
|
||||
}
|
||||
)
|
||||
const authorData = await signedFetch(postData.actor ?? postData.attributedTo, {
|
||||
headers: {
|
||||
"User-Agent": FRIENDLY_USERAGENT,
|
||||
Accept: "application/activity+json",
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.catch((err) => {
|
||||
// only posts can be activity+json right now, reduce log spam
|
||||
if (platform !== "cohost")
|
||||
logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);
|
||||
if (platform !== "cohost") logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);
|
||||
});
|
||||
|
||||
if (authorData) {
|
||||
|
@ -637,9 +534,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
// bootleg author, mainly for cohost
|
||||
const authorUrl = postData.actor ?? postData.attributedTo;
|
||||
const authorUrlObj = new URL(authorUrl);
|
||||
const name = authorUrlObj.pathname.substring(
|
||||
authorUrlObj.pathname.lastIndexOf("/") + 1
|
||||
);
|
||||
const name = authorUrlObj.pathname.substring(authorUrlObj.pathname.lastIndexOf("/") + 1);
|
||||
author = {
|
||||
name,
|
||||
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.
|
||||
if (!author) {
|
||||
logger.warn(
|
||||
"fedimbed",
|
||||
`Bailing trying to re-embed "${url}": Failed to get author.`
|
||||
);
|
||||
logger.warn("fedimbed", `Bailing trying to re-embed "${url}": Failed to get author.`);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -682,12 +574,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
|
||||
let desc = "";
|
||||
let MAX_LENGTH = 3999;
|
||||
if (
|
||||
(cw != "" || sensitive) &&
|
||||
images.length == 0 &&
|
||||
videos.length == 0 &&
|
||||
audios.length == 0
|
||||
) {
|
||||
if ((cw != "" || sensitive) && images.length == 0 && videos.length == 0 && audios.length == 0) {
|
||||
desc += "||" + content + "||";
|
||||
MAX_LENGTH -= 4;
|
||||
|
||||
|
@ -708,9 +595,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
}
|
||||
}
|
||||
|
||||
const user = author.name
|
||||
? `${author.name} (${author.handle})`
|
||||
: author.handle;
|
||||
const user = author.name ? `${author.name} (${author.handle})` : author.handle;
|
||||
|
||||
const baseEmbed = {
|
||||
color,
|
||||
|
@ -734,9 +619,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
};
|
||||
if (images.length > 0) {
|
||||
if (images.length > 1) {
|
||||
const links = images
|
||||
.map((attachment, index) => `[Image ${index + 1}](${attachment.url})`)
|
||||
.join(" | ");
|
||||
const links = images.map((attachment, index) => `[Image ${index + 1}](${attachment.url})`).join(" | ");
|
||||
|
||||
if (links.length <= 1024)
|
||||
baseEmbed.fields.push({
|
||||
|
@ -756,9 +639,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
if (videos.length > 1) {
|
||||
baseEmbed.fields.push({
|
||||
name: "Videos",
|
||||
value: videos
|
||||
.map((attachment, index) => `[Video ${index + 1}](${attachment.url})`)
|
||||
.join(" | "),
|
||||
value: videos.map((attachment, index) => `[Video ${index + 1}](${attachment.url})`).join(" | "),
|
||||
inline: true,
|
||||
});
|
||||
} else {
|
||||
|
@ -773,9 +654,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
if (audios.length > 1) {
|
||||
baseEmbed.fields.push({
|
||||
name: "Audios",
|
||||
value: audios
|
||||
.map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`)
|
||||
.join(" | "),
|
||||
value: audios.map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`).join(" | "),
|
||||
inline: true,
|
||||
});
|
||||
} else {
|
||||
|
@ -796,29 +675,24 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
const percent = o.count / poll.total;
|
||||
const bar = Math.round(percent * 30);
|
||||
|
||||
return `**${o.name}** (${o.count}, ${Math.round(
|
||||
percent * 100
|
||||
)}%)\n\`[${"=".repeat(bar)}${" ".repeat(30 - bar)}]\``;
|
||||
return `**${o.name}** (${o.count}, ${Math.round(percent * 100)}%)\n\`[${"=".repeat(bar)}${" ".repeat(
|
||||
30 - bar
|
||||
)}]\``;
|
||||
})
|
||||
.join("\n\n") +
|
||||
`\n\n${poll.total} votes \u2022 Ends <t:${Math.floor(
|
||||
poll.end.getTime() / 1000
|
||||
)}:R>`,
|
||||
.join("\n\n") + `\n\n${poll.total} votes \u2022 Ends <t:${Math.floor(poll.end.getTime() / 1000)}:R>`,
|
||||
});
|
||||
}
|
||||
|
||||
let sendWait = false;
|
||||
if (videos.length > 0 || audios.length > 0 || images.length > 4) {
|
||||
sendWait = true;
|
||||
if (msg instanceof Dysnomia.Message) await msg.addReaction("\uD83D\uDCE4");
|
||||
if (msg instanceof Message) await msg.addReaction("\uD83D\uDCE4");
|
||||
}
|
||||
|
||||
const embeds = [];
|
||||
const files = [];
|
||||
|
||||
const guild =
|
||||
msg.channel?.guild ??
|
||||
(msg.guildID ? hf.bot.guilds.get(msg.guildID) : false);
|
||||
const guild = msg.channel?.guild ?? (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false);
|
||||
|
||||
if (images.length > 0) {
|
||||
if (images.length <= 4) {
|
||||
|
@ -852,9 +726,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||
(attachment.type.indexOf("/") > -1
|
||||
? attachment.type.replace("/", ".")
|
||||
: attachment.type +
|
||||
"." +
|
||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
||||
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
||||
file,
|
||||
description: attachment.desc,
|
||||
});
|
||||
|
@ -886,9 +758,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||
(attachment.type.indexOf("/") > -1
|
||||
? attachment.type.replace("/", ".")
|
||||
: attachment.type +
|
||||
"." +
|
||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
||||
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
|
||||
file,
|
||||
description: attachment.desc,
|
||||
});
|
||||
|
@ -959,9 +829,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||
(attachment.type.indexOf("/") > -1
|
||||
? attachment.type.replace("/", ".").replace("quicktime", "mov")
|
||||
: attachment.type +
|
||||
"." +
|
||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")),
|
||||
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")),
|
||||
file,
|
||||
});
|
||||
}
|
||||
|
@ -989,14 +857,8 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
filename:
|
||||
(cw != "" || spoiler ? "SPOILER_" : "") +
|
||||
(attachment.type.indexOf("/") > -1
|
||||
? attachment.type
|
||||
.replace("/", ".")
|
||||
.replace("mpeg", "mp3")
|
||||
.replace("vnd.wave", "wav")
|
||||
.replace("x-", "")
|
||||
: attachment.type +
|
||||
"." +
|
||||
(url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")),
|
||||
? attachment.type.replace("/", ".").replace("mpeg", "mp3").replace("vnd.wave", "wav").replace("x-", "")
|
||||
: attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")),
|
||||
file,
|
||||
});
|
||||
}
|
||||
|
@ -1006,8 +868,7 @@ async function processUrl(msg, url, spoiler = false) {
|
|||
return {
|
||||
response: {
|
||||
content:
|
||||
cw != "" &&
|
||||
(images.length > 0 || videos.length > 0 || audios.length > 0)
|
||||
cw != "" && (images.length > 0 || videos.length > 0 || audios.length > 0)
|
||||
? `:warning: ${cw} || ${url} ||`
|
||||
: spoiler
|
||||
? `|| ${url} ||`
|
||||
|
@ -1051,10 +912,7 @@ events.add("messageCreate", "fedimbed", async function (msg) {
|
|||
for (const service of Object.keys(PATH_REGEX)) {
|
||||
const regex = PATH_REGEX[service];
|
||||
if (urlObj && regex.test(urlObj.pathname)) {
|
||||
logger.verbose(
|
||||
"fedimbed",
|
||||
`Hit "${service}" for "${url}", processing now.`
|
||||
);
|
||||
logger.verbose("fedimbed", `Hit "${service}" for "${url}", processing now.`);
|
||||
try {
|
||||
const {response, sendWait} = await processUrl(msg, url, hasSpoiler);
|
||||
await msg.channel.createMessage(response).then(() => {
|
||||
|
@ -1067,10 +925,7 @@ events.add("messageCreate", "fedimbed", async function (msg) {
|
|||
}
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"fedimbed",
|
||||
`Error processing "${url}":\n` + err.stack
|
||||
);
|
||||
logger.error("fedimbed", `Error processing "${url}":\n` + err.stack);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1080,8 +935,7 @@ events.add("messageCreate", "fedimbed", async function (msg) {
|
|||
});
|
||||
|
||||
const fedimbedCommand = new InteractionCommand("fedimbed");
|
||||
fedimbedCommand.helpText =
|
||||
"Better embeds for fediverse (Mastodon, Pleroma, etc) posts";
|
||||
fedimbedCommand.helpText = "Better embeds for fediverse (Mastodon, Pleroma, etc) posts";
|
||||
fedimbedCommand.options.url = {
|
||||
name: "url",
|
||||
type: ApplicationCommandOptionTypes.STRING,
|
||||
|
@ -1098,8 +952,8 @@ fedimbedCommand.options.spoiler = {
|
|||
};
|
||||
fedimbedCommand.permissions = Permissions.embedLinks | Permissions.attachFiles;
|
||||
fedimbedCommand.callback = async function (interaction) {
|
||||
let url = getOption(interaction, fedimbedCommand, "url");
|
||||
const spoiler = getOption(interaction, fedimbedCommand, "spoiler");
|
||||
let url = this.getOption(interaction, "url");
|
||||
const spoiler = this.getOption(interaction, "spoiler");
|
||||
|
||||
url = url
|
||||
.replace(/\|/g, "")
|
||||
|
|
|
@ -5,16 +5,15 @@ const CATEGORY = "misc";
|
|||
const FOXWELLS_GUILD_ID = "300436792916836352";
|
||||
|
||||
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");
|
||||
|
||||
// taken from rot13.com
|
||||
function rot(s, i) {
|
||||
return s.replace(/[a-zA-Z]/g, function (c) {
|
||||
return String.fromCharCode(
|
||||
(c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + i) ? c : c - 26
|
||||
);
|
||||
return String.fromCharCode((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!
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${cachedLightsURL}?r=${r}&g=${g}&b=${b}${bri ? `&bri=${bri}` : ""}`
|
||||
);
|
||||
const response = await fetch(`${cachedLightsURL}?r=${r}&g=${g}&b=${b}${bri ? `&bri=${bri}` : ""}`);
|
||||
|
||||
if (response.status == 200) {
|
||||
return {reaction: "\uD83D\uDC4C"};
|
||||
|
@ -54,17 +51,11 @@ utsuholights.callback = async function (msg, line, [hex, bri]) {
|
|||
};
|
||||
//hf.registerCommand(utsuholights);
|
||||
|
||||
const JPEG_HEADER = Buffer.from([
|
||||
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46,
|
||||
]);
|
||||
const JPEG_HEADER = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46]);
|
||||
|
||||
async function fetchPlant() {
|
||||
const res = await fetch("https://nuclear.utsuho.rocks/plant/");
|
||||
const boundary = res.headers
|
||||
.get("Content-Type")
|
||||
.split(";")[1]
|
||||
.trim()
|
||||
.replace("boundary=", "--");
|
||||
const boundary = res.headers.get("Content-Type").split(";")[1].trim().replace("boundary=", "--");
|
||||
let buffer = Buffer.alloc(0);
|
||||
|
||||
let searchForNextBoundary = false;
|
||||
|
@ -78,9 +69,7 @@ async function fetchPlant() {
|
|||
} else if (searchForNextBoundary) {
|
||||
if (data.toString().startsWith(boundary)) {
|
||||
res.body.end();
|
||||
const length = Number(
|
||||
buffer.toString().match(/Content-Length:.*?(\d+)/)[1]
|
||||
);
|
||||
const length = Number(buffer.toString().match(/Content-Length:.*?(\d+)/)[1]);
|
||||
const headerOffset = buffer.indexOf(JPEG_HEADER);
|
||||
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 vin_channel;
|
||||
let board_channel;
|
||||
|
@ -300,9 +168,7 @@ async function processReaction(_msg, reaction, user) {
|
|||
if (reaction.name != "\u2b50") return;
|
||||
|
||||
if (!vin_channel) {
|
||||
vin_channel = hf.bot.guilds
|
||||
.get(FOXWELLS_GUILD_ID)
|
||||
.channels.get(VINBOARD_CHANNEL_ID);
|
||||
vin_channel = hf.bot.guilds.get(FOXWELLS_GUILD_ID).channels.get(VINBOARD_CHANNEL_ID);
|
||||
}
|
||||
|
||||
if (!vin_channel) {
|
||||
|
@ -311,9 +177,7 @@ async function processReaction(_msg, reaction, user) {
|
|||
}
|
||||
|
||||
if (!board_channel) {
|
||||
board_channel = hf.bot.guilds
|
||||
.get(FOXWELLS_GUILD_ID)
|
||||
.threads.get(VINBOARD_THREAD_ID);
|
||||
board_channel = hf.bot.guilds.get(FOXWELLS_GUILD_ID).threads.get(VINBOARD_THREAD_ID);
|
||||
}
|
||||
|
||||
if (!board_channel) {
|
||||
|
@ -321,15 +185,11 @@ async function processReaction(_msg, reaction, user) {
|
|||
return;
|
||||
}
|
||||
|
||||
const msg =
|
||||
vin_channel.messages.get(_msg.id) ??
|
||||
(await vin_channel.getMessage(_msg.id));
|
||||
const msg = vin_channel.messages.get(_msg.id) ?? (await vin_channel.getMessage(_msg.id));
|
||||
|
||||
if (!vinboard_webhook) {
|
||||
const webhooks = await vin_channel.getWebhooks();
|
||||
vinboard_webhook = webhooks.find(
|
||||
(webhook) => webhook.id == VINBOARD_WEBHOOK_ID
|
||||
);
|
||||
vinboard_webhook = webhooks.find((webhook) => webhook.id == VINBOARD_WEBHOOK_ID);
|
||||
}
|
||||
|
||||
if (!vinboard_webhook) {
|
||||
|
@ -338,9 +198,7 @@ async function processReaction(_msg, reaction, user) {
|
|||
}
|
||||
|
||||
const reacts = await msg.getReaction("\u2b50");
|
||||
const trueCount = reacts.filter(
|
||||
(reactor) => reactor.id != msg.author.id
|
||||
).length;
|
||||
const trueCount = reacts.filter((reactor) => reactor.id != msg.author.id).length;
|
||||
|
||||
const dbEntry = await getBoardEntry(msg.id);
|
||||
if (dbEntry) {
|
||||
|
@ -360,12 +218,7 @@ async function processReaction(_msg, reaction, user) {
|
|||
board_channel.messages.get(dbEntry.board_id) ??
|
||||
(await board_channel.getMessage(dbEntry.board_id).catch(() => {}));
|
||||
if (_boardMessage) {
|
||||
logger.verbose(
|
||||
"vinboard",
|
||||
`Updating count for "${msg.id}" (${
|
||||
dbEntry.count ?? 0
|
||||
} -> ${trueCount})`
|
||||
);
|
||||
logger.verbose("vinboard", `Updating count for "${msg.id}" (${dbEntry.count ?? 0} -> ${trueCount})`);
|
||||
|
||||
const props = {
|
||||
avatarURL: _boardMessage.author.avatarURL,
|
||||
|
@ -374,24 +227,17 @@ async function processReaction(_msg, reaction, user) {
|
|||
embeds: _boardMessage.embeds,
|
||||
wait: true,
|
||||
};
|
||||
props.attachments = [..._boardMessage.attachments.values()].map(
|
||||
(attach) => ({id: attach.id})
|
||||
);
|
||||
props.attachments = [..._boardMessage.attachments.values()].map((attach) => ({id: attach.id}));
|
||||
props.embeds[0].title = `${trueCount} \u2b50`;
|
||||
props.embeds[0].color = pastelize(msg.author.username);
|
||||
await hf.bot.editWebhookMessage(
|
||||
vinboard_webhook.id,
|
||||
vinboard_webhook.token,
|
||||
_boardMessage.id,
|
||||
props
|
||||
);
|
||||
await hf.bot.editWebhookMessage(vinboard_webhook.id, vinboard_webhook.token, _boardMessage.id, props);
|
||||
await setBoardEntry(msg.id, trueCount, _boardMessage.id);
|
||||
} else {
|
||||
logger.verbose("vinboard", `Creating entry for "${msg.id}"`);
|
||||
const boardMessage = await hf.bot.executeWebhook(
|
||||
vinboard_webhook.id,
|
||||
vinboard_webhook.token,
|
||||
await createBoardMessage(msg, trueCount)
|
||||
await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID)
|
||||
);
|
||||
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
||||
}
|
||||
|
@ -400,7 +246,7 @@ async function processReaction(_msg, reaction, user) {
|
|||
const boardMessage = await hf.bot.executeWebhook(
|
||||
vinboard_webhook.id,
|
||||
vinboard_webhook.token,
|
||||
await createBoardMessage(msg, trueCount)
|
||||
await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID)
|
||||
);
|
||||
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
||||
}
|
||||
|
@ -409,7 +255,7 @@ async function processReaction(_msg, reaction, user) {
|
|||
const boardMessage = await hf.bot.executeWebhook(
|
||||
vinboard_webhook.id,
|
||||
vinboard_webhook.token,
|
||||
await createBoardMessage(msg, trueCount)
|
||||
await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID)
|
||||
);
|
||||
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const Command = require("../lib/command.js");
|
||||
const CATEGORY = "general";
|
||||
|
||||
const {snowflakeToTimestamp} = require("../lib/utils.js");
|
||||
const {snowflakeToTimestamp} = require("../util/time.js");
|
||||
|
||||
const help = new Command("help");
|
||||
help.category = CATEGORY;
|
||||
|
@ -27,9 +27,7 @@ help.callback = function (msg, line) {
|
|||
for (const cat in sorted) {
|
||||
embed.fields.push({
|
||||
name: cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1),
|
||||
value: `${sorted[cat].length} Commands\n\`${
|
||||
hf.config.prefix
|
||||
}help --${cat.toLowerCase()}\``,
|
||||
value: `${sorted[cat].length} Commands\n\`${hf.config.prefix}help --${cat.toLowerCase()}\``,
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
|
@ -40,9 +38,7 @@ help.callback = function (msg, line) {
|
|||
|
||||
if (sorted[cat]) {
|
||||
const embed = {
|
||||
title: `HiddenPhox Help: Category > ${
|
||||
cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1)
|
||||
}`,
|
||||
title: `HiddenPhox Help: Category > ${cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1)}`,
|
||||
fields: [],
|
||||
};
|
||||
|
||||
|
@ -78,9 +74,7 @@ help.callback = function (msg, line) {
|
|||
fields: [
|
||||
{
|
||||
name: "Category",
|
||||
value:
|
||||
cmd.category.toUpperCase().charAt(0) +
|
||||
cmd.category.toLowerCase().substring(1),
|
||||
value: cmd.category.toUpperCase().charAt(0) + cmd.category.toLowerCase().substring(1),
|
||||
inline: true,
|
||||
},
|
||||
],
|
||||
|
@ -125,9 +119,7 @@ ping.callback = async function (msg) {
|
|||
const msgTimestamp = snowflakeToTimestamp(msg.id);
|
||||
const noncePing = Math.floor(msgTimestamp - nonceTimestamp);
|
||||
|
||||
const gateway = hf.bot.shards.get(
|
||||
hf.bot.guildShardMap[hf.bot.channelGuildMap[msg.channel.id]] || 0
|
||||
).latency;
|
||||
const gateway = hf.bot.shards.get(hf.bot.guildShardMap[hf.bot.channelGuildMap[msg.channel.id]] || 0).latency;
|
||||
|
||||
const newMsg = await msg.channel.createMessage({
|
||||
content: `Pong.\n**RTT:** \`...\`\n**Gateway:** \`${gateway}ms\`${
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const Command = require("../lib/command.js");
|
||||
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");
|
||||
|
||||
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.helpText = "Among Us Dumpy GIF Creator";
|
||||
dumpy.usage = "<width> [url]";
|
||||
|
@ -37,14 +37,9 @@ dumpy.callback = async function (msg, line, [width, url]) {
|
|||
if (isNaN(parseInt(width))) url = width;
|
||||
width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48);
|
||||
|
||||
return await createImageCallback(
|
||||
msg,
|
||||
url,
|
||||
async (img) => await dumpyConvert(img, width),
|
||||
"dumpy.gif"
|
||||
);
|
||||
return await createImageCallback(msg, url, async (img) => await dumpyConvert(img, width), "dumpy.gif");
|
||||
};
|
||||
hf.registerCommand(dumpy);
|
||||
hf.registerCommand(dumpy);*/
|
||||
|
||||
const hooh = new Command("hooh");
|
||||
hooh.category = CATEGORY;
|
||||
|
@ -56,14 +51,7 @@ hooh.callback = async function (msg, line) {
|
|||
line,
|
||||
async (url) => {
|
||||
const img = await Jimp.read(url);
|
||||
const half1 = img
|
||||
.clone()
|
||||
.crop(
|
||||
0,
|
||||
img.bitmap.height / 2,
|
||||
img.bitmap.width,
|
||||
img.bitmap.height / 2
|
||||
);
|
||||
const half1 = img.clone().crop(0, img.bitmap.height / 2, img.bitmap.width, img.bitmap.height / 2);
|
||||
const half2 = half1.clone().mirror(false, true);
|
||||
|
||||
return await img
|
||||
|
@ -86,9 +74,7 @@ woow.callback = async function (msg, line) {
|
|||
line,
|
||||
async (url) => {
|
||||
const img = await Jimp.read(url);
|
||||
const half1 = img
|
||||
.clone()
|
||||
.crop(0, 0, img.bitmap.width, img.bitmap.height / 2);
|
||||
const half1 = img.clone().crop(0, 0, img.bitmap.width, img.bitmap.height / 2);
|
||||
const half2 = half1.clone().mirror(false, true);
|
||||
|
||||
return await img
|
||||
|
@ -111,9 +97,7 @@ haah.callback = async function (msg, line) {
|
|||
line,
|
||||
async (url) => {
|
||||
const img = await Jimp.read(url);
|
||||
const half1 = img
|
||||
.clone()
|
||||
.crop(0, 0, img.bitmap.width / 2, img.bitmap.height);
|
||||
const half1 = img.clone().crop(0, 0, img.bitmap.width / 2, img.bitmap.height);
|
||||
const half2 = half1.clone().mirror(true, false);
|
||||
|
||||
return await img
|
||||
|
@ -136,9 +120,7 @@ waaw.callback = async function (msg, line) {
|
|||
line,
|
||||
async (url) => {
|
||||
const img = await Jimp.read(url);
|
||||
const half1 = img
|
||||
.clone()
|
||||
.crop(img.bitmap.width / 2, 0, img.bitmap.width / 2, img.bitmap.height);
|
||||
const half1 = img.clone().crop(img.bitmap.width / 2, 0, img.bitmap.width / 2, img.bitmap.height);
|
||||
const half2 = half1.clone().mirror(true, false);
|
||||
|
||||
return await img
|
||||
|
@ -160,9 +142,7 @@ invert.callback = async function (msg, line) {
|
|||
msg,
|
||||
line,
|
||||
async (img) => {
|
||||
return await Jimp.read(img).then((x) =>
|
||||
x.invert().getBufferAsync(Jimp.MIME_PNG)
|
||||
);
|
||||
return await Jimp.read(img).then((x) => x.invert().getBufferAsync(Jimp.MIME_PNG));
|
||||
},
|
||||
"invert.png"
|
||||
);
|
||||
|
@ -178,9 +158,7 @@ flip.callback = async function (msg, line) {
|
|||
msg,
|
||||
line,
|
||||
async (img) => {
|
||||
return await Jimp.read(img).then((x) =>
|
||||
x.mirror(true, false).getBufferAsync(Jimp.MIME_PNG)
|
||||
);
|
||||
return await Jimp.read(img).then((x) => x.mirror(true, false).getBufferAsync(Jimp.MIME_PNG));
|
||||
},
|
||||
"flip.png"
|
||||
);
|
||||
|
@ -196,9 +174,7 @@ flop.callback = async function (msg, line) {
|
|||
msg,
|
||||
line,
|
||||
async (img) => {
|
||||
return await Jimp.read(img).then((x) =>
|
||||
x.mirror(false, true).getBufferAsync(Jimp.MIME_PNG)
|
||||
);
|
||||
return await Jimp.read(img).then((x) => x.mirror(false, true).getBufferAsync(Jimp.MIME_PNG));
|
||||
},
|
||||
"flop.png"
|
||||
);
|
||||
|
@ -215,9 +191,7 @@ jpeg.callback = async function (msg, line) {
|
|||
msg,
|
||||
line,
|
||||
async (img) => {
|
||||
return await Jimp.read(img).then((x) =>
|
||||
x.quality(1).getBufferAsync(Jimp.MIME_JPEG)
|
||||
);
|
||||
return await Jimp.read(img).then((x) => x.quality(1).getBufferAsync(Jimp.MIME_JPEG));
|
||||
},
|
||||
"jpeg.jpg"
|
||||
);
|
||||
|
|
|
@ -3,16 +3,12 @@ const InteractionCommand = require("../lib/interactionCommand.js");
|
|||
const logger = require("../lib/logger.js");
|
||||
const CATEGORY = "misc";
|
||||
|
||||
const {ApplicationCommandOptionTypes} =
|
||||
require("@projectdysnomia/dysnomia").Constants;
|
||||
const {librex} = require("../../config.json");
|
||||
const {getOption} = require("../lib/interactionDispatcher.js");
|
||||
const {
|
||||
formatTime,
|
||||
parseHtmlEntities,
|
||||
formatUsername,
|
||||
safeString,
|
||||
} = require("../lib/utils.js");
|
||||
const {ApplicationCommandOptionTypes} = require("../util/dconstants.js");
|
||||
|
||||
const {formatUsername, safeString} = require("../util/misc.js");
|
||||
const {formatTime} = require("../util/time.js");
|
||||
const {parseHtmlEntities} = require("../util/html.js");
|
||||
|
||||
const GoogleImages = require("google-images");
|
||||
const {tinycolor, random: randomColor} = require("@ctrl/tinycolor");
|
||||
const sharp = require("sharp");
|
||||
|
@ -28,15 +24,13 @@ yt.usage = "[search term]";
|
|||
yt.callback = async function (msg, line) {
|
||||
if (!line) return "Arguments are required.";
|
||||
|
||||
const req = await fetch(
|
||||
`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`
|
||||
).then((x) => x.json());
|
||||
const req = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`).then((x) =>
|
||||
x.json()
|
||||
);
|
||||
|
||||
const topVid = req.items[0];
|
||||
|
||||
let out = `**${safeString(
|
||||
parseHtmlEntities(topVid.title)
|
||||
)}** | \`${safeString(
|
||||
let out = `**${safeString(parseHtmlEntities(topVid.title))}** | \`${safeString(
|
||||
parseHtmlEntities(topVid.uploaderName)
|
||||
)}\`\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];
|
||||
if (!vid) continue;
|
||||
|
||||
out += `- **${safeString(
|
||||
parseHtmlEntities(vid.title)
|
||||
)}** | By: \`${safeString(
|
||||
out += `- **${safeString(parseHtmlEntities(vid.title))}** | By: \`${safeString(
|
||||
parseHtmlEntities(vid.uploaderName)
|
||||
)}\` | <https://youtube.com${vid.url}>\n`;
|
||||
}
|
||||
|
@ -65,7 +57,7 @@ ytInteraction.options.search = {
|
|||
default: "",
|
||||
};
|
||||
ytInteraction.callback = async function (interaction) {
|
||||
const search = getOption(interaction, ytInteraction, "search");
|
||||
const search = this.getOption(interaction, "search");
|
||||
|
||||
return yt.callback(interaction, search);
|
||||
};
|
||||
|
@ -78,9 +70,9 @@ fyt.usage = "[search term]";
|
|||
fyt.callback = async function (msg, line) {
|
||||
if (!line) return "Arguments are required.";
|
||||
|
||||
const req = await fetch(
|
||||
`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`
|
||||
).then((x) => x.json());
|
||||
const req = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`).then((x) =>
|
||||
x.json()
|
||||
);
|
||||
|
||||
const vid = req.items[0];
|
||||
|
||||
|
@ -100,7 +92,7 @@ fytInteraction.options.search = {
|
|||
default: "",
|
||||
};
|
||||
fytInteraction.callback = async function (interaction) {
|
||||
const search = getOption(interaction, fytInteraction, "search");
|
||||
const search = this.getOption(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 req = await fetch(
|
||||
`http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(
|
||||
query
|
||||
)}&appid=LH2K8H-T3QKETAGT3&output=json`
|
||||
`http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(query)}&appid=LH2K8H-T3QKETAGT3&output=json`
|
||||
).then((x) => x.json());
|
||||
|
||||
const data = req.queryresult.pods;
|
||||
|
@ -148,11 +138,9 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) {
|
|||
for (const x in extra) {
|
||||
embed.fields.push({
|
||||
name: extra[x].title,
|
||||
value: `[${
|
||||
extra[x].subpods[0].plaintext.length > 0
|
||||
? extra[x].subpods[0].plaintext
|
||||
: "<click for image>"
|
||||
}](${extra[x].subpods[0].img.src})`,
|
||||
value: `[${extra[x].subpods[0].plaintext.length > 0 ? extra[x].subpods[0].plaintext : "<click for image>"}](${
|
||||
extra[x].subpods[0].img.src
|
||||
})`,
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
|
@ -173,8 +161,7 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) {
|
|||
}
|
||||
|
||||
let string = "";
|
||||
if (data[1].subpods[0].plaintext.length > 0)
|
||||
string = safeString(data[1].subpods[0].plaintext);
|
||||
if (data[1].subpods[0].plaintext.length > 0) string = safeString(data[1].subpods[0].plaintext);
|
||||
|
||||
let text;
|
||||
if (string.length > 2000 - (6 + safeString(query).length)) {
|
||||
|
@ -216,8 +203,8 @@ wolframInteraction.options.verbose = {
|
|||
default: false,
|
||||
};
|
||||
wolframInteraction.callback = async function (interaction) {
|
||||
const query = getOption(interaction, wolframInteraction, "query");
|
||||
const verbose = getOption(interaction, wolframInteraction, "verbose");
|
||||
const query = this.getOption(interaction, "query");
|
||||
const verbose = this.getOption(interaction, "verbose");
|
||||
|
||||
return wolfram.callback(interaction, query, [query], {verbose});
|
||||
};
|
||||
|
@ -232,10 +219,7 @@ gimg.callback = async function (msg, line) {
|
|||
if (!line) return "No arguments given.";
|
||||
|
||||
const images = await imagesClient.search(line, {
|
||||
safe:
|
||||
msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]")
|
||||
? "off"
|
||||
: "high",
|
||||
safe: msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") ? "off" : "high",
|
||||
});
|
||||
|
||||
const index = Math.floor(Math.random() * images.length);
|
||||
|
@ -250,9 +234,7 @@ gimg.callback = async function (msg, line) {
|
|||
url: image.url,
|
||||
},
|
||||
footer: {
|
||||
text: `Image ${Number(index) + 1}/${
|
||||
images.length
|
||||
}. Rerun to get a different image.`,
|
||||
text: `Image ${Number(index) + 1}/${images.length}. Rerun to get a different image.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -268,10 +250,7 @@ fimg.callback = async function (msg, line) {
|
|||
if (!line) return "No arguments given.";
|
||||
|
||||
const images = await imagesClient.search(line, {
|
||||
safe:
|
||||
msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]")
|
||||
? "off"
|
||||
: "high",
|
||||
safe: msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") ? "off" : "high",
|
||||
});
|
||||
|
||||
const image = images[0];
|
||||
|
@ -295,22 +274,15 @@ poll.category = CATEGORY;
|
|||
poll.helpText = "Start a poll";
|
||||
poll.usage = "[topic] [option 1] [option 2] [...option 3-10]";
|
||||
poll.callback = async function (msg, line, [topic, ...options]) {
|
||||
if (!line || !topic)
|
||||
return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"';
|
||||
if (!line || !topic) return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"';
|
||||
|
||||
const arrOptions = [...options].slice(0, 10);
|
||||
if (arrOptions.length < 2) return "A minimum of two options are required.";
|
||||
|
||||
const reactions = [];
|
||||
let displayString = `**${formatUsername(
|
||||
msg.author
|
||||
)}** has started a poll:\n## __${topic}__\n`;
|
||||
let displayString = `**${formatUsername(msg.author)}** has started a poll:\n## __${topic}__\n`;
|
||||
for (let i = 0; i < arrOptions.length; i++) {
|
||||
displayString +=
|
||||
(i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`) +
|
||||
": " +
|
||||
arrOptions[i] +
|
||||
"\n";
|
||||
displayString += (i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`) + ": " + arrOptions[i] + "\n";
|
||||
reactions[i] = i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`;
|
||||
}
|
||||
|
||||
|
@ -353,28 +325,20 @@ anonradio.callback = async function () {
|
|||
|
||||
let playing;
|
||||
try {
|
||||
playing = await fetch("https://anonradio.net/playing").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
playing = await fetch("https://anonradio.net/playing").then((res) => res.text());
|
||||
} catch (err) {
|
||||
try {
|
||||
playing = await fetch("http://anonradio.net/playing").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
playing = await fetch("http://anonradio.net/playing").then((res) => res.text());
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
}
|
||||
let schedule;
|
||||
try {
|
||||
schedule = await fetch("https://anonradio.net/schedule/").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
schedule = await fetch("https://anonradio.net/schedule/").then((res) => res.text());
|
||||
} catch (err) {
|
||||
try {
|
||||
schedule = await fetch("http://anonradio.net/schedule/").then((res) =>
|
||||
res.text()
|
||||
);
|
||||
schedule = await fetch("http://anonradio.net/schedule/").then((res) => res.text());
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
|
@ -384,9 +348,7 @@ anonradio.callback = async function () {
|
|||
|
||||
const icecast = await fetch("http://anonradio.net:8010/status-json.xsl")
|
||||
.then((res) => res.text())
|
||||
.then((data) =>
|
||||
JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",'))
|
||||
);
|
||||
.then((data) => JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",')));
|
||||
|
||||
let lines = schedule.split("\n");
|
||||
lines = lines.slice(4, lines.length - 2);
|
||||
|
@ -418,17 +380,11 @@ anonradio.callback = async function () {
|
|||
hour = hour.slice(0, 2) + ":" + hour.slice(-2);
|
||||
|
||||
const timestamp =
|
||||
Date.parse(
|
||||
`${DAYS[targetDay]}, ${currentYear}-${targetMonth}-${targetDateDay} ${hour} UTC`
|
||||
) / 1000;
|
||||
Date.parse(`${DAYS[targetDay]}, ${currentYear}-${targetMonth}-${targetDateDay} ${hour} UTC`) / 1000;
|
||||
|
||||
let nameOut = name;
|
||||
|
||||
if (time == "Sat 0300")
|
||||
nameOut = name.replace(
|
||||
"Open Mic - Anyone can stream",
|
||||
"Synth Battle Royale"
|
||||
);
|
||||
if (time == "Sat 0300") nameOut = name.replace("Open Mic - Anyone can stream", "Synth Battle Royale");
|
||||
|
||||
parsedLines.push({
|
||||
timestamp,
|
||||
|
@ -451,14 +407,9 @@ anonradio.callback = async function () {
|
|||
title = `${liveNow.name} (\`${liveNow.id}\`)`;
|
||||
subtitle = playing;
|
||||
} else {
|
||||
const [_, current, peakDay, peakMonth, dj, metadata] = playing.match(
|
||||
/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/
|
||||
);
|
||||
const [_, current, peakDay, peakMonth, dj, metadata] = playing.match(/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/);
|
||||
|
||||
if (
|
||||
metadata == "https://archives.anonradio.net" ||
|
||||
liveNow.name == "Synth Battle Royale"
|
||||
) {
|
||||
if (metadata == "https://archives.anonradio.net" || liveNow.name == "Synth Battle Royale") {
|
||||
title = `${liveNow.name} (\`${dj}\`)`;
|
||||
} else {
|
||||
title = `${metadata} (\`${dj}\`)`;
|
||||
|
@ -468,14 +419,10 @@ anonradio.callback = async function () {
|
|||
|
||||
let openmicTime = "";
|
||||
if (liveNow.id == "openmic") {
|
||||
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) {
|
||||
const startTime = new Date(streamData.stream_start_iso8601).getTime();
|
||||
openmicTime = `-\\*- OpenMIC DJ has been streaming for ${formatTime(
|
||||
Date.now() - startTime
|
||||
)} -\\*-\n`;
|
||||
openmicTime = `-\\*- OpenMIC DJ has been streaming for ${formatTime(Date.now() - startTime)} -\\*-\n`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,9 +454,7 @@ shodan.callback = async function (msg, line) {
|
|||
if (!line || line == "") return "Arguments required.";
|
||||
if (!REGEX_IPV4.test(line)) return "Invalid IP address.";
|
||||
|
||||
const data = await fetch("https://internetdb.shodan.io/" + line).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
const data = await fetch("https://internetdb.shodan.io/" + line).then((res) => res.json());
|
||||
|
||||
if (data.detail) return data.detail;
|
||||
|
||||
|
@ -519,10 +464,7 @@ shodan.callback = async function (msg, line) {
|
|||
fields: [
|
||||
{
|
||||
name: "Hostnames",
|
||||
value:
|
||||
data.hostnames.length > 0
|
||||
? data.hostnames.map((str) => `\`${str}\``).join(" ")
|
||||
: "None",
|
||||
value: data.hostnames.length > 0 ? data.hostnames.map((str) => `\`${str}\``).join(" ") : "None",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
|
@ -532,26 +474,17 @@ shodan.callback = async function (msg, line) {
|
|||
},
|
||||
{
|
||||
name: "Tags",
|
||||
value:
|
||||
data.tags.length > 0
|
||||
? data.tags.map((str) => `\`${str}\``).join(", ")
|
||||
: "None",
|
||||
value: data.tags.length > 0 ? data.tags.map((str) => `\`${str}\``).join(", ") : "None",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "CPEs",
|
||||
value:
|
||||
data.cpes.length > 0
|
||||
? data.cpes.map((str) => `\`${str}\``).join(" ")
|
||||
: "None",
|
||||
value: data.cpes.length > 0 ? data.cpes.map((str) => `\`${str}\``).join(" ") : "None",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Vulnerabilities",
|
||||
value:
|
||||
data.vulns.length > 0
|
||||
? data.vulns.map((str) => `\`${str}\``).join(" ")
|
||||
: "None",
|
||||
value: data.vulns.length > 0 ? data.vulns.map((str) => `\`${str}\``).join(" ") : "None",
|
||||
inline: true,
|
||||
},
|
||||
],
|
||||
|
@ -596,9 +529,7 @@ generate.callback = async function (msg, line) {
|
|||
const title = `Responses for "${safeString(line)}"`;
|
||||
|
||||
const out = {
|
||||
content: `Generated in ${formatTime(Date.now() - start)}${
|
||||
retries > 0 ? " with " + retries + " retries" : ""
|
||||
}`,
|
||||
content: `Generated in ${formatTime(Date.now() - start)}${retries > 0 ? " with " + retries + " retries" : ""}`,
|
||||
embeds: [],
|
||||
files: images,
|
||||
};
|
||||
|
@ -627,7 +558,7 @@ search.addAlias("google");
|
|||
search.addAlias("ddg");
|
||||
search.callback = async function (msg, line, args, {results = 2}) {
|
||||
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.";
|
||||
|
||||
const encodedQuery = encodeURIComponent(query);
|
||||
|
@ -638,9 +569,7 @@ search.callback = async function (msg, line, args, {results = 2}) {
|
|||
if (res.url != url) return res.url;
|
||||
}
|
||||
|
||||
const res = await fetch(`${librex}/api.php?q=${encodedQuery}&p=0&t=0`).then(
|
||||
(res) => res.json()
|
||||
);
|
||||
const res = await fetch(`${hf.config.librex}/api.php?q=${encodedQuery}&p=0&t=0`).then((res) => res.json());
|
||||
if (res.error?.message) {
|
||||
if (res.error.message.indexOf("No results found.") > -1) {
|
||||
return "Search returned no results.";
|
||||
|
@ -649,27 +578,17 @@ search.callback = async function (msg, line, args, {results = 2}) {
|
|||
}
|
||||
} else {
|
||||
const searchResults = Object.values(res)
|
||||
.filter(
|
||||
(result) => result.did_you_mean == null && typeof result !== "string"
|
||||
)
|
||||
.filter((result) => result.did_you_mean == null && typeof result !== "string")
|
||||
.splice(0, Number(results));
|
||||
|
||||
if (searchResults.length > 0) {
|
||||
let out = `__**Results for \`${safeString(query)}\`**__\n`;
|
||||
for (const result of searchResults) {
|
||||
if (result.special_response) {
|
||||
out +=
|
||||
"> " +
|
||||
safeString(
|
||||
parseHtmlEntities(
|
||||
result.special_response.response.split("\n").join("\n> ")
|
||||
)
|
||||
);
|
||||
out += "> " + safeString(parseHtmlEntities(result.special_response.response.split("\n").join("\n> ")));
|
||||
out += `\n<${encodeURI(result.special_response.source)}>`;
|
||||
} else {
|
||||
out += `**${safeString(
|
||||
parseHtmlEntities(result.title)
|
||||
).trim()}** - <${encodeURI(result.url)}>`;
|
||||
out += `**${safeString(parseHtmlEntities(result.title)).trim()}** - <${encodeURI(result.url)}>`;
|
||||
out += `\n> ${safeString(parseHtmlEntities(result.description))}`;
|
||||
}
|
||||
out += "\n\n";
|
||||
|
@ -702,8 +621,8 @@ searchInteraction.options.results = {
|
|||
default: 2,
|
||||
};
|
||||
searchInteraction.callback = async function (interaction) {
|
||||
const query = getOption(interaction, searchInteraction, "query");
|
||||
const results = getOption(interaction, searchInteraction, "results");
|
||||
const query = this.getOption(interaction, "query");
|
||||
const results = this.getOption(interaction, "results");
|
||||
|
||||
return search.callback(interaction, query, [query], {results});
|
||||
};
|
||||
|
@ -717,9 +636,7 @@ color.callback = async function (msg, line, args, {truerandom}) {
|
|||
random = false;
|
||||
|
||||
if (!line || line == "" || args.length == 0) {
|
||||
color = truerandom
|
||||
? tinycolor(Math.floor(Math.random() * 0xffffff))
|
||||
: randomColor();
|
||||
color = truerandom ? tinycolor(Math.floor(Math.random() * 0xffffff)) : randomColor();
|
||||
random = true;
|
||||
}
|
||||
|
||||
|
@ -806,14 +723,13 @@ colorInteraction.options.input = {
|
|||
colorInteraction.options.truerandom = {
|
||||
name: "truerandom",
|
||||
type: ApplicationCommandOptionTypes.BOOLEAN,
|
||||
description:
|
||||
"Should the random color give a 'true random' color instead of an adjust color",
|
||||
description: "Should the random color give a 'true random' color instead of an adjust color",
|
||||
required: false,
|
||||
default: false,
|
||||
};
|
||||
colorInteraction.callback = async function (interaction) {
|
||||
const input = getOption(interaction, colorInteraction, "input");
|
||||
const truerandom = getOption(interaction, colorInteraction, "truerandom");
|
||||
const input = this.getOption(interaction, "input");
|
||||
const truerandom = this.getOption(interaction, "truerandom");
|
||||
|
||||
return color.callback(interaction, input, [input], {truerandom});
|
||||
};
|
||||
|
@ -849,10 +765,7 @@ const handshake = Buffer.concat([
|
|||
identPort,
|
||||
writeVarInt(1),
|
||||
]);
|
||||
const handshakeWithLength = Buffer.concat([
|
||||
writeVarInt(handshake.length),
|
||||
handshake,
|
||||
]);
|
||||
const handshakeWithLength = Buffer.concat([writeVarInt(handshake.length), handshake]);
|
||||
|
||||
const status = Buffer.concat([writeVarInt(1), writeVarInt(0x0)]);
|
||||
|
||||
|
@ -927,12 +840,7 @@ mcserver.callback = async function (msg, line) {
|
|||
}
|
||||
const dataAsString = totalData.toString().trim();
|
||||
console.log(dataAsString);
|
||||
const json = JSON.parse(
|
||||
dataAsString.slice(
|
||||
dataAsString.indexOf("{"),
|
||||
dataAsString.lastIndexOf("}") + 1
|
||||
)
|
||||
);
|
||||
const json = JSON.parse(dataAsString.slice(dataAsString.indexOf("{"), dataAsString.lastIndexOf("}") + 1));
|
||||
logger.verbose("mcserver", "close", json);
|
||||
clearTimeout(timeout);
|
||||
return resolve(json);
|
||||
|
@ -946,22 +854,16 @@ mcserver.callback = async function (msg, line) {
|
|||
} else {
|
||||
await msg.removeReaction("\uD83C\uDFD3");
|
||||
|
||||
const motd = data.description.text.replace(
|
||||
/\u00a7([a-f0-9k-or])/gi,
|
||||
(formatting) => {
|
||||
const ansi = formattingToAnsi[formatting];
|
||||
return ansi ? `\x1b[${ansi}m` : "";
|
||||
}
|
||||
);
|
||||
const motd = data.description.text.replace(/\u00a7([a-f0-9k-or])/gi, (formatting) => {
|
||||
const ansi = formattingToAnsi[formatting];
|
||||
return ansi ? `\x1b[${ansi}m` : "";
|
||||
});
|
||||
|
||||
const players = data.players?.sample?.map((player) => player.name) ?? [];
|
||||
const totalPlayers = `(${data.players.online}/${data.players.max})`;
|
||||
let image;
|
||||
if (data.favicon) {
|
||||
image = Buffer.from(
|
||||
data.favicon.slice(data.favicon.indexOf(",")),
|
||||
"base64"
|
||||
);
|
||||
image = Buffer.from(data.favicon.slice(data.favicon.indexOf(",")), "base64");
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const Command = require("../lib/command.js");
|
||||
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");
|
||||
tidy.addAlias("prune");
|
||||
|
@ -14,16 +15,10 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) {
|
|||
if (!showHelp && !msg.guildID) {
|
||||
return "Can only be used in guilds.";
|
||||
}
|
||||
if (
|
||||
!showHelp &&
|
||||
!msg.channel.permissionsOf(msg.author.id).has("manageMessages")
|
||||
) {
|
||||
if (!showHelp && !msg.channel.permissionsOf(msg.author.id).has("manageMessages")) {
|
||||
return "You do not have `Manage Messages` permission.";
|
||||
}
|
||||
if (
|
||||
!showHelp &&
|
||||
!msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages")
|
||||
) {
|
||||
if (!showHelp && !msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages")) {
|
||||
return "I do not have `Manage Messages` permission.";
|
||||
}
|
||||
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,
|
||||
})
|
||||
.then((msgs) => msgs.map((m) => m.id));
|
||||
await msg.channel.deleteMessages(
|
||||
messages,
|
||||
`Message purge by ${formatUsername(msg.author)}`
|
||||
);
|
||||
await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)}`);
|
||||
|
||||
return `Deleted ${messages.length} message(s).`;
|
||||
}
|
||||
|
@ -53,14 +45,10 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) {
|
|||
before: msg.id,
|
||||
limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10,
|
||||
})
|
||||
.then((msgs) =>
|
||||
msgs.filter((m) => m.author.id == user.id).map((m) => m.id)
|
||||
);
|
||||
.then((msgs) => msgs.filter((m) => m.author.id == user.id).map((m) => m.id));
|
||||
await msg.channel.deleteMessages(
|
||||
messages,
|
||||
`Message purge by ${formatUsername(
|
||||
msg.author
|
||||
)} targeting ${formatUsername(user)}`
|
||||
`Message purge by ${formatUsername(msg.author)} targeting ${formatUsername(user)}`
|
||||
);
|
||||
|
||||
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,
|
||||
})
|
||||
.then((msgs) => msgs.filter((m) => msg.author.bot).map((m) => m.id));
|
||||
await msg.channel.deleteMessages(
|
||||
messages,
|
||||
`Message purge by ${formatUsername(msg.author)} targeting bots`
|
||||
);
|
||||
await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)} targeting bots`);
|
||||
|
||||
return `Deleted ${messages.length} message(s).`;
|
||||
}
|
||||
case "filter": {
|
||||
if (count.length === 0)
|
||||
return "Filter must be at least 1 character long.";
|
||||
if (count.length === 0) return "Filter must be at least 1 character long.";
|
||||
|
||||
const messages = await msg.channel
|
||||
.getMessages({
|
||||
before: msg.id,
|
||||
limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10,
|
||||
})
|
||||
.then((msgs) =>
|
||||
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}"`
|
||||
);
|
||||
.then((msgs) => 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}"`);
|
||||
|
||||
return `Deleted ${messages.length} message(s).`;
|
||||
}
|
||||
|
|
|
@ -4,23 +4,18 @@ const {Readable} = require("node:stream");
|
|||
const ffprobe = require("node-ffprobe");
|
||||
|
||||
const Command = require("../lib/command.js");
|
||||
const {
|
||||
formatTime,
|
||||
parseHtmlEntities,
|
||||
formatUsername,
|
||||
selectionMessage,
|
||||
} = require("../lib/utils.js");
|
||||
|
||||
const {formatUsername} = require("../util/misc.js");
|
||||
const {formatTime} = require("../util/time.js");
|
||||
const {parseHtmlEntities} = require("../util/html.js");
|
||||
const {selectionMessage} = require("../util/selection.js");
|
||||
|
||||
const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
|
||||
const REGEX_YOUTUBE_PLAYLIST =
|
||||
/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/;
|
||||
const REGEX_YOUTUBE_PLAYLIST = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/;
|
||||
const REGEX_YOUTUBE_PLAYLIST_SHORT = /^PL[a-zA-Z0-9-_]{1,32}$/;
|
||||
const REGEX_SOUNDCLOUD =
|
||||
/^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/;
|
||||
const REGEX_SOUNDCLOUD_PLAYLIST =
|
||||
/^((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)$/;
|
||||
const REGEX_SOUNDCLOUD = /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/;
|
||||
const REGEX_SOUNDCLOUD_PLAYLIST = /^((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;
|
||||
|
||||
|
@ -40,9 +35,7 @@ async function getSoundcloudClientID() {
|
|||
return SOUNDCLOUD_CLIENTID;
|
||||
}
|
||||
const page = await fetch("https://soundcloud.com").then((res) => res.text());
|
||||
const scripts = page
|
||||
.match(/<script crossorigin src="(.+?)"><\/script>/g)
|
||||
.reverse();
|
||||
const scripts = page.match(/<script crossorigin src="(.+?)"><\/script>/g).reverse();
|
||||
for (const script of scripts) {
|
||||
const url = script.match(/src="(.+?)"/)[1];
|
||||
const contents = await fetch(url).then((res) => res.text());
|
||||
|
@ -55,37 +48,23 @@ async function getSoundcloudClientID() {
|
|||
return null;
|
||||
}
|
||||
|
||||
async function processPlaylist(
|
||||
url,
|
||||
type,
|
||||
shuffle = false,
|
||||
limit = -1,
|
||||
offset = 0
|
||||
) {
|
||||
async function processPlaylist(url, type, shuffle = false, limit = -1, offset = 0) {
|
||||
let playlist;
|
||||
|
||||
if (type === "yt") {
|
||||
const playlistId =
|
||||
url.match(REGEX_YOUTUBE_PLAYLIST)?.[4] ??
|
||||
url.match(REGEX_YOUTUBE_PLAYLIST_SHORT)?.[0];
|
||||
const playlistId = url.match(REGEX_YOUTUBE_PLAYLIST)?.[4] ?? url.match(REGEX_YOUTUBE_PLAYLIST_SHORT)?.[0];
|
||||
if (!playlistId) return null;
|
||||
|
||||
const baseUrl = "/playlists/" + playlistId;
|
||||
|
||||
const data = await fetch(hf.config.piped_api + baseUrl).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
const data = await fetch(hf.config.piped_api + baseUrl).then((res) => res.json());
|
||||
|
||||
playlist = data.relatedStreams;
|
||||
|
||||
let pageToken = data.nextpage;
|
||||
while (pageToken?.startsWith("{")) {
|
||||
const pageData = await fetch(
|
||||
hf.config.piped_api +
|
||||
"/nextpage" +
|
||||
baseUrl +
|
||||
"&nextpage=" +
|
||||
encodeURIComponent(pageToken)
|
||||
hf.config.piped_api + "/nextpage" + baseUrl + "&nextpage=" + encodeURIComponent(pageToken)
|
||||
).then((res) => res.json());
|
||||
if (pageData.nextpage) pageToken = pageData.nextpage;
|
||||
|
||||
|
@ -100,23 +79,18 @@ async function processPlaylist(
|
|||
).then((res) => res.json());
|
||||
|
||||
while (!userInfo.uri) {
|
||||
userInfo = await fetch(
|
||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
|
||||
).then((res) => res.json());
|
||||
userInfo = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`).then(
|
||||
(res) => res.json()
|
||||
);
|
||||
}
|
||||
|
||||
const likesUrl =
|
||||
userInfo.uri.replace("api.", "api-v2.") +
|
||||
"/likes?limit=500&client_id=" +
|
||||
clientId;
|
||||
const likesUrl = userInfo.uri.replace("api.", "api-v2.") + "/likes?limit=500&client_id=" + clientId;
|
||||
|
||||
let currentLikes = await fetch(likesUrl).then((res) => res.json());
|
||||
playlist = currentLikes.collection;
|
||||
|
||||
while (currentLikes.next_href != null) {
|
||||
currentLikes = await fetch(
|
||||
currentLikes.next_href + "&client_id=" + clientId
|
||||
).then((res) => res.json());
|
||||
currentLikes = await fetch(currentLikes.next_href + "&client_id=" + clientId).then((res) => res.json());
|
||||
playlist = [...playlist, ...currentLikes.collection];
|
||||
}
|
||||
} else if (url.indexOf("/tracks")) {
|
||||
|
@ -125,29 +99,22 @@ async function processPlaylist(
|
|||
).then((res) => res.json());
|
||||
|
||||
while (!userInfo.uri) {
|
||||
userInfo = await fetch(
|
||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
|
||||
).then((res) => res.json());
|
||||
userInfo = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`).then(
|
||||
(res) => res.json()
|
||||
);
|
||||
}
|
||||
|
||||
const tracksUrl =
|
||||
userInfo.uri.replace("api.", "api-v2.") +
|
||||
"/tracks?limit=500&client_id=" +
|
||||
clientId;
|
||||
const tracksUrl = userInfo.uri.replace("api.", "api-v2.") + "/tracks?limit=500&client_id=" + clientId;
|
||||
|
||||
let currentTracks = await fetch(tracksUrl).then((res) => res.json());
|
||||
playlist = currentTracks.collection;
|
||||
|
||||
while (currentTracks.next_href != null) {
|
||||
currentTracks = await fetch(
|
||||
currentTracks.next_href + "&client_id=" + clientId
|
||||
).then((res) => res.json());
|
||||
currentTracks = await fetch(currentTracks.next_href + "&client_id=" + clientId).then((res) => res.json());
|
||||
playlist = [...playlist, ...currentTracks.collection];
|
||||
}
|
||||
} else {
|
||||
playlist = await fetch(
|
||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
|
||||
)
|
||||
playlist = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`)
|
||||
.then((res) => res.json())
|
||||
.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,/;
|
||||
async function enqueue({
|
||||
guild_id,
|
||||
voice_id,
|
||||
text_id,
|
||||
url,
|
||||
type,
|
||||
addedBy,
|
||||
suppress = false,
|
||||
queueNext = false,
|
||||
}) {
|
||||
async function enqueue({guild_id, voice_id, text_id, url, type, addedBy, suppress = false, queueNext = false}) {
|
||||
if (!url) return;
|
||||
|
||||
const connection =
|
||||
voiceStorage.get(guild_id) ??
|
||||
(await createVoiceConnection(guild_id, voice_id, text_id));
|
||||
const connection = voiceStorage.get(guild_id) ?? (await createVoiceConnection(guild_id, voice_id, text_id));
|
||||
const textChannel = hf.bot.guilds.get(guild_id).channels.get(text_id);
|
||||
|
||||
let title,
|
||||
|
@ -245,9 +201,7 @@ async function enqueue({
|
|||
id = uri.searchParams.get("v");
|
||||
}
|
||||
}
|
||||
info = await fetch(`${hf.config.piped_api}/streams/${id}`).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
info = await fetch(`${hf.config.piped_api}/streams/${id}`).then((res) => res.json());
|
||||
} catch (err) {
|
||||
await textChannel.createMessage({
|
||||
content: `:warning: Failed to get metadata: \`\`\`\n${err}\n\`\`\``,
|
||||
|
@ -261,35 +215,23 @@ async function enqueue({
|
|||
const hlsUrl = new URL(info.hls);
|
||||
const hlsBase = await fetch(info.hls)
|
||||
.then((res) => res.text())
|
||||
.then((data) =>
|
||||
data.replaceAll(
|
||||
"/api/manifest/",
|
||||
`https://${hlsUrl.hostname}/api/manifest/`
|
||||
)
|
||||
);
|
||||
.then((data) => data.replaceAll("/api/manifest/", `https://${hlsUrl.hostname}/api/manifest/`));
|
||||
|
||||
media = Readable.from(
|
||||
await fetch(hlsBase.match(REGEX_HLS_AUDIO_TRACK)[1])
|
||||
.then((res) => res.text())
|
||||
.then((data) =>
|
||||
data.replaceAll(
|
||||
"/videoplayback/",
|
||||
`https://${hlsUrl.hostname}/videoplayback/`
|
||||
)
|
||||
)
|
||||
.then((data) => data.replaceAll("/videoplayback/", `https://${hlsUrl.hostname}/videoplayback/`))
|
||||
);
|
||||
} else if (type == "sc") {
|
||||
if (url?.startsWith("sc:"))
|
||||
url = url.replace(/^sc:/, "https://soundcloud.com/");
|
||||
if (url?.startsWith("sc:")) url = url.replace(/^sc:/, "https://soundcloud.com/");
|
||||
const client_id = await getSoundcloudClientID();
|
||||
|
||||
const info = await fetch(
|
||||
`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${client_id}`
|
||||
).then((res) => res.json());
|
||||
const info = await fetch(`https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${client_id}`).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
|
||||
const formatUrl = info.media.transcodings.filter(
|
||||
(obj) => !obj.snipped && obj.format.protocol == "progressive"
|
||||
)[0].url;
|
||||
const formatUrl = info.media.transcodings.filter((obj) => !obj.snipped && obj.format.protocol == "progressive")[0]
|
||||
.url;
|
||||
const streamUrl = await fetch(`${formatUrl}?client_id=${client_id}`)
|
||||
.then((res) => res.json())
|
||||
.then((obj) => obj.url);
|
||||
|
@ -314,11 +256,7 @@ async function enqueue({
|
|||
|
||||
if (info.tags) {
|
||||
title = `${
|
||||
info.tags.artist ??
|
||||
info.tags.ARTIST ??
|
||||
info.tags.album_artist ??
|
||||
info.tags.ALBUM_ARTIST ??
|
||||
"<unknown artist>"
|
||||
info.tags.artist ?? info.tags.ARTIST ?? info.tags.album_artist ?? info.tags.ALBUM_ARTIST ?? "<unknown artist>"
|
||||
} - ${info.tags.title ?? info.tags.TITLE ?? "<no title>"}`;
|
||||
}
|
||||
|
||||
|
@ -347,26 +285,17 @@ async function enqueue({
|
|||
textChannel.createMessage({
|
||||
embeds: [
|
||||
{
|
||||
title: `<:ms_tick:503341995348066313> Added to queue ${
|
||||
queueNext === true ? "(next up)" : ""
|
||||
}`,
|
||||
title: `<:ms_tick:503341995348066313> Added to queue ${queueNext === true ? "(next up)" : ""}`,
|
||||
color: 0x00cc00,
|
||||
fields: [
|
||||
{
|
||||
name: "Title",
|
||||
value: (title !== url ? `[${title}](${url})` : url).substring(
|
||||
0,
|
||||
1024
|
||||
),
|
||||
value: (title !== url ? `[${title}](${url})` : url).substring(0, 1024),
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Length",
|
||||
value: stream
|
||||
? "<continuous>"
|
||||
: length
|
||||
? formatTime(length)
|
||||
: "<unknown>",
|
||||
value: stream ? "<continuous>" : length ? formatTime(length) : "<unknown>",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
|
@ -393,10 +322,7 @@ async function enqueue({
|
|||
await connection.connection.play(media, {
|
||||
inlineVolume: true,
|
||||
voiceDataTimeout: -1,
|
||||
inputArgs: [
|
||||
"-protocol_whitelist",
|
||||
"file,http,https,tcp,tls,pipe,data,crypto",
|
||||
],
|
||||
inputArgs: ["-protocol_whitelist", "file,http,https,tcp,tls,pipe,data,crypto"],
|
||||
});
|
||||
|
||||
textChannel.createMessage({
|
||||
|
@ -407,19 +333,12 @@ async function enqueue({
|
|||
fields: [
|
||||
{
|
||||
name: "Title",
|
||||
value: (title && title != url
|
||||
? `[${title}](${url})`
|
||||
: url
|
||||
).substring(0, 1024),
|
||||
value: (title && title != url ? `[${title}](${url})` : url).substring(0, 1024),
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Length",
|
||||
value: stream
|
||||
? "<continuous>"
|
||||
: length
|
||||
? formatTime(length)
|
||||
: "<unknown>",
|
||||
value: stream ? "<continuous>" : length ? formatTime(length) : "<unknown>",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
|
@ -448,20 +367,17 @@ async function enqueue({
|
|||
}
|
||||
|
||||
async function youtubeSearch(msg, str) {
|
||||
const {items} = await fetch(
|
||||
`${hf.config.piped_api}/search?q=${encodeURIComponent(str)}&filter=videos`
|
||||
).then((x) => x.json());
|
||||
const {items} = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(str)}&filter=videos`).then((x) =>
|
||||
x.json()
|
||||
);
|
||||
|
||||
const selection = items.map((item) => ({
|
||||
value: "https://youtube.com" + item.url,
|
||||
key: item.url.replace("/watch?v=", ""),
|
||||
display: `${parseHtmlEntities(item.title).substring(0, 99)}${
|
||||
parseHtmlEntities(item.title).length > 99 ? "…" : ""
|
||||
display: `${parseHtmlEntities(item.title).substring(0, 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 {
|
||||
|
@ -478,12 +394,7 @@ command.addAlias("m");
|
|||
command.category = "misc";
|
||||
command.helpText = "Music";
|
||||
command.usage = "help";
|
||||
command.callback = async function (
|
||||
msg,
|
||||
line,
|
||||
args,
|
||||
{shuffle = false, limit = -1, offset = 0, next = false}
|
||||
) {
|
||||
command.callback = async function (msg, line, args, {shuffle = false, limit = -1, offset = 0, next = false}) {
|
||||
if (!msg.guildID) return "This command can only be used in guilds.";
|
||||
|
||||
const subcommand = args.shift();
|
||||
|
@ -503,10 +414,7 @@ command.callback = async function (
|
|||
let type;
|
||||
let playlist = false;
|
||||
|
||||
if (
|
||||
REGEX_YOUTUBE_PLAYLIST.test(argStr) ||
|
||||
REGEX_YOUTUBE_PLAYLIST_SHORT.test(argStr)
|
||||
) {
|
||||
if (REGEX_YOUTUBE_PLAYLIST.test(argStr) || REGEX_YOUTUBE_PLAYLIST_SHORT.test(argStr)) {
|
||||
type = "yt";
|
||||
playlist = true;
|
||||
} else if (REGEX_SOUNDCLOUD_PLAYLIST.test(argStr)) {
|
||||
|
@ -533,25 +441,17 @@ command.callback = async function (
|
|||
const statusMessage = await msg.channel.createMessage({
|
||||
embeds: [
|
||||
{
|
||||
title:
|
||||
"<a:loading:493087964918972426> Processing playlist...",
|
||||
title: "<a:loading:493087964918972426> Processing playlist...",
|
||||
description: `Fetching tracks...`,
|
||||
color: 0xcc0088,
|
||||
},
|
||||
],
|
||||
});
|
||||
const playlist = await processPlaylist(
|
||||
argStr,
|
||||
type,
|
||||
shuffle,
|
||||
limit,
|
||||
offset
|
||||
);
|
||||
const playlist = await processPlaylist(argStr, type, shuffle, limit, offset);
|
||||
await statusMessage.edit({
|
||||
embeds: [
|
||||
{
|
||||
title:
|
||||
"<a:loading:493087964918972426> Processing playlist...",
|
||||
title: "<a:loading:493087964918972426> Processing playlist...",
|
||||
description: `${playlist.length} tracks`,
|
||||
color: 0xcc0088,
|
||||
},
|
||||
|
@ -562,9 +462,7 @@ command.callback = async function (
|
|||
if (type == "yt") {
|
||||
url = "https://youtube.com" + track.url;
|
||||
} else if (type == "sc") {
|
||||
url = track.track
|
||||
? track.track.permalink_url
|
||||
: track.permalink_url;
|
||||
url = track.track ? track.track.permalink_url : track.permalink_url;
|
||||
}
|
||||
|
||||
await enqueue({
|
||||
|
@ -599,20 +497,13 @@ command.callback = async function (
|
|||
}
|
||||
} else {
|
||||
if (argStr.match(/^https?:\/\//)) {
|
||||
let contentType = await fetch(argStr, {method: "HEAD"}).then(
|
||||
(res) => res.headers.get("Content-Type")
|
||||
);
|
||||
let contentType = await fetch(argStr, {method: "HEAD"}).then((res) => res.headers.get("Content-Type"));
|
||||
|
||||
if (!contentType) {
|
||||
contentType = await fetch(argStr, {method: "GET"}).then((res) =>
|
||||
res.headers.get("Content-Type")
|
||||
);
|
||||
contentType = await fetch(argStr, {method: "GET"}).then((res) => res.headers.get("Content-Type"));
|
||||
}
|
||||
|
||||
if (
|
||||
contentType.startsWith("audio/") ||
|
||||
contentType.startsWith("video/")
|
||||
) {
|
||||
if (contentType.startsWith("audio/") || contentType.startsWith("video/")) {
|
||||
await enqueue({
|
||||
guild_id: msg.guildID,
|
||||
voice_id: msg.member.voiceState.channelID,
|
||||
|
@ -684,30 +575,22 @@ command.callback = async function (
|
|||
return "You are not in a voice channel.";
|
||||
}
|
||||
case "np": {
|
||||
if (!voiceStorage.has(msg.guildID))
|
||||
return "The bot is not in a voice channel.";
|
||||
if (!voiceStorage.has(msg.guildID)) return "The bot is not in a voice channel.";
|
||||
|
||||
const connection = voiceStorage.get(msg.guildID);
|
||||
const nowPlaying = connection.nowplaying;
|
||||
if (!nowPlaying || !connection.connection.playing)
|
||||
return "Nothing is currently playing.";
|
||||
if (!nowPlaying || !connection.connection.playing) return "Nothing is currently playing.";
|
||||
|
||||
const position = Date.now() - nowPlaying.start;
|
||||
|
||||
const timeEnd =
|
||||
nowPlaying.length == 0 ? "\u221e" : formatTime(nowPlaying.length);
|
||||
const timeEnd = nowPlaying.length == 0 ? "\u221e" : formatTime(nowPlaying.length);
|
||||
const timePos = formatTime(position);
|
||||
|
||||
const progress =
|
||||
nowPlaying.length == 0 ? 1 : position / nowPlaying.length;
|
||||
const progress = nowPlaying.length == 0 ? 1 : position / nowPlaying.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}\``;
|
||||
const bar = `\`[${"=".repeat(barLength)}${" ".repeat(NOWPLAYING_BAR_LENGTH - barLength)}]\``;
|
||||
const time = `\`${timePos}${" ".repeat(NOWPLAYING_BAR_LENGTH + 2 - timePos.length - timeEnd.length)}${timeEnd}\``;
|
||||
|
||||
return {
|
||||
embed: {
|
||||
|
@ -738,8 +621,7 @@ command.callback = async function (
|
|||
}
|
||||
case "queue":
|
||||
case "q": {
|
||||
if (!voiceStorage.has(msg.guildID))
|
||||
return "The bot is not in a voice channel";
|
||||
if (!voiceStorage.has(msg.guildID)) return "The bot is not in a voice channel";
|
||||
|
||||
const connection = voiceStorage.get(msg.guildID);
|
||||
const queue = connection.queue;
|
||||
|
@ -754,11 +636,9 @@ command.callback = async function (
|
|||
const item = queue[index];
|
||||
fields.push({
|
||||
name: item.title ?? item.url,
|
||||
value: `${item.title ? `[Link](${item.url}) - ` : ""}${formatTime(
|
||||
item.length
|
||||
)}\nAdded by: <@${item.addedBy}>\n<t:${Math.floor(
|
||||
nextTrack / 1000
|
||||
)}:R>`,
|
||||
value: `${item.title ? `[Link](${item.url}) - ` : ""}${formatTime(item.length)}\nAdded by: <@${
|
||||
item.addedBy
|
||||
}>\n<t:${Math.floor(nextTrack / 1000)}:R>`,
|
||||
inline: true,
|
||||
});
|
||||
nextTrack += item.length;
|
||||
|
@ -796,8 +676,7 @@ command.callback = async function (
|
|||
if (queue.length === 0) return "Nothing else is currently queued";
|
||||
|
||||
const hasManageMessages = msg.member.permissions.has("manageMessages");
|
||||
if (!hasManageMessages)
|
||||
queue = queue.filter((item) => item.addedBy == msg.member.id);
|
||||
if (!hasManageMessages) queue = queue.filter((item) => item.addedBy == msg.member.id);
|
||||
|
||||
if (queue.length === 0) return "You currently have nothing queued";
|
||||
|
||||
|
@ -809,9 +688,7 @@ command.callback = async function (
|
|||
return {
|
||||
key: item.id,
|
||||
display: (item.title ?? item.url).substr(0, 100),
|
||||
description: hasManageMessages
|
||||
? `Added by: ${formatUsername(user)}`
|
||||
: "",
|
||||
description: hasManageMessages ? `Added by: ${formatUsername(user)}` : "",
|
||||
};
|
||||
}),
|
||||
30000,
|
||||
|
@ -819,9 +696,7 @@ command.callback = async function (
|
|||
);
|
||||
|
||||
if (Array.isArray(toRemove)) {
|
||||
connection.queue = connection.queue.filter(
|
||||
(item) => !toRemove.includes(item.id)
|
||||
);
|
||||
connection.queue = connection.queue.filter((item) => !toRemove.includes(item.id));
|
||||
return `Removed ${toRemove.length} item(s).`;
|
||||
} else {
|
||||
return toRemove;
|
||||
|
|
|
@ -16,18 +16,12 @@ const {resolve} = require("path");
|
|||
const timer = require("../lib/timer");
|
||||
const logger = require("../lib/logger");
|
||||
|
||||
if (!fs.existsSync(resolve(__dirname, "..", "..", "private_reminders.json")))
|
||||
return;
|
||||
if (!fs.existsSync(resolve(__dirname, "..", "..", "private_reminders.json"))) return;
|
||||
|
||||
const tzFormatterCache = {};
|
||||
const dmCache = {};
|
||||
|
||||
const reminderData = require(resolve(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"private_reminders.json"
|
||||
));
|
||||
const reminderData = require(resolve(__dirname, "..", "..", "private_reminders.json"));
|
||||
|
||||
hf.database.run(
|
||||
"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);
|
||||
|
||||
if (date != lastRan && hour == data.hour && minutes == 0) {
|
||||
logger.verbose(
|
||||
"privateReminders",
|
||||
`attempting to send reminder to ${data.user}`
|
||||
);
|
||||
logger.verbose("privateReminders", `attempting to send reminder to ${data.user}`);
|
||||
if (channel != null) {
|
||||
await channel.createMessage({
|
||||
content: ":alarm_clock: " + data.message,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Command = require("../lib/command.js");
|
||||
|
||||
const {selectionMessage} = require("../lib/utils.js");
|
||||
const {selectionMessage} = require("../util/selection.js");
|
||||
|
||||
hf.database.run(
|
||||
"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);
|
||||
|
||||
try {
|
||||
return await selectionMessage(
|
||||
msg,
|
||||
"Multiple roles found, please pick from this list:",
|
||||
selection
|
||||
);
|
||||
return await selectionMessage(msg, "Multiple roles found, please pick from this list:", selection);
|
||||
} catch (out) {
|
||||
return out;
|
||||
}
|
||||
|
@ -129,9 +125,7 @@ roleme.callback = async function (msg, line, args) {
|
|||
|
||||
const roles = await getRoles(msg.guildID);
|
||||
|
||||
const role = await lookupRole(msg, argStr, (role) =>
|
||||
roles.includes(role.id)
|
||||
);
|
||||
const role = await lookupRole(msg, argStr, (role) => roles.includes(role.id));
|
||||
if (role === "No results") return role;
|
||||
|
||||
if (!roles.includes(role.id)) {
|
||||
|
@ -169,9 +163,7 @@ roleme.callback = async function (msg, line, args) {
|
|||
|
||||
const roles = await getRoles(msg.guildID);
|
||||
|
||||
const role = await lookupRole(msg, argStr, (role) =>
|
||||
roles.includes(role.id)
|
||||
);
|
||||
const role = await lookupRole(msg, argStr, (role) => roles.includes(role.id));
|
||||
if (role === "No results") return role;
|
||||
|
||||
if (!roles.includes(role.id)) {
|
||||
|
@ -225,11 +217,7 @@ roleme.callback = async function (msg, line, args) {
|
|||
|
||||
const roles = await getRoles(msg.guildID);
|
||||
|
||||
const role = await lookupRole(
|
||||
msg,
|
||||
(subcommand + " " + argStr).trim(),
|
||||
(role) => roles.includes(role.id)
|
||||
);
|
||||
const role = await lookupRole(msg, (subcommand + " " + argStr).trim(), (role) => roles.includes(role.id));
|
||||
if (role === "No results") return role;
|
||||
|
||||
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) {
|
||||
this._rows = [titles];
|
||||
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));
|
||||
|
||||
return codes.map((code) => [
|
||||
code.padStart(4, "0"),
|
||||
global.____unicode_data[code.padStart(4, "0")],
|
||||
]);
|
||||
return codes.map((code) => {
|
||||
const padded = code.padStart(4, "0");
|
||||
return [padded, global.____unicode_data[padded]];
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
Loading…
Reference in a new issue