revamp booru
This commit is contained in:
parent
7c741f88d5
commit
ff2e2ea587
8 changed files with 231 additions and 38 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -72,8 +72,7 @@ web_modules/
|
||||||
# Yarn Integrity file
|
# Yarn Integrity file
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
|
|
||||||
# dotenv environment variable files and credentials
|
# dotenv environment variable files
|
||||||
credentials.json
|
|
||||||
.env
|
.env
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
|
@ -130,5 +129,9 @@ dist
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# configs
|
||||||
|
credentials.json
|
||||||
|
emojis.json
|
||||||
|
|
||||||
# database
|
# database
|
||||||
data.db
|
data.db
|
16
config/emojis.example.json
Normal file
16
config/emojis.example.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"booru": {
|
||||||
|
"rating": {
|
||||||
|
"safe": "<:rating_safe:1293819920978804829>",
|
||||||
|
"general": "<:rating_general:1293819929199513610>",
|
||||||
|
"questionable": "<:rating_questionable:1293819907099725925>",
|
||||||
|
"explicit": "<:rating_explicit:1293819893795389491>",
|
||||||
|
"unknown": "<:rating_unknown:1293819936845594665>"
|
||||||
|
},
|
||||||
|
"score": {
|
||||||
|
"green_arrow_up": "<:green_arrow_up:1293819944399667222>",
|
||||||
|
"red_arrow_down": "<:red_arrow_down:1293819951764869181>",
|
||||||
|
"yellow_tilde": "<:yellow_tilde:1293819958643396608>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -102,4 +102,4 @@ module.exports = {
|
||||||
await interaction.respond(choices.map(choice => ({ name: choice, value: choice })))
|
await interaction.respond(choices.map(choice => ({ name: choice, value: choice })))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -3,7 +3,7 @@ const { generateImageUrl } = require('@imgproxy/imgproxy-node');
|
||||||
const { stringify } = require("node:querystring");
|
const { stringify } = require("node:querystring");
|
||||||
const { readFileSync } = require("node:fs");
|
const { readFileSync } = require("node:fs");
|
||||||
const { decode } = require("html-entities");
|
const { decode } = require("html-entities");
|
||||||
const { extname } = require("node:path");
|
const { extname, basename } = require("node:path");
|
||||||
const { knex } = require("../../db.js");
|
const { knex } = require("../../db.js");
|
||||||
const Booru = require("@himeka/booru");
|
const Booru = require("@himeka/booru");
|
||||||
|
|
||||||
|
@ -63,11 +63,11 @@ function notEmpty(str) {
|
||||||
return str.trim() !== ''
|
return str.trim() !== ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const regexCutTags = /[\S\s]{1,75}[^,]{0,25}/;
|
const regexCutTags = /[\S\s]{1,75}[^,]{0,125}/;
|
||||||
function formatTags(tags) {
|
function formatTags(tags) {
|
||||||
const tagString = decode(tags.join(', '));
|
const tagString = decode(tags.join(', '));
|
||||||
|
|
||||||
if (tagString.length < 100) {
|
if (tagString.length < 500) {
|
||||||
return tagString;
|
return tagString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,12 +75,15 @@ function formatTags(tags) {
|
||||||
return `${escapeMarkdown(tagCutMatch[0] ?? '')}, ...`;
|
return `${escapeMarkdown(tagCutMatch[0] ?? '')}, ...`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var credentials = JSON.parse(readFileSync("config/credentials.json"));
|
||||||
|
var emojis = JSON.parse(readFileSync("config/emojis.json"));
|
||||||
|
|
||||||
const ratingEmojis = {
|
const ratingEmojis = {
|
||||||
s: '<:rating_safe:1293819920978804829>',
|
s: emojis.booru.rating.safe,
|
||||||
g: '<:rating_general:1293819929199513610>',
|
g: emojis.booru.rating.general,
|
||||||
q: '<:rating_questionable:1293819907099725925>',
|
q: emojis.booru.rating.questionable,
|
||||||
e: '<:rating_explicit:1293819893795389491>',
|
e: emojis.booru.rating.explicit,
|
||||||
u: '<:rating_unknown:1293819936845594665>',
|
u: emojis.booru.rating.unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatRating(rating) {
|
function formatRating(rating) {
|
||||||
|
@ -89,11 +92,11 @@ function formatRating(rating) {
|
||||||
|
|
||||||
function formatScore(score) {
|
function formatScore(score) {
|
||||||
if (score > 0) {
|
if (score > 0) {
|
||||||
return `<:green_arrow_up:1293819944399667222> ${score}`
|
return `${emojis.booru.score.green_arrow_up} ${score}`
|
||||||
} else if (score < 0) {
|
} else if (score < 0) {
|
||||||
return `<:red_arrow_down:1293819951764869181> ${score}`
|
return `${emojis.booru.score.red_arrow_down} ${score}`
|
||||||
} else {
|
} else {
|
||||||
return `<:yellow_tilde:1293819958643396608> ${score}`
|
return `${emojis.booru.score.yellow_tilde} ${score}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +109,6 @@ const blacklist = [
|
||||||
"ai_art"
|
"ai_art"
|
||||||
];
|
];
|
||||||
|
|
||||||
var credentials = JSON.parse(readFileSync("credentials.json"));
|
|
||||||
|
|
||||||
function proxy(url) {
|
function proxy(url) {
|
||||||
if (!process.env.IMGPROXY_HOST)
|
if (!process.env.IMGPROXY_HOST)
|
||||||
return url;
|
return url;
|
||||||
|
@ -142,10 +143,12 @@ module.exports = {
|
||||||
if (!result)
|
if (!result)
|
||||||
result = { blacklist: '' };
|
result = { blacklist: '' };
|
||||||
|
|
||||||
const userBlacklist = (result.blacklist ?? "").trim().split(" ");
|
const userBlacklist = (result.blacklist ?? "").trim().split(" ").filter(notEmpty);
|
||||||
const searchTags = [rating, ...tags, ...[...blacklist, ...userBlacklist].map(i => "-" + i)];
|
const searchTags = [rating, ...tags, ...[...blacklist, ...userBlacklist].map(i => "-" + i)];
|
||||||
const startTime = process.hrtime.bigint();
|
const startTime = process.hrtime.bigint();
|
||||||
|
|
||||||
|
console.log(searchTags);
|
||||||
|
|
||||||
var post = (await Booru.search(booru, searchTags, { limit: 1, random: true, credentials: credentials[booru] ?? null }))[0];
|
var post = (await Booru.search(booru, searchTags, { limit: 1, random: true, credentials: credentials[booru] ?? null }))[0];
|
||||||
if (post == null) {
|
if (post == null) {
|
||||||
await interaction.followUp("<:warning:1293874152150667315> Could not find any post matching tags.");
|
await interaction.followUp("<:warning:1293874152150667315> Could not find any post matching tags.");
|
||||||
|
@ -155,12 +158,13 @@ module.exports = {
|
||||||
const endTime = process.hrtime.bigint();
|
const endTime = process.hrtime.bigint();
|
||||||
const timeTaken = endTime - startTime;
|
const timeTaken = endTime - startTime;
|
||||||
|
|
||||||
const ext = extname((post['data']).file_name ?? post.fileUrl ?? '').toLowerCase();
|
const fileName = (post.rating != "g" ? "SPOILER_" : "") + basename(post.fileUrl);
|
||||||
|
const ext = extname(fileName).toLowerCase();
|
||||||
|
|
||||||
const leadingDescription = [
|
const leadingDescription = [
|
||||||
`**Score:** ${formatScore(post.score ?? 0)}`,
|
`**Score:** ${formatScore(post.score ?? 0)}`,
|
||||||
`**Rating:** ${formatRating(post.rating)}`,
|
`**Rating:** ${formatRating(post.rating)}`,
|
||||||
`[File URL](${post.fileUrl})`,
|
`[File URL](<${post.fileUrl}>)`,
|
||||||
`\`${ext}\``,
|
`\`${ext}\``,
|
||||||
].join(' | ')
|
].join(' | ')
|
||||||
|
|
||||||
|
@ -174,26 +178,24 @@ module.exports = {
|
||||||
timeTaken ? formatTime(timeTaken) : '',
|
timeTaken ? formatTime(timeTaken) : '',
|
||||||
].filter(notEmpty).join(' · ')
|
].filter(notEmpty).join(' · ')
|
||||||
|
|
||||||
if (isEmbeddableFileType(ext)) {
|
const embed = new EmbedBuilder()
|
||||||
const embed = new EmbedBuilder()
|
.setColor("#cba6f7")
|
||||||
.setColor("#cba6f7")
|
.setTitle(`Post #${post.id}`)
|
||||||
.setTitle(`Post #${post.id}`)
|
.setURL(post.postView)
|
||||||
.setURL(post.postView)
|
.setDescription(description)
|
||||||
.setDescription(description)
|
.setFooter({
|
||||||
.setImage(post.fileUrl)
|
text: footerText,
|
||||||
.setFooter({
|
iconURL: proxy(`https://${post.booru.domain}/favicon.ico`),
|
||||||
text: footerText,
|
})
|
||||||
iconURL: proxy(`https://${post.booru.domain}/favicon.ico`),
|
|
||||||
})
|
|
||||||
|
|
||||||
await interaction.followUp({ content: "", embeds: [embed.data] });
|
await interaction.followUp({
|
||||||
} else {
|
content: "",
|
||||||
await interaction.followUp(
|
embeds: [embed.data],
|
||||||
'>>> ' + bold(`[Post #${post.id}](<${post.postView}>)`) + "\n" +
|
files: [{
|
||||||
description + "\n" +
|
attachment: post.fileUrl,
|
||||||
footerText
|
name: fileName
|
||||||
);
|
}]
|
||||||
}
|
});
|
||||||
},
|
},
|
||||||
async autocomplete(interaction) {
|
async autocomplete(interaction) {
|
||||||
const focusedValue = interaction.options.getFocused();
|
const focusedValue = interaction.options.getFocused();
|
||||||
|
|
98
src/commands/fun/serverconfig.js
Normal file
98
src/commands/fun/serverconfig.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
const { InteractionContextType, ApplicationIntegrationType, SlashCommandBuilder } = require("discord.js");
|
||||||
|
const { format } = require("node:util");
|
||||||
|
const { knex } = require("../../db.js");
|
||||||
|
|
||||||
|
const configData = {
|
||||||
|
"yuri": {
|
||||||
|
"description": "Automated yuri posting",
|
||||||
|
"options": [{
|
||||||
|
"name": "enable",
|
||||||
|
"required": true,
|
||||||
|
"type": "Boolean",
|
||||||
|
"description": "Should the bot post yuri?"
|
||||||
|
}, {
|
||||||
|
"name": "channel",
|
||||||
|
"type": "Channel",
|
||||||
|
"description": "Where to post yuri",
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = new SlashCommandBuilder()
|
||||||
|
.setName("serverconfig")
|
||||||
|
.setDescription("Manage your server settings")
|
||||||
|
.setContexts([
|
||||||
|
InteractionContextType.Guild
|
||||||
|
])
|
||||||
|
.setIntegrationTypes([
|
||||||
|
ApplicationIntegrationType.GuildInstall
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const cfg in configData) {
|
||||||
|
const config = configData[cfg];
|
||||||
|
|
||||||
|
data.addSubcommand((builder) => {
|
||||||
|
builder
|
||||||
|
.setName(cfg)
|
||||||
|
.setDescription(config.description);
|
||||||
|
|
||||||
|
for (const opt in config.options) {
|
||||||
|
const option = config.options[opt];
|
||||||
|
|
||||||
|
builder[`add${option.type}Option`](optionBuilder =>
|
||||||
|
optionBuilder
|
||||||
|
.setName(option.name)
|
||||||
|
.setRequired(option.required ?? false)
|
||||||
|
.setDescription(option.description)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data,
|
||||||
|
async execute(interaction) {
|
||||||
|
var result = await knex.select("data").from("serverconfigs").where("id", interaction.guildId).first();
|
||||||
|
if (!result)
|
||||||
|
result = { data: '{}' };
|
||||||
|
|
||||||
|
const config = JSON.parse(result.data);
|
||||||
|
const cfg = interaction.options.getSubcommand(true);
|
||||||
|
config[cfg] = config[cfg] ?? {};
|
||||||
|
|
||||||
|
for (const option of configData[cfg].options) {
|
||||||
|
config[cfg][option.name] = interaction.options[`get${option.type}`](option.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
id: interaction.guildId,
|
||||||
|
data: JSON.stringify(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex.raw(format('%s ON CONFLICT (id) DO UPDATE SET %s',
|
||||||
|
knex("serverconfigs").insert(data).toString().toString(),
|
||||||
|
knex("serverconfigs").update(data).whereRaw(`'serverconfigs'.id = '${interaction.guildId}'`).toString().replace(/^update\s.*\sset\s/i, '')
|
||||||
|
));
|
||||||
|
|
||||||
|
interaction.reply({ content: "Settings updated!", ephemeral: true });
|
||||||
|
},
|
||||||
|
async autocomplete(interaction) {
|
||||||
|
const focusedOption = interaction.options.getFocused(true);
|
||||||
|
const command = interaction.options.getSubcommand(true);
|
||||||
|
|
||||||
|
console.log(command, focusedOption);
|
||||||
|
|
||||||
|
const id = "";
|
||||||
|
|
||||||
|
const choices = [];
|
||||||
|
for (const option in configData) {
|
||||||
|
if (focusedOption.name == "name" && option.startsWith(focusedOption.value))
|
||||||
|
choices.push(option);
|
||||||
|
else if (focusedOption.name == "value" && (option == interaction.options.getString("name") ?? ""))
|
||||||
|
choices.push(...buildChoices(option, interaction));
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.respond(choices.map(choice => ({ name: choice, value: choice })))
|
||||||
|
},
|
||||||
|
};
|
62
src/commands/fun/userconfig.js
Normal file
62
src/commands/fun/userconfig.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
const { InteractionContextType, ApplicationIntegrationType, SlashCommandBuilder } = require("discord.js");
|
||||||
|
const { knex } = require("../../db.js");
|
||||||
|
|
||||||
|
const configData = {}
|
||||||
|
|
||||||
|
const data = new SlashCommandBuilder()
|
||||||
|
.setName("userconfig")
|
||||||
|
.setDescription("Manage your user settings")
|
||||||
|
.setContexts([
|
||||||
|
InteractionContextType.Guild,
|
||||||
|
InteractionContextType.BotDM,
|
||||||
|
InteractionContextType.PrivateChannel
|
||||||
|
])
|
||||||
|
.setIntegrationTypes([
|
||||||
|
ApplicationIntegrationType.UserInstall
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const option in configData) {
|
||||||
|
const config = configData[option];
|
||||||
|
|
||||||
|
data.addSubcommand((builder) => {
|
||||||
|
builder
|
||||||
|
.setName(option)
|
||||||
|
.setDescription(config.description)
|
||||||
|
switch (config.type) {
|
||||||
|
case "bool":
|
||||||
|
builder.addBooleanOption(builder =>
|
||||||
|
builder.setName("value")
|
||||||
|
);
|
||||||
|
case "channel":
|
||||||
|
builder.addChannelOption(builder =>
|
||||||
|
builder.setName("channel")
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data,
|
||||||
|
async execute(interaction) {
|
||||||
|
interaction.reply("Not implemented yet, sorry!");
|
||||||
|
},
|
||||||
|
async autocomplete(interaction) {
|
||||||
|
const focusedOption = interaction.options.getFocused(true);
|
||||||
|
const command = interaction.options.getSubcommand(true);
|
||||||
|
|
||||||
|
console.log(command, focusedOption);
|
||||||
|
|
||||||
|
const id = "";
|
||||||
|
|
||||||
|
const choices = [];
|
||||||
|
for (const option in configData) {
|
||||||
|
if (focusedOption.name == "name" && option.startsWith(focusedOption.value))
|
||||||
|
choices.push(option);
|
||||||
|
else if (focusedOption.name == "value" && (option == interaction.options.getString("name") ?? ""))
|
||||||
|
choices.push(...buildChoices(option, interaction));
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.respond(choices.map(choice => ({ name: choice, value: choice })))
|
||||||
|
},
|
||||||
|
};
|
12
src/index.js
12
src/index.js
|
@ -80,6 +80,18 @@ client.once(Events.ClientReady, async () => {
|
||||||
table.string("blacklist");
|
table.string("blacklist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable("serverconfigs")))
|
||||||
|
await knex.schema.createTable("serverconfigs", function (table) {
|
||||||
|
table.string("id").primary();
|
||||||
|
table.string("data");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable("userconfigs")))
|
||||||
|
await knex.schema.createTable("userconfigs", function (table) {
|
||||||
|
table.string("id").primary();
|
||||||
|
table.string("data");
|
||||||
|
});
|
||||||
|
|
||||||
var user = client.user.toJSON();
|
var user = client.user.toJSON();
|
||||||
|
|
||||||
for (const prompt of fs.readdirSync(promptsDir)) {
|
for (const prompt of fs.readdirSync(promptsDir)) {
|
||||||
|
|
Loading…
Reference in a new issue