Compare commits

..

No commits in common. "afa0a70f8a86cfee054856d9fcf14d508d60fcdc" and "6f8435b699bbb3a9af87e14f7eb3b9d75fcf0741" have entirely different histories.

18 changed files with 73 additions and 463 deletions

View file

@ -1,13 +0,0 @@
# Prod
**/dist
**/node_modules
**/persist
# Files
**/.env
**/.prettierrc
**/LICENSE
**/package-lock.json
**/package.json
**/README.md
**/tsconfig.json

View file

@ -1,4 +0,0 @@
{
"trailingComma": "none",
"arrowParens": "avoid"
}

View file

@ -6,7 +6,6 @@
"scripts": { "scripts": {
"build": "rm -rf ./dist && tsc -p .", "build": "rm -rf ./dist && tsc -p .",
"start": "node ./dist/main.js", "start": "node ./dist/main.js",
"prettier": "if prettier -v >/dev/null 2>&1; then prettier . --write; else npx prettier . --write; fi",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "powermaker450", "author": "powermaker450",

View file

@ -19,5 +19,5 @@
import { Client } from "discord.js"; import { Client } from "discord.js";
export const BotClient = new Client({ export const BotClient = new Client({
intents: ["Guilds", "GuildMessages", "DirectMessages"] intents: ["Guilds", "GuildMessages", "DirectMessages"],
}); });

View file

@ -24,8 +24,8 @@ import Logger from "../utils/Logger";
const logger = new Logger("Deployer"); const logger = new Logger("Deployer");
const commandsData = Object.values(commands).map(command => const commandsData = Object.values(commands).map((command) =>
command.data.toJSON() command.data.toJSON(),
); );
const rest = new REST({ version: "9" }).setToken(BOT_TOKEN); const rest = new REST({ version: "9" }).setToken(BOT_TOKEN);
@ -35,7 +35,7 @@ export async function deployCommands({ guildId }: DeployCommandsProps) {
logger.log("Started refreshing (/) commands."); logger.log("Started refreshing (/) commands.");
await rest.put(Routes.applicationGuildCommands(BOT_ID, guildId), { await rest.put(Routes.applicationGuildCommands(BOT_ID, guildId), {
body: commandsData body: commandsData,
}); });
logger.log("Successfully reloaded (/) commands."); logger.log("Successfully reloaded (/) commands.");

View file

@ -17,47 +17,35 @@
*/ */
import { import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
CommandInteraction, CommandInteraction,
ComponentType,
EmbedBuilder, EmbedBuilder,
SlashCommandBuilder, SlashCommandBuilder,
TextChannel TextChannel,
} from "discord.js"; } from "discord.js";
import { BotClient } from "../bot"; import { BotClient } from "../bot";
import { dt } from "../main"; import { dt } from "../main";
import { StoreMan } from "../storeman"; import { StoreMan } from "../storeman";
import getRandomColor from "../utils/getRandomColor"; import getRandomColor from "../utils/getRandomColor";
import Logger from "../utils/Logger"; import Logger from "../utils/Logger";
import { submit } from "../modals";
const logger = new Logger("(/) confess"); const logger = new Logger("(/) confess");
export const data = new SlashCommandBuilder() export const data = new SlashCommandBuilder()
.setName("confess") .setName("confess")
.setDescription("Send a confession") .setDescription("Send a confession")
.addStringOption(option => .addStringOption((option) =>
option option
.setName("message") .setName("message")
.setRequired(true) .setRequired(true)
.setDescription("What you want to confess") .setDescription("What you want to confess"),
)
.addStringOption(option =>
option
.setName("attachment")
.setDescription("The link to an image to attach (optional)")
); );
export async function execute(interaction: CommandInteraction) { export async function execute(interaction: CommandInteraction) {
// TODO: This all works as intended, but I'd like for it so be a reusable function
// instead because all of this is reused in src/main.ts:56
try { try {
if (dt.isBanned(interaction.guild?.id!, interaction.user.id)) { if (dt.isBanned(interaction.guild?.id!, interaction.user.id)) {
return interaction.reply({ return interaction.reply({
content: "You are banned from confessions in this server!", content: "You are banned from confessions in this server!",
ephemeral: true ephemeral: true,
}); });
} }
@ -65,7 +53,7 @@ export async function execute(interaction: CommandInteraction) {
return interaction.reply({ return interaction.reply({
content: content:
"The bot hasn't been set up yet! Ask the server admins to set it up.", "The bot hasn't been set up yet! Ask the server admins to set it up.",
ephemeral: true ephemeral: true,
}); });
} }
@ -74,12 +62,7 @@ export async function execute(interaction: CommandInteraction) {
const adminChannel = dt.getGuildInfo(interaction.guild?.id!)?.settings const adminChannel = dt.getGuildInfo(interaction.guild?.id!)?.settings
.modChannel; .modChannel;
// @ts-ignore // @ts-ignore
const messageContent: string = interaction.options.getString("message"); const messageContent = interaction.options.getString("message");
// @ts-ignore
const attachment: string = interaction.options.getString("attachment");
const isAttachment = (text: string) =>
text && (text.startsWith("http://") || text.startsWith("https://"));
const color = getRandomColor(); const color = getRandomColor();
const messageId = StoreMan.genId(); const messageId = StoreMan.genId();
@ -89,8 +72,6 @@ export async function execute(interaction: CommandInteraction) {
// @ts-ignore // @ts-ignore
.setDescription(messageContent); .setDescription(messageContent);
isAttachment(attachment) && userConfessionEmbed.setImage(attachment);
const adminConfessionEmbed = new EmbedBuilder() const adminConfessionEmbed = new EmbedBuilder()
.setColor(color) .setColor(color)
.setTitle(`Anonymous Confession \`${messageId}\``) .setTitle(`Anonymous Confession \`${messageId}\``)
@ -99,44 +80,22 @@ export async function execute(interaction: CommandInteraction) {
.addFields( .addFields(
{ {
name: "Author", name: "Author",
value: interaction.user.displayName value: interaction.user.displayName,
}, },
{ {
name: "Author ID", name: "Author ID",
value: interaction.user.id value: interaction.user.id,
} },
); );
isAttachment(attachment) && adminConfessionEmbed.setImage(attachment);
const submitConfessionButton = new ButtonBuilder()
.setCustomId("submitConfession")
.setLabel("Submit a Confession")
.setStyle(ButtonStyle.Primary);
const actionRow = new ActionRowBuilder<ButtonBuilder>().setComponents(
submitConfessionButton
);
const message = await ( const message = await (
BotClient.channels.cache.get(confessChannel!) as TextChannel BotClient.channels.cache.get(confessChannel!) as TextChannel
).send({ ).send({
embeds: [userConfessionEmbed], embeds: [userConfessionEmbed],
components: [actionRow]
});
const collector = message.createMessageComponentCollector({
componentType: ComponentType.Button
});
collector.on("collect", i => {
if (i.customId === "submitConfession") {
i.showModal(submit);
}
}); });
await (BotClient.channels.cache.get(adminChannel!) as TextChannel).send({ await (BotClient.channels.cache.get(adminChannel!) as TextChannel).send({
embeds: [adminConfessionEmbed] embeds: [adminConfessionEmbed],
}); });
dt.addConfession( dt.addConfession(
@ -145,29 +104,11 @@ export async function execute(interaction: CommandInteraction) {
interaction.user.displayName, interaction.user.displayName,
interaction.user.id, interaction.user.id,
messageContent, messageContent,
attachment
); );
const confessionsLength = dt.getGuildInfo(interaction.guild?.id!)
?.confessions.length!;
if (confessionsLength >= 2) {
await (
BotClient.channels.cache.get(confessChannel!) as TextChannel
).messages
.fetch(
dt.getGuildInfo(interaction.guild?.id!)?.confessions[
confessionsLength - 2
].messageId!
)
.then(message => {
message.edit({ components: [] });
});
}
return interaction.reply({ return interaction.reply({
content: "Confession sent!", content: "Confession sent!",
ephemeral: true ephemeral: true,
}); });
} catch (err) { } catch (err) {
logger.error("An error occured:", err); logger.error("An error occured:", err);

View file

@ -19,7 +19,7 @@
import { import {
CommandInteraction, CommandInteraction,
PermissionFlagsBits, PermissionFlagsBits,
SlashCommandBuilder SlashCommandBuilder,
} from "discord.js"; } from "discord.js";
import { dt } from "../main"; import { dt } from "../main";
import Logger from "../utils/Logger"; import Logger from "../utils/Logger";
@ -29,11 +29,11 @@ const logger = new Logger("(/) confessban");
export const data = new SlashCommandBuilder() export const data = new SlashCommandBuilder()
.setName("confessban") .setName("confessban")
.setDescription("Ban a user from submitting confessions.") .setDescription("Ban a user from submitting confessions.")
.addStringOption(option => .addStringOption((option) =>
option option
.setName("id") .setName("id")
.setDescription("The confession ID to ban") .setDescription("The confession ID to ban")
.setRequired(true) .setRequired(true),
) )
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers); .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers);
@ -41,18 +41,18 @@ export async function execute(interaction: CommandInteraction) {
const result = dt.addBan( const result = dt.addBan(
interaction.guild?.id!, interaction.guild?.id!,
// @ts-ignore // @ts-ignore
interaction.options.getString("id") interaction.options.getString("id"),
); );
try { try {
return result return result
? interaction.reply({ ? interaction.reply({
content: "User was banned.", content: "User was banned.",
ephemeral: true ephemeral: true,
}) })
: interaction.reply({ : interaction.reply({
content: "No confession with that ID was found.", content: "No confession with that ID was found.",
ephemeral: true ephemeral: true,
}); });
} catch (err) { } catch (err) {
logger.error("An error occured:", err); logger.error("An error occured:", err);

View file

@ -1,49 +0,0 @@
/*
* Confoss: Anonymous confessions for Discord, free as in freedom and price!
* Copyright (C) 2024 powermaker450
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
CommandInteraction,
PermissionFlagsBits,
SlashCommandBuilder
} from "discord.js";
import { dt } from "../main";
import { BotClient } from "../bot";
export const data = new SlashCommandBuilder()
.setName("confessbanlist")
.setDescription("Get the current ban list")
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages);
export async function execute(interaction: CommandInteraction) {
const bannedMembers = dt.getBans(interaction.guild?.id!);
let content = bannedMembers.length
? "Banned Members:\n"
: "There are no banned members.";
for (const member of bannedMembers) {
const identifiedMember = await BotClient.users.fetch(member.user);
content += `\n${identifiedMember.displayName} | \`${member.confessionId}\``;
}
return interaction.reply({
content: content,
ephemeral: true
});
}

View file

@ -20,7 +20,7 @@ import {
CommandInteraction, CommandInteraction,
EmbedBuilder, EmbedBuilder,
SlashCommandBuilder, SlashCommandBuilder,
TextChannel TextChannel,
} from "discord.js"; } from "discord.js";
import { dt } from "../main"; import { dt } from "../main";
import { BotClient } from "../bot"; import { BotClient } from "../bot";
@ -32,8 +32,8 @@ const logger = new Logger("(/) confessdel");
export const data = new SlashCommandBuilder() export const data = new SlashCommandBuilder()
.setName("confessdel") .setName("confessdel")
.setDescription("Deletes a confession") .setDescription("Deletes a confession")
.addStringOption(option => .addStringOption((option) =>
option.setName("id").setDescription("The confession id").setRequired(true) option.setName("id").setDescription("The confession id").setRequired(true),
); );
export async function execute(interaction: CommandInteraction) { export async function execute(interaction: CommandInteraction) {
@ -41,7 +41,7 @@ export async function execute(interaction: CommandInteraction) {
return interaction.reply({ return interaction.reply({
content: content:
"The bot hasn't been set up yet! Ask the server admins to set it up.", "The bot hasn't been set up yet! Ask the server admins to set it up.",
ephemeral: true ephemeral: true,
}); });
} }
@ -53,7 +53,7 @@ export async function execute(interaction: CommandInteraction) {
try { try {
const confession = dt.getConfession( const confession = dt.getConfession(
interaction.guild?.id!, interaction.guild?.id!,
idVal idVal,
)?.messageId; )?.messageId;
const channelId = dt.getGuildInfo(interaction.guild?.id!)?.settings const channelId = dt.getGuildInfo(interaction.guild?.id!)?.settings
.confessChannel!; .confessChannel!;
@ -65,9 +65,9 @@ export async function execute(interaction: CommandInteraction) {
await (BotClient.channels.cache.get(channelId) as TextChannel).messages await (BotClient.channels.cache.get(channelId) as TextChannel).messages
.fetch(confession!) .fetch(confession!)
.then(e => { .then((e) => {
e.edit({ e.edit({
embeds: [emptyEmbed] embeds: [emptyEmbed],
}); });
}); });
@ -75,7 +75,7 @@ export async function execute(interaction: CommandInteraction) {
return interaction.reply({ return interaction.reply({
content: "Confession removed.", content: "Confession removed.",
ephemeral: true ephemeral: true,
}); });
} catch (err) { } catch (err) {
logger.error("An error occured:", err); logger.error("An error occured:", err);
@ -84,7 +84,7 @@ export async function execute(interaction: CommandInteraction) {
return interaction.reply({ return interaction.reply({
content: content:
"Either the confession wasn't found or you may not be allowed to remove it.", "Either the confession wasn't found or you may not be allowed to remove it.",
ephemeral: true ephemeral: true,
}); });
} }
} }

View file

@ -19,7 +19,7 @@
import { import {
CommandInteraction, CommandInteraction,
PermissionFlagsBits, PermissionFlagsBits,
SlashCommandBuilder SlashCommandBuilder,
} from "discord.js"; } from "discord.js";
import { dt } from "../main"; import { dt } from "../main";
import Logger from "../utils/Logger"; import Logger from "../utils/Logger";
@ -29,11 +29,11 @@ const logger = new Logger("(/) confesspardon");
export const data = new SlashCommandBuilder() export const data = new SlashCommandBuilder()
.setName("confesspardon") .setName("confesspardon")
.setDescription("Unbans a user from confessions") .setDescription("Unbans a user from confessions")
.addStringOption(option => .addStringOption((option) =>
option option
.setName("id") .setName("id")
.setDescription("The confession ID to unban") .setDescription("The confession ID to unban")
.setRequired(true) .setRequired(true),
) )
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages); .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages);
@ -41,18 +41,18 @@ export function execute(interaction: CommandInteraction) {
const result = dt.removeBan( const result = dt.removeBan(
interaction.guild?.id!, interaction.guild?.id!,
// @ts-ignore // @ts-ignore
interaction.options.getString("id") interaction.options.getString("id"),
); );
try { try {
return result return result
? interaction.reply({ ? interaction.reply({
content: "User was unbanned.", content: "User was unbanned.",
ephemeral: true ephemeral: true,
}) })
: interaction.reply({ : interaction.reply({
content: "No confession with that ID was found.", content: "No confession with that ID was found.",
ephemeral: true ephemeral: true,
}); });
} catch (err) { } catch (err) {
logger.error("An error occured:", err); logger.error("An error occured:", err);

View file

@ -19,7 +19,6 @@
import * as confess from "./confess"; import * as confess from "./confess";
import * as confessdel from "./confessdel"; import * as confessdel from "./confessdel";
import * as confessban from "./confessban"; import * as confessban from "./confessban";
import * as confessbanlist from "./confessbanlist";
import * as confesspardon from "./confesspardon"; import * as confesspardon from "./confesspardon";
import * as ping from "./ping"; import * as ping from "./ping";
import * as setup from "./setup"; import * as setup from "./setup";
@ -28,8 +27,7 @@ export const commands = {
confess, confess,
confessdel, confessdel,
confessban, confessban,
confessbanlist,
confesspardon, confesspardon,
ping, ping,
setup setup,
}; };

View file

@ -23,7 +23,7 @@ import {
CommandInteraction, CommandInteraction,
ComponentType, ComponentType,
PermissionFlagsBits, PermissionFlagsBits,
SlashCommandBuilder SlashCommandBuilder,
} from "discord.js"; } from "discord.js";
import { dt } from "../main"; import { dt } from "../main";
import Logger from "../utils/Logger"; import Logger from "../utils/Logger";
@ -39,7 +39,7 @@ export async function execute(interaction: CommandInteraction) {
if (dt.checkSetup(interaction.guild?.id!)) { if (dt.checkSetup(interaction.guild?.id!)) {
return interaction.reply({ return interaction.reply({
content: "This guild has already been set up!", content: "This guild has already been set up!",
ephemeral: true ephemeral: true,
}); });
} }
@ -58,20 +58,20 @@ export async function execute(interaction: CommandInteraction) {
const response = await interaction.reply({ const response = await interaction.reply({
content: `# Let's get started, ${interaction.user.displayName}!\nFirst, let's choose a channel for your confessions.`, content: `# Let's get started, ${interaction.user.displayName}!\nFirst, let's choose a channel for your confessions.`,
ephemeral: true, ephemeral: true,
components: [channelRow] components: [channelRow],
}); });
const collector = response.createMessageComponentCollector({ const collector = response.createMessageComponentCollector({
componentType: ComponentType.ChannelSelect, componentType: ComponentType.ChannelSelect,
time: 45_000 time: 45_000,
}); });
collector.on("collect", async i => { collector.on("collect", async (i) => {
confessChannel = i.values[0]; confessChannel = i.values[0];
await i.update({ await i.update({
content: "Awesome!", content: "Awesome!",
components: [] components: [],
}); });
collector.stop(); collector.stop();
@ -83,55 +83,55 @@ export async function execute(interaction: CommandInteraction) {
const logChannelRow = const logChannelRow =
new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents( new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
logChannelList logChannelList,
); );
const logResponse = await interaction.followUp({ const logResponse = await interaction.followUp({
content: "# Now, select a logging channel, for moderation purposes.", content: "# Now, select a logging channel, for moderation purposes.",
ephemeral: true, ephemeral: true,
components: [logChannelRow] components: [logChannelRow],
}); });
const logCollector = logResponse.createMessageComponentCollector({ const logCollector = logResponse.createMessageComponentCollector({
componentType: ComponentType.ChannelSelect, componentType: ComponentType.ChannelSelect,
time: 45_000 time: 45_000,
}); });
logCollector.on("collect", async ij => { logCollector.on("collect", async (ij) => {
logChannel = ij.values[0]; logChannel = ij.values[0];
await ij.update({ await ij.update({
content: "Setup Complete!", content: "Setup Complete!",
components: [] components: [],
}); });
dt.setup(guildId!, { dt.setup(guildId!, {
confessChannel: confessChannel, confessChannel: confessChannel,
modChannel: logChannel, modChannel: logChannel,
bans: [] bans: [],
}); });
logCollector.stop(); logCollector.stop();
}); });
logCollector.on("end", content => { logCollector.on("end", (content) => {
// If there is no content, follow up with an error message. // If there is no content, follow up with an error message.
!content.size && !content.size &&
interaction.followUp({ interaction.followUp({
content: "No channel selected. Please try again.", content: "No channel selected. Please try again.",
ephemeral: true, ephemeral: true,
components: [] components: [],
}); });
}); });
}); });
collector.on("end", collected => { collector.on("end", (collected) => {
// Same as above logCollector end // Same as above logCollector end
!collected.size && !collected.size &&
interaction.followUp({ interaction.followUp({
content: "No channel selected. Try again.", content: "No channel selected. Try again.",
ephemeral: true, ephemeral: true,
components: [] components: [],
}); });
}); });
} catch (err) { } catch (err) {

View file

@ -16,43 +16,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ComponentType,
EmbedBuilder,
Events,
Interaction,
ModalSubmitInteraction,
TextChannel
} from "discord.js";
import { BotClient, BOT_TOKEN, deployCommands } from "./bot"; import { BotClient, BOT_TOKEN, deployCommands } from "./bot";
import { commands } from "./commands"; import { commands } from "./commands";
import { StoreMan } from "./storeman"; import { StoreMan } from "./storeman";
import Logger from "./utils/Logger"; import Logger from "./utils/Logger";
import getRandomColor from "./utils/getRandomColor";
import { submit } from "./modals";
export const dt = new StoreMan(StoreMan.checkFile()); export const dt = new StoreMan(StoreMan.checkFile());
const logger = new Logger("Main"); const logger = new Logger("Main");
BotClient.once("ready", client => { BotClient.once("ready", (client) => {
logger.log(`We're ready! Logged in as ${client.user.tag}`); logger.log(`We're ready! Logged in as ${client.user.tag}`);
}); });
// Deploy the commands for a new guild BotClient.on("guildCreate", async (guild) => {
BotClient.on("guildCreate", async guild => {
await deployCommands({ guildId: guild.id }); await deployCommands({ guildId: guild.id });
}); });
// Delete the data for a guild after it is removed BotClient.on("guildDelete", (guild) => {
BotClient.on("guildDelete", guild => {
logger.log(`${guild.name} didn't want us anymore... :(`); logger.log(`${guild.name} didn't want us anymore... :(`);
dt.clearSettings(guild.id); dt.clearSettings(guild.id);
}); });
BotClient.on(Events.InteractionCreate, async interaction => { BotClient.on("interactionCreate", async (interaction) => {
if (!interaction.isCommand()) { if (!interaction.isCommand()) {
return; return;
} }
@ -64,145 +49,4 @@ BotClient.on(Events.InteractionCreate, async interaction => {
} }
}); });
BotClient.on(Events.MessageDelete, async message => {
const id = message.guild?.id!;
const confessions = dt.getGuildInfo(id)?.confessions!;
for (const confession of confessions) {
if (confession.messageId === id) {
dt.adminDelConfession(id, confession.id);
}
}
});
BotClient.on(Events.InteractionCreate, async interaction => {
if (!interaction.isModalSubmit()) {
return;
}
if (interaction.customId === "submitConfession") {
const messageContent: string =
interaction.fields.getTextInputValue("confessionInput");
const attachment: string = interaction.fields.getTextInputValue(
"confessionAttachment"
);
try {
if (dt.isBanned(interaction.guild?.id!, interaction.user.id)) {
return interaction.reply({
content: "You are banned from confessions in this server!",
ephemeral: true
});
}
if (!dt.getGuildInfo(interaction.guild?.id!)) {
return interaction.reply({
content:
"The bot hasn't been set up yet! Ask the server admins to set it up.",
ephemeral: true
});
}
const confessChannel = dt.getGuildInfo(interaction.guild?.id!)?.settings
.confessChannel;
const adminChannel = dt.getGuildInfo(interaction.guild?.id!)?.settings
.modChannel;
const isAttachment = (text: string) =>
text && (text.startsWith("http://") || text.startsWith("https://"));
const color = getRandomColor();
const messageId = StoreMan.genId();
const userConfessionEmbed = new EmbedBuilder()
.setColor(color)
.setTitle(`Anonymous Confession \`${messageId}\``)
// @ts-ignore
.setDescription(messageContent);
isAttachment(attachment) && userConfessionEmbed.setImage(attachment);
const adminConfessionEmbed = new EmbedBuilder()
.setColor(color)
.setTitle(`Anonymous Confession \`${messageId}\``)
// @ts-ignore
.setDescription(messageContent)
.addFields(
{
name: "Author",
value: interaction.user.displayName
},
{
name: "Author ID",
value: interaction.user.id
}
);
isAttachment(attachment) && adminConfessionEmbed.setImage(attachment);
const submitConfessionButton = new ButtonBuilder()
.setCustomId("submitConfession")
.setLabel("Submit a Confession")
.setStyle(ButtonStyle.Primary);
const actionRow = new ActionRowBuilder<ButtonBuilder>().setComponents(
submitConfessionButton
);
const message = await (
BotClient.channels.cache.get(confessChannel!) as TextChannel
).send({
embeds: [userConfessionEmbed],
components: [actionRow]
});
const collector = message.createMessageComponentCollector({
componentType: ComponentType.Button
});
collector.on("collect", i => {
if (i.customId === "submitConfession") {
i.showModal(submit);
}
});
await (BotClient.channels.cache.get(adminChannel!) as TextChannel).send({
embeds: [adminConfessionEmbed]
});
dt.addConfession(
message,
messageId,
interaction.user.displayName,
interaction.user.id,
messageContent,
attachment
);
const confessionsLength = dt.getGuildInfo(interaction.guild?.id!)
?.confessions.length!;
if (confessionsLength >= 2) {
await (
BotClient.channels.cache.get(confessChannel!) as TextChannel
).messages
.fetch(
dt.getGuildInfo(interaction.guild?.id!)?.confessions[
confessionsLength - 2
].messageId!
)
.then(message => {
message.edit({ components: [] });
});
}
return interaction.reply({
content: "Confession sent!",
ephemeral: true
});
} catch (err) {
logger.error("An error occured:", err);
}
}
});
BotClient.login(BOT_TOKEN); BotClient.login(BOT_TOKEN);

View file

@ -1,19 +0,0 @@
/*
* Confoss: Anonymous confessions for Discord, free as in freedom and price!
* Copyright (C) 2024 powermaker450
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export * from "./submit";

View file

@ -1,56 +0,0 @@
/*
* Confoss: Anonymous confessions for Discord, free as in freedom and price!
* Copyright (C) 2024 powermaker450
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
ActionRowBuilder,
ModalActionRowComponentBuilder,
ModalBuilder,
TextInputBuilder,
TextInputStyle
} from "discord.js";
const submit = new ModalBuilder()
.setCustomId("submitConfession")
.setTitle("Submit Confession");
const confessionInput = new TextInputBuilder()
.setCustomId("confessionInput")
.setLabel("Confession")
.setRequired(true)
.setMaxLength(2000)
.setStyle(TextInputStyle.Paragraph);
const attachmentInput = new TextInputBuilder()
.setCustomId("confessionAttachment")
.setLabel("Attachment (optional)")
.setRequired(false)
.setStyle(TextInputStyle.Short);
const confessionRow =
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
confessionInput
);
const attachmentRow =
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
attachmentInput
);
submit.addComponents(confessionRow, attachmentRow);
export { submit };

View file

@ -18,7 +18,7 @@
import fs from "fs"; import fs from "fs";
import crypto from "crypto"; import crypto from "crypto";
import { Confession, ConfessionBan, GuildData, GuildSettings } from "./types"; import { Confession, GuildData, GuildSettings } from "./types";
import { DATA_DIR } from "./config"; import { DATA_DIR } from "./config";
import { CommandInteraction, Message } from "discord.js"; import { CommandInteraction, Message } from "discord.js";
@ -39,7 +39,6 @@ export class StoreMan {
author: string, author: string,
authorId: string, authorId: string,
content: string, content: string,
attachment?: string
): Confession { ): Confession {
return { return {
id: id, id: id,
@ -47,7 +46,6 @@ export class StoreMan {
author: author, author: author,
authorId: authorId, authorId: authorId,
content: content, content: content,
attachment: attachment
}; };
} }
@ -74,7 +72,7 @@ export class StoreMan {
fs.writeFileSync( fs.writeFileSync(
StoreMan.fullPath, StoreMan.fullPath,
JSON.stringify(this.data, null, 2), JSON.stringify(this.data, null, 2),
"utf8" "utf8",
); );
} }
@ -94,7 +92,7 @@ export class StoreMan {
this.data.push({ this.data.push({
id: id, id: id,
confessions: [], confessions: [],
settings: opts settings: opts,
}); });
this.saveFile(); this.saveFile();
@ -102,7 +100,7 @@ export class StoreMan {
// Clear the settings for a given guild // Clear the settings for a given guild
public clearSettings(id: string): void { public clearSettings(id: string): void {
this.data = this.data.filter(guild => { this.data = this.data.filter((guild) => {
return guild.id !== id; return guild.id !== id;
}); });
this.saveFile(); this.saveFile();
@ -125,7 +123,6 @@ export class StoreMan {
author: string, author: string,
authorId: string, authorId: string,
content: string, content: string,
attachment?: string
): boolean { ): boolean {
const guildId = message.guild?.id; const guildId = message.guild?.id;
@ -137,14 +134,7 @@ export class StoreMan {
} }
guild.confessions.push( guild.confessions.push(
StoreMan.toConfession( StoreMan.toConfession(message, id, author, authorId, content),
message,
id,
author,
authorId,
content,
attachment
)
); );
this.saveFile(); this.saveFile();
return true; return true;
@ -152,13 +142,13 @@ export class StoreMan {
} }
throw new Error( throw new Error(
`No guild with id ${id} was found. Something's pretty wrong.` `No guild with id ${id} was found. Something's pretty wrong.`,
); );
} }
public getConfession( public getConfession(
guildId: string, guildId: string,
confessionId: string confessionId: string,
): Confession | null { ): Confession | null {
for (const guild of this.data) { for (const guild of this.data) {
if (guild.id === guildId) { if (guild.id === guildId) {
@ -176,7 +166,7 @@ export class StoreMan {
// Attempts to delete a confession. If it is sucessfully deleted, returns true, else false. // Attempts to delete a confession. If it is sucessfully deleted, returns true, else false.
public delConfesssion( public delConfesssion(
{ guild, user }: CommandInteraction, { guild, user }: CommandInteraction,
confessionId: string confessionId: string,
): boolean { ): boolean {
const guildId = guild?.id; const guildId = guild?.id;
const userId = user.id; const userId = user.id;
@ -185,7 +175,7 @@ export class StoreMan {
if (guild.id === guildId) { if (guild.id === guildId) {
for (const confession of guild.confessions) { for (const confession of guild.confessions) {
if (confession.authorId === userId) { if (confession.authorId === userId) {
guild.confessions = guild.confessions.filter(confession => { guild.confessions = guild.confessions.filter((confession) => {
return confession.id !== confessionId; return confession.id !== confessionId;
}); });
@ -199,22 +189,12 @@ export class StoreMan {
return false; return false;
} }
public adminDelConfession(guildId: string, confessionId: string): void {
for (const guild of this.data) {
if (guild.id === guildId) {
guild.confessions = guild.confessions.filter(confession => {
return confession.id !== confessionId;
});
}
}
}
// Check if a certain user is banned within a guild. // Check if a certain user is banned within a guild.
public isBanned(guildId: string, userId: string): boolean { public isBanned(guildId: string, userId: string): boolean {
for (const guild of this.data) { for (const guild of this.data) {
if (guild.id === guildId) { if (guild.id === guildId) {
for (const ban of guild.settings.bans) { for (const ban of guild.settings.bans) {
if (ban.user === userId) { if (ban === userId) {
return true; return true;
} }
} }
@ -224,7 +204,7 @@ export class StoreMan {
return false; return false;
} }
public getBans(guildId: string): ConfessionBan[] { public getBans(guildId: string): string[] {
for (const guild of this.data) { for (const guild of this.data) {
if (guild.id === guildId) { if (guild.id === guildId) {
return guild.settings.bans; return guild.settings.bans;
@ -243,10 +223,7 @@ export class StoreMan {
if (confession) { if (confession) {
// Only add the user to the ban list if they aren't banned already // Only add the user to the ban list if they aren't banned already
!this.isBanned(guildId, confession.authorId) && !this.isBanned(guildId, confession.authorId) &&
guild.settings.bans.push({ guild.settings.bans.push(confession.authorId!);
user: confession.authorId,
confessionId: confessionId
});
this.saveFile(); this.saveFile();
return true; return true;
@ -262,10 +239,8 @@ export class StoreMan {
for (const guild of this.data) { for (const guild of this.data) {
if (guild.id === guildId) { if (guild.id === guildId) {
if (this.getConfession(guildId, confessionId)) { if (this.getConfession(guildId, confessionId)) {
guild.settings.bans = guild.settings.bans.filter(ban => { guild.settings.bans = guild.settings.bans.filter((ban) => {
return ( return ban !== this.getConfession(guildId, confessionId)?.authorId!;
ban.user !== this.getConfession(guildId, confessionId)?.authorId!
);
}); });
this.saveFile(); this.saveFile();

View file

@ -22,18 +22,12 @@ export interface Confession {
author: string; author: string;
authorId: string; authorId: string;
content: string; content: string;
attachment?: string;
}
export interface ConfessionBan {
user: string;
confessionId: string;
} }
export interface GuildSettings { export interface GuildSettings {
confessChannel: string; confessChannel: string;
modChannel: string; modChannel: string;
bans: ConfessionBan[]; bans: string[];
} }
export interface GuildData { export interface GuildData {

View file

@ -36,7 +36,7 @@ export default class Logger {
public static readonly udln = chalk.underline; public static readonly udln = chalk.underline;
public static readonly anon = Logger.bold.gray( public static readonly anon = Logger.bold.gray(
`[ConfessBot] | ${Logger.emp("Anonymous ")}` `[ConfessBot] | ${Logger.emp("Anonymous ")}`,
); );
constructor(origin?: string) { constructor(origin?: string) {