2021-08-12 19:32:06 +00:00
|
|
|
const Command = require("../lib/command.js");
|
2022-12-03 06:11:16 +00:00
|
|
|
const events = require("../lib/events.js");
|
|
|
|
|
2021-08-12 19:32:06 +00:00
|
|
|
const CATEGORY = "misc";
|
2022-12-03 06:11:16 +00:00
|
|
|
const FOXWELLS_GUILD_ID = "300436792916836352";
|
2021-08-12 19:32:06 +00:00
|
|
|
|
2022-05-22 19:21:09 +00:00
|
|
|
const {tinycolor} = require("@ctrl/tinycolor");
|
2022-12-03 19:29:26 +00:00
|
|
|
const {pastelize} = require("../lib/utils.js");
|
2021-08-12 19:32:06 +00:00
|
|
|
|
2022-06-22 17:10:24 +00:00
|
|
|
const logger = require("../lib/logger.js");
|
2022-06-22 17:07:27 +00:00
|
|
|
|
2021-08-12 19:32:06 +00:00
|
|
|
// 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
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// im making it "hard" to get cause im bored :^)
|
|
|
|
const LIGHTS_URL = "nUE0pUZ6Yl9hqJAfMJSlYaI0p3Ibol5lo2Aepl9fnJqbqN==";
|
|
|
|
const HEX_REGEX = /^#?([0-9a-fA-F]{1,2})([0-9a-fA-F]{1,2})([0-9a-fA-F]{1,2})$/;
|
|
|
|
|
|
|
|
let cachedLightsURL; // saving compute time :^)
|
|
|
|
|
|
|
|
const utsuholights = new Command("utsuholights");
|
|
|
|
utsuholights.category = CATEGORY;
|
|
|
|
utsuholights.helpText = "Utsuho Lights";
|
|
|
|
utsuholights.usage = "<hex> [brightness]";
|
2022-11-30 01:15:41 +00:00
|
|
|
utsuholights.callback = async function (msg, line, [hex, bri]) {
|
2021-08-12 19:32:06 +00:00
|
|
|
if (!hex) {
|
|
|
|
return "Hex color required.";
|
|
|
|
}
|
|
|
|
if (!HEX_REGEX.test(hex)) {
|
|
|
|
return "Could not determine hex color.";
|
|
|
|
}
|
|
|
|
|
2022-05-22 19:21:09 +00:00
|
|
|
const {r, g, b} = tinycolor(hex).toRgb();
|
2021-08-12 19:32:06 +00:00
|
|
|
|
|
|
|
if (!cachedLightsURL) {
|
|
|
|
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}` : ""}`
|
|
|
|
);
|
|
|
|
|
|
|
|
if (response.status == 200) {
|
|
|
|
return {reaction: "\uD83D\uDC4C"};
|
|
|
|
} else {
|
|
|
|
return `:warning: An error occurred: \`${await response.text()}\``;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
hf.registerCommand(utsuholights);
|
2022-05-27 03:44:06 +00:00
|
|
|
|
2022-05-27 04:58:31 +00:00
|
|
|
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=", "--");
|
|
|
|
let buffer = Buffer.alloc(0);
|
|
|
|
|
|
|
|
let searchForNextBoundary = false;
|
|
|
|
return await new Promise((resolve) => {
|
|
|
|
res.body.on("data", function (data) {
|
|
|
|
if (!searchForNextBoundary) {
|
|
|
|
if (data.toString().startsWith(boundary)) {
|
|
|
|
buffer = Buffer.concat([buffer, data]);
|
|
|
|
searchForNextBoundary = true;
|
|
|
|
}
|
|
|
|
} else if (searchForNextBoundary) {
|
|
|
|
if (data.toString().startsWith(boundary)) {
|
|
|
|
res.body.end();
|
|
|
|
const length = Number(
|
|
|
|
buffer.toString().match(/Content-Length:.*?(\d+)/)[1]
|
|
|
|
);
|
|
|
|
const headerOffset = buffer.indexOf(JPEG_HEADER);
|
|
|
|
const data = buffer.slice(headerOffset, headerOffset + length);
|
|
|
|
|
|
|
|
resolve(data);
|
|
|
|
} else {
|
|
|
|
buffer = Buffer.concat([buffer, data]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-05-27 03:44:06 +00:00
|
|
|
const plant = new Command("plant");
|
|
|
|
plant.category = CATEGORY;
|
|
|
|
plant.helpText = "Plant cam";
|
|
|
|
plant.callback = async function () {
|
|
|
|
try {
|
2022-05-27 04:58:31 +00:00
|
|
|
return {file: {file: await fetchPlant(), name: "plant.jpg"}};
|
2022-05-27 03:44:06 +00:00
|
|
|
} catch (err) {
|
2022-06-22 17:07:27 +00:00
|
|
|
logger.error("hf:cmd:plant", err);
|
2022-05-27 03:44:06 +00:00
|
|
|
return "<:trollhollow:851301241417498704> where plant (Encountered an error getting plant cam)";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
hf.registerCommand(plant);
|
2022-12-03 06:11:16 +00:00
|
|
|
|
|
|
|
/* vinboard */
|
|
|
|
const VINBOARD_CHANNEL_ID = "770879461871714324";
|
|
|
|
const VINBOARD_THREAD_ID = "1048462330201124935";
|
|
|
|
const VINBOARD_WEBHOOK_ID = "1048471543287660665";
|
|
|
|
|
|
|
|
hf.database.run(
|
2022-12-03 17:17:09 +00:00
|
|
|
"CREATE TABLE IF NOT EXISTS vinboard (message_id TEXT NOT NULL PRIMARY KEY, count INTEGER NOT NULL, board_id TEXT NOT NULL) WITHOUT ROWID"
|
2022-12-03 06:11:16 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
function getBoardEntry(id) {
|
|
|
|
return new Promise((resolve, reject) => {
|
2022-12-03 17:17:09 +00:00
|
|
|
hf.database.get(
|
2022-12-03 07:21:39 +00:00
|
|
|
"SELECT message_id,count,board_id FROM vinboard WHERE message_id = $id",
|
2022-12-03 06:11:16 +00:00
|
|
|
{
|
|
|
|
$id: id,
|
|
|
|
},
|
2022-12-03 17:17:09 +00:00
|
|
|
(err, row) => {
|
2022-12-03 06:11:16 +00:00
|
|
|
if (err == null) {
|
2022-12-03 07:20:16 +00:00
|
|
|
resolve(row);
|
2022-12-03 06:11:16 +00:00
|
|
|
} else {
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function setBoardEntry(id, count, board_id) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
hf.database.run(
|
|
|
|
"REPLACE INTO vinboard VALUES ($id,$count,$board_id)",
|
|
|
|
{
|
|
|
|
$id: id,
|
|
|
|
$count: count,
|
|
|
|
$board_id: board_id,
|
|
|
|
},
|
|
|
|
(err) => {
|
|
|
|
if (err == null) {
|
|
|
|
resolve(true);
|
|
|
|
} else {
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function deleteBoardEntry(id) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
hf.database.run(
|
|
|
|
"DELETE FROM vinboard WHERE message_id = $id",
|
|
|
|
{
|
|
|
|
$id: id,
|
|
|
|
},
|
|
|
|
(err) => {
|
|
|
|
if (err == null) {
|
|
|
|
resolve(true);
|
|
|
|
} else {
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function findSuitableImage(msg) {
|
|
|
|
const out = {};
|
|
|
|
|
|
|
|
const attachments = [...msg.attachments.values()];
|
|
|
|
const attachment = attachments[0];
|
|
|
|
|
|
|
|
if (attachment) {
|
|
|
|
const url = attachment.url;
|
|
|
|
if (/(jpe?g|png|gif|webp)$/.test(url)) {
|
|
|
|
out.url = url;
|
|
|
|
} else if (/(mp4|webm|mov)$/.test(url)) {
|
|
|
|
out.video = true;
|
|
|
|
out.attachment = true;
|
|
|
|
out.url = "attachment://thumb.jpg";
|
2022-12-03 06:21:23 +00:00
|
|
|
out.file = await fetch(attachment.proxyURL + "?format=jpeg")
|
2022-12-03 06:11:16 +00:00
|
|
|
.then((res) => res.arrayBuffer())
|
|
|
|
.then((buf) => Buffer.from(buf));
|
|
|
|
}
|
2022-12-03 06:27:42 +00:00
|
|
|
} else {
|
|
|
|
for (const embed of msg.embeds) {
|
|
|
|
if (!embed.url) continue;
|
2022-12-03 19:47:28 +00:00
|
|
|
if (embed.url && /(jpe?g|png|gif|webp)$/.test(embed.url)) {
|
2022-12-03 19:46:04 +00:00
|
|
|
out.url = embed.url;
|
|
|
|
} else if (embed.image) {
|
2022-12-03 06:27:42 +00:00
|
|
|
out.url = embed.image.url;
|
|
|
|
break;
|
|
|
|
} else if (embed.video) {
|
|
|
|
out.video = true;
|
|
|
|
out.url = "attachment://thumb.jpg";
|
|
|
|
out.file = await fetch(embed.video.proxyURL + "?format=jpeg")
|
|
|
|
.then((res) => res.arrayBuffer())
|
|
|
|
.then((buf) => Buffer.from(buf));
|
|
|
|
break;
|
|
|
|
}
|
2022-12-03 06:11:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2022-12-03 07:02:54 +00:00
|
|
|
async function createBoardMessage(msg, count, fetchAttachment = true) {
|
2022-12-03 06:11:16 +00:00
|
|
|
const embed = {
|
|
|
|
title: `${count} \u2b50`,
|
2022-12-03 19:29:26 +00:00
|
|
|
color: pastelize(msg.author.username),
|
2022-12-03 06:11:16 +00:00
|
|
|
description: msg.content,
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: "Jump Link",
|
|
|
|
value: `[Jump](${msg.jumpLink})`,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
timestamp: msg.timestamp,
|
|
|
|
};
|
|
|
|
|
2022-12-03 07:02:54 +00:00
|
|
|
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,
|
|
|
|
};
|
2022-12-03 06:11:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2022-12-03 06:24:35 +00:00
|
|
|
avatarURL:
|
|
|
|
msg.member?.avatarURL("png", 256) ?? msg.author.avatarURL("png", 256),
|
|
|
|
username: msg.member?.displayName ?? msg.author.username,
|
2022-12-03 06:11:16 +00:00
|
|
|
threadID: VINBOARD_THREAD_ID,
|
|
|
|
embeds: [embed],
|
2022-12-03 07:02:54 +00:00
|
|
|
files: image?.file ? [{contents: image.file, name: "thumb.jpg"}] : null,
|
2022-12-03 06:32:51 +00:00
|
|
|
wait: true,
|
2022-12-03 06:11:16 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let vinboard_webhook;
|
2022-12-03 06:17:44 +00:00
|
|
|
let vin_channel;
|
2022-12-03 06:42:42 +00:00
|
|
|
let board_channel;
|
2022-12-03 06:17:44 +00:00
|
|
|
async function processReaction(_msg, user, reaction) {
|
|
|
|
if (_msg.guildID != FOXWELLS_GUILD_ID) return;
|
|
|
|
if (_msg.channel.id != VINBOARD_CHANNEL_ID) return;
|
2022-12-03 06:11:16 +00:00
|
|
|
if (user.bot) return;
|
|
|
|
if (reaction.name != "\u2b50") return;
|
|
|
|
|
2022-12-03 06:17:44 +00:00
|
|
|
if (!vin_channel) {
|
|
|
|
vin_channel = hf.bot.guilds
|
|
|
|
.get(FOXWELLS_GUILD_ID)
|
|
|
|
.channels.get(VINBOARD_CHANNEL_ID);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vin_channel) {
|
|
|
|
logger.error("vinboard", "Failed to get channel.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-03 06:42:42 +00:00
|
|
|
if (!board_channel) {
|
2022-12-03 06:45:13 +00:00
|
|
|
board_channel = hf.bot.guilds
|
|
|
|
.get(FOXWELLS_GUILD_ID)
|
|
|
|
.threads.get(VINBOARD_THREAD_ID);
|
2022-12-03 06:42:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!board_channel) {
|
|
|
|
logger.error("vinboard", "Failed to get thread.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-03 06:17:44 +00:00
|
|
|
const msg =
|
|
|
|
vin_channel.messages.get(_msg.id) ??
|
|
|
|
(await vin_channel.getMessage(_msg.id));
|
|
|
|
|
2022-12-03 06:11:16 +00:00
|
|
|
if (!vinboard_webhook) {
|
2022-12-03 06:17:44 +00:00
|
|
|
const webhooks = await vin_channel.getWebhooks();
|
2022-12-03 06:11:16 +00:00
|
|
|
vinboard_webhook = webhooks.find(
|
|
|
|
(webhook) => webhook.id == VINBOARD_WEBHOOK_ID
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vinboard_webhook) {
|
|
|
|
logger.error("vinboard", "Failed to get webhook.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const reacts = await msg.getReactions("\u2b50");
|
|
|
|
const trueCount = reacts.filter(
|
|
|
|
(reactor) => reactor.id != msg.author.id
|
|
|
|
).length;
|
|
|
|
|
2022-12-03 06:48:09 +00:00
|
|
|
const dbEntry = await getBoardEntry(msg.id);
|
2022-12-03 06:11:16 +00:00
|
|
|
if (dbEntry) {
|
|
|
|
if (trueCount == 0) {
|
|
|
|
logger.verbose("vinboard", `Deleting entry for "${msg.id}"`);
|
|
|
|
if (dbEntry.board_id) {
|
2022-12-03 06:14:37 +00:00
|
|
|
await vinboard_webhook.deleteMessage(
|
2022-12-03 06:11:16 +00:00
|
|
|
dbEntry.board_id,
|
|
|
|
"[Vinboard] Message has 0 reactions now."
|
|
|
|
);
|
|
|
|
await deleteBoardEntry(msg.id);
|
|
|
|
}
|
2022-12-03 06:55:08 +00:00
|
|
|
} else if (dbEntry.board_id) {
|
|
|
|
const _boardMessage =
|
|
|
|
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})`
|
|
|
|
);
|
2022-12-03 06:57:29 +00:00
|
|
|
|
2022-12-03 17:27:36 +00:00
|
|
|
const props = {
|
|
|
|
avatarURL: _boardMessage.author.avatarURL("png", 256),
|
|
|
|
username: _boardMessage.author.username,
|
|
|
|
threadID: VINBOARD_THREAD_ID,
|
|
|
|
embeds: _boardMessage.embeds,
|
|
|
|
wait: true,
|
|
|
|
};
|
2022-12-03 17:22:14 +00:00
|
|
|
props.attachments = [..._boardMessage.attachments.values()].map(
|
|
|
|
(attach) => ({id: attach.id})
|
|
|
|
);
|
2022-12-03 17:27:36 +00:00
|
|
|
props.embeds[0].title = `${trueCount} \u2b50`;
|
2022-12-03 19:29:26 +00:00
|
|
|
props.embeds[0].color = pastelize(msg.author.username);
|
2022-12-03 06:57:29 +00:00
|
|
|
await vinboard_webhook.editMessage(_boardMessage.id, props);
|
2022-12-03 06:55:08 +00:00
|
|
|
await setBoardEntry(msg.id, trueCount, _boardMessage.id);
|
|
|
|
} else {
|
|
|
|
logger.verbose("vinboard", `Creating entry for "${msg.id}"`);
|
|
|
|
const boardMessage = await vinboard_webhook.execute(
|
|
|
|
await createBoardMessage(msg, trueCount)
|
|
|
|
);
|
|
|
|
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
2022-12-03 06:11:16 +00:00
|
|
|
}
|
2022-12-03 07:02:54 +00:00
|
|
|
} else {
|
|
|
|
logger.verbose("vinboard", `Creating entry for "${msg.id}"`);
|
|
|
|
const boardMessage = await vinboard_webhook.execute(
|
|
|
|
await createBoardMessage(msg, trueCount)
|
|
|
|
);
|
|
|
|
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
2022-12-03 06:11:16 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logger.verbose("vinboard", `Creating entry for "${msg.id}"`);
|
2022-12-03 06:14:37 +00:00
|
|
|
const boardMessage = await vinboard_webhook.execute(
|
2022-12-03 06:11:16 +00:00
|
|
|
await createBoardMessage(msg, trueCount)
|
|
|
|
);
|
|
|
|
await setBoardEntry(msg.id, trueCount, boardMessage.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
events.add("messageReactionAdd", "vinboard", processReaction);
|
|
|
|
events.add("messageReactionRemove", "vinboard", processReaction);
|