Adds Deletion

This commit is contained in:
Michel Fedde 2025-03-29 20:15:50 +01:00
parent a0b668cb90
commit 0d9cf6a370
8 changed files with 174 additions and 35 deletions

View file

@ -0,0 +1,34 @@
import {DatabaseDefinition} from "../DatabaseDefinition";
export type DBGroup = {
id: number;
groupid: number;
key: string;
value: string;
}
const dbDefinition: DatabaseDefinition = {
name: "groups",
columns: [
{
name: "id",
type: "INTEGER",
autoIncrement: true,
primaryKey: true,
},
{
name: "groupid",
type: "VARCHAR(32)",
},
{
name: "key",
type: "VARCHAR(32)",
},
{
name: "value",
type: "VARCHAR(128)",
}
]
}
export default dbDefinition;

View file

@ -2,7 +2,7 @@ import {
AutocompleteInteraction,
ChatInputCommandInteraction,
CommandInteraction,
GuildMember,
GuildMember, SlashCommandIntegerOption,
SlashCommandStringOption
} from "discord.js";
import {Container} from "../../Container/Container";
@ -11,32 +11,32 @@ import {GroupModel} from "../../Models/GroupModel";
import {UserError} from "../UserError";
export class GroupSelection {
public static createOptionSetup(): SlashCommandStringOption {
return new SlashCommandStringOption()
public static createOptionSetup(): SlashCommandIntegerOption {
return new SlashCommandIntegerOption()
.setName("group")
.setDescription("Defines the group you want to manage the playdates for")
.setRequired(true)
.setAutocomplete(true)
}
public static async handleAutocomplete(interaction: AutocompleteInteraction): Promise<void> {
public static async handleAutocomplete(interaction: AutocompleteInteraction, onlyLeaders: boolean = false): Promise<void> {
const value = interaction.options.getFocused();
const repo = Container.get<GroupRepository>(GroupRepository.name);
const groups = repo.findGroupsByMember(<GuildMember>interaction.member);
let groups = repo.findGroupsByMember(<GuildMember>interaction.member, onlyLeaders);
await interaction.respond(
groups
.filter((group) => group.name.startsWith(value))
.map((group) => ({name: group.name, value: group.name }))
.map((group) => ({name: group.name, value: group.id }))
)
}
public static getGroup(interaction: CommandInteraction): GroupModel {
const groupname = interaction.options.get("group");
const groupname = interaction.options.get("group", true);
if (!groupname) {
throw new UserError("No group name provided");
}
const group = Container.get<GroupRepository>(GroupRepository.name).findGroupByName((groupname.value ?? '').toString());
const group = Container.get<GroupRepository>(GroupRepository.name).getById(<number>(groupname.value ?? 0));
if (!group) {
throw new UserError("No group found");
}

View file

@ -3,15 +3,25 @@ import {
Interaction,
CommandInteraction,
ChatInputCommandInteraction,
MessageFlags, GuildMemberRoleManager, InteractionReplyOptions, GuildMember
MessageFlags, GuildMemberRoleManager, InteractionReplyOptions, GuildMember, EmbedBuilder, AutocompleteInteraction
} from "discord.js";
import {ChatInteractionCommand, Command} from "./Command";
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
import {GroupModel} from "../../Models/GroupModel";
import {GroupRepository} from "../../Repositories/GroupRepository";
import {DatabaseConnection} from "../../Database/DatabaseConnection";
import {Container} from "../../Container/Container";
import {GroupSelection} from "../CommandPartials/GroupSelection";
import {UserError} from "../UserError";
import {ArrayUtils} from "../../Utilities/ArrayUtils";
export class GroupCommand implements Command, ChatInteractionCommand {
export class GroupCommand implements Command, ChatInteractionCommand, AutocompleteCommand {
private static GOODBYE_MESSAGES: string[] = [
'Sad to see you go.',
'May your next adventure be fruitful.',
'I hope, I served you well.',
'I wish you, good luck on your next adventures.',
]
definition(): SlashCommandBuilder {
// @ts-ignore
return new SlashCommandBuilder()
@ -35,8 +45,17 @@ export class GroupCommand implements Command, ChatInteractionCommand {
listCommand
.setName("list")
.setDescription("Displays the groups you are apart of.")
)
.addSubcommand(command => command
.setName('config')
.setDescription("Starts the config manager for the group.")
.addIntegerOption(GroupSelection.createOptionSetup())
)
.addSubcommand(command => command
.setName("remove")
.setDescription("Deletes a group you are the leader for.")
.addIntegerOption(GroupSelection.createOptionSetup())
);
}
execute(interaction: ChatInputCommandInteraction): Promise<void> {
switch (interaction.options.getSubcommand()) {
@ -46,6 +65,11 @@ export class GroupCommand implements Command, ChatInteractionCommand {
case "list":
this.list(interaction);
break;
case "remove":
this.remove(interaction);
break;
case "config":
this.runConfigurator(interaction);
default:
throw new Error("Unsupported command");
}
@ -79,22 +103,65 @@ export class GroupCommand implements Command, ChatInteractionCommand {
const repo = Container.get<GroupRepository>(GroupRepository.name);
const groups = repo.findGroupsByMember(<GuildMember>interaction.member);
const embed = new EmbedBuilder()
.setTitle("Your groups on this server:")
.setFields(
groups.map(group => {
return {
name: group.name,
value: `
Role: <@&${group.role.roleid}>
`
}
})
)
const reply: InteractionReplyOptions = {
embeds: [
{
title: "Your groups on this server:",
fields: groups.map((group) => {
return {
name: group.name,
value: ""
}
})
}
embed
],
allowedMentions: { roles: [] },
flags: MessageFlags.Ephemeral
}
interaction.reply(reply);
}
private async remove(interaction: ChatInputCommandInteraction) {
const group = GroupSelection.getGroup(interaction);
const repo = Container.get<GroupRepository>(GroupRepository.name);
if (group.leader.memberid != interaction.member?.user.id) {
throw new UserError("Can't remove group. You are not the leader.");
}
repo.deleteGroup(group);
const embed = new EmbedBuilder()
.setTitle("Group deleted.")
.setDescription(
`:x: Deleted \`${group.name}\`. ${ArrayUtils.chooseRandom(GroupCommand.GOODBYE_MESSAGES)}`
)
await interaction.reply({
embeds: [
embed
],
flags: MessageFlags.Ephemeral,
})
}
async handleAutocomplete(interaction: AutocompleteInteraction): Promise<void> {
const option = interaction.options.getFocused(true);
if (option.name == "group") {
await GroupSelection.handleAutocomplete(interaction, true);
return;
}
}
private runConfigurator(interaction: ChatInputCommandInteraction) {
const group = GroupSelection.getGroup(interaction);
}
}

View file

@ -31,7 +31,7 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
.addSubcommand((subcommand) => subcommand
.setName("create")
.setDescription("Creates a new playdate")
.addStringOption(GroupSelection.createOptionSetup())
.addIntegerOption(GroupSelection.createOptionSetup())
.addStringOption((option) => option
.setName("from")
.setDescription("Defines the start date & time. Format: YYYY-MM-DD HH:mm")
@ -48,12 +48,12 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
.addSubcommand((subcommand) => subcommand
.setName("list")
.setDescription("Lists all playdates")
.addStringOption(GroupSelection.createOptionSetup())
.addIntegerOption(GroupSelection.createOptionSetup())
)
.addSubcommand((subcommand) => subcommand
.setName("remove")
.setDescription("Removes a playdate")
.addStringOption(GroupSelection.createOptionSetup())
.addIntegerOption(GroupSelection.createOptionSetup())
.addIntegerOption((option) => option
.setName("playdate")
.setDescription("Selects a playdate")
@ -179,8 +179,6 @@ export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInter
throw new UserError("No playdate found");
}
console.log(selected, group);
if (selected.group?.id != group.id) {
throw new UserError("No playdate found");
}

View file

@ -0,0 +1,8 @@
import {Model} from "./Model";
import {GroupModel} from "./GroupModel";
export interface GroupConfigurationModel extends Model {
group: GroupModel;
key: string;
value: string;
}

View file

@ -4,6 +4,8 @@ import Groups, {DBGroup} from "../Database/tables/Groups";
import {DatabaseConnection} from "../Database/DatabaseConnection";
import {CacheType, CacheTypeReducer, Guild, GuildMember, GuildMemberRoleManager} from "discord.js";
import {Nullable} from "../types/Nullable";
import {PlaydateRepository} from "./PlaydateRepository";
import {Container} from "../Container/Container";
export class GroupRepository extends Repository<GroupModel, DBGroup> {
@ -43,12 +45,31 @@ export class GroupRepository extends Repository<GroupModel, DBGroup> {
return dbResult.map((result) => this.convertToModelType(result));
}
public findGroupsByMember(member: GuildMember) {
public findGroupsByMember(member: GuildMember, onlyLeader: boolean = false) {
if (!member) {
throw new Error("Can't find member for guild: none given");
}
return this.findGroupsByRoles(member.guild.id, [...member.roles.cache.keys()])
const groups = this.findGroupsByRoles(member.guild.id, [...member.roles.cache.keys()])
if (!onlyLeader) {
return groups;
}
return groups.filter((group: GroupModel) => {
return group.leader.memberid === member.id;
})
}
public deleteGroup(group: GroupModel): void {
this.delete(group.id);
debugger
const repo = Container.get<PlaydateRepository>(PlaydateRepository.name);
const playdates = repo.findFromGroup(group, true)
playdates.forEach((playdate) => {
repo.delete(playdate.id);
})
}
protected convertToModelType(intermediateModel: DBGroup | undefined): GroupModel {

View file

@ -5,7 +5,6 @@ import {DatabaseConnection} from "../Database/DatabaseConnection";
import {GroupRepository} from "./GroupRepository";
import {GroupModel} from "../Models/GroupModel";
import {Nullable} from "../types/Nullable";
import playdate from "../Database/tables/Playdate";
export class PlaydateRepository extends Repository<PlaydateModel, DBPlaydate> {
@ -19,16 +18,22 @@ export class PlaydateRepository extends Repository<PlaydateModel, DBPlaydate> {
);
}
findFromGroup(group: GroupModel) {
findFromGroup(group: GroupModel, all = false) {
let sql = `SELECT * FROM ${this.schema.name} WHERE groupid = ?`;
const params = [group.id];
if (!all) {
sql += " AND time_from > ?"
params.push(new Date().getTime())
}
const finds = this.database.fetchAll<number, DBPlaydate>(
`SELECT * FROM ${this.schema.name} WHERE groupid = ? AND time_from > ?`,
group.id,
new Date().getTime()
sql,
...params
);
return finds.map((playdate) => this.convertToModelType(playdate, group));
}
protected convertToModelType(intermediateModel: DBPlaydate | undefined, fixedGroup: Nullable<GroupModel> = null): PlaydateModel {
if (!intermediateModel) {
throw new Error("Unable to convert the playdate model");

View file

@ -0,0 +1,6 @@
export class ArrayUtils {
public static chooseRandom<T>(array: Array<T>):T {
const index = Math.floor(Math.random() * array.length);
return array[index];
}
}