Adds initial progress
This commit is contained in:
commit
a0b668cb90
34 changed files with 2680 additions and 0 deletions
17
source/Discord/Commands/Command.ts
Normal file
17
source/Discord/Commands/Command.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import {ChatInputCommandInteraction, Interaction, SlashCommandBuilder} from "discord.js";
|
||||
import Commands from "./Commands";
|
||||
|
||||
export interface Command {
|
||||
definition(): SlashCommandBuilder;
|
||||
}
|
||||
|
||||
export interface ChatInteractionCommand {
|
||||
execute(interaction: ChatInputCommandInteraction): Promise<void>;
|
||||
}
|
||||
|
||||
export interface AutocompleteCommand {
|
||||
handleAutocomplete(interaction: Interaction): Promise<void>;
|
||||
}
|
||||
|
||||
export type CommandUnion =
|
||||
Command | Partial<ChatInteractionCommand> | Partial<AutocompleteCommand>;
|
||||
41
source/Discord/Commands/Commands.ts
Normal file
41
source/Discord/Commands/Commands.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import {HelloWorldCommand} from "./HelloWorldCommand";
|
||||
import {Command, CommandUnion} from "./Command";
|
||||
import {GroupCommand} from "./Groups";
|
||||
import {PlaydatesCommand} from "./Playdates";
|
||||
|
||||
const commands: Set<Command> = new Set<Command>([
|
||||
new HelloWorldCommand(),
|
||||
new GroupCommand(),
|
||||
new PlaydatesCommand()
|
||||
]);
|
||||
|
||||
export default class Commands {
|
||||
private mappedCommands: Map<string, Command> = new Map<string, Command>();
|
||||
|
||||
constructor() {
|
||||
this.mappedCommands = this.getMap();
|
||||
}
|
||||
|
||||
public getCommand(commandName: string): CommandUnion|undefined {
|
||||
if (!this.mappedCommands.has(commandName)) {
|
||||
throw new Error(`Unknown command: ${commandName}`);
|
||||
}
|
||||
|
||||
return this.mappedCommands.get(commandName);
|
||||
}
|
||||
|
||||
public get allCommands(): Set<Command> {
|
||||
return commands;
|
||||
}
|
||||
|
||||
private getMap(): Map<string, Command>
|
||||
{
|
||||
const map = new Map<string, Command>();
|
||||
for (const command of commands) {
|
||||
const definition = command.definition()
|
||||
map.set(definition.name, command);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
100
source/Discord/Commands/Groups.ts
Normal file
100
source/Discord/Commands/Groups.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import {
|
||||
SlashCommandBuilder,
|
||||
Interaction,
|
||||
CommandInteraction,
|
||||
ChatInputCommandInteraction,
|
||||
MessageFlags, GuildMemberRoleManager, InteractionReplyOptions, GuildMember
|
||||
} from "discord.js";
|
||||
import {ChatInteractionCommand, Command} from "./Command";
|
||||
import {GroupModel} from "../../Models/GroupModel";
|
||||
import {GroupRepository} from "../../Repositories/GroupRepository";
|
||||
import {DatabaseConnection} from "../../Database/DatabaseConnection";
|
||||
import {Container} from "../../Container/Container";
|
||||
|
||||
export class GroupCommand implements Command, ChatInteractionCommand {
|
||||
definition(): SlashCommandBuilder {
|
||||
// @ts-ignore
|
||||
return new SlashCommandBuilder()
|
||||
.setName('groups')
|
||||
.setDescription(`Manages groups`)
|
||||
.addSubcommand(create =>
|
||||
create.setName("create")
|
||||
.setDescription("Creates a new group, with executing user being the leader")
|
||||
.addStringOption((option) =>
|
||||
option.setName("name")
|
||||
.setDescription("Defines the name for the group.")
|
||||
.setRequired(true)
|
||||
)
|
||||
.addRoleOption((builder) =>
|
||||
builder.setName("role")
|
||||
.setDescription("Defines the role, where all the members are located in.")
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(listCommand =>
|
||||
listCommand
|
||||
.setName("list")
|
||||
.setDescription("Displays the groups you are apart of.")
|
||||
);
|
||||
|
||||
}
|
||||
execute(interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
switch (interaction.options.getSubcommand()) {
|
||||
case "create":
|
||||
this.create(interaction);
|
||||
break;
|
||||
case "list":
|
||||
this.list(interaction);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported command");
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private create(interaction: ChatInputCommandInteraction): void {
|
||||
const name = interaction.options.getString("name") ?? '';
|
||||
const role = interaction.options.getRole("role");
|
||||
|
||||
const group: GroupModel = {
|
||||
id: -1,
|
||||
name: name,
|
||||
leader: {
|
||||
server: interaction.guildId ?? '',
|
||||
memberid: interaction.member?.user.id ?? ''
|
||||
},
|
||||
role: {
|
||||
server: interaction.guildId ?? '',
|
||||
roleid: role?.id ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
Container.get<GroupRepository>(GroupRepository.name).create(group);
|
||||
|
||||
interaction.reply({content: `:white_check_mark: Created group \`${name}\``, flags: MessageFlags.Ephemeral })
|
||||
}
|
||||
|
||||
private list(interaction: ChatInputCommandInteraction) {
|
||||
const repo = Container.get<GroupRepository>(GroupRepository.name);
|
||||
const groups = repo.findGroupsByMember(<GuildMember>interaction.member);
|
||||
|
||||
const reply: InteractionReplyOptions = {
|
||||
embeds: [
|
||||
{
|
||||
title: "Your groups on this server:",
|
||||
|
||||
fields: groups.map((group) => {
|
||||
return {
|
||||
name: group.name,
|
||||
value: ""
|
||||
}
|
||||
})
|
||||
}
|
||||
],
|
||||
flags: MessageFlags.Ephemeral
|
||||
}
|
||||
|
||||
interaction.reply(reply);
|
||||
}
|
||||
}
|
||||
25
source/Discord/Commands/HelloWorldCommand.ts
Normal file
25
source/Discord/Commands/HelloWorldCommand.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import {SlashCommandBuilder, Interaction, CommandInteraction} from "discord.js";
|
||||
import {Command} from "./Command";
|
||||
|
||||
export class HelloWorldCommand implements Command {
|
||||
private static RESPONSES: string[] = [
|
||||
'Hello :)',
|
||||
'zzzZ... ZzzzZ... huh? I am awake. I am awake!',
|
||||
'Roll for initiative!',
|
||||
'I was an adventurerer like you...',
|
||||
'Hello :) How are you?',
|
||||
]
|
||||
|
||||
definition(): SlashCommandBuilder
|
||||
{
|
||||
return new SlashCommandBuilder()
|
||||
.setName("hello")
|
||||
.setDescription("Displays a random response. (commonly used to test if the bot is online)")
|
||||
}
|
||||
async execute(interaction: CommandInteraction): Promise<void> {
|
||||
const random = Math.floor(Math.random() * HelloWorldCommand.RESPONSES.length);
|
||||
|
||||
await interaction.reply(HelloWorldCommand.RESPONSES[random]);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
206
source/Discord/Commands/Playdates.ts
Normal file
206
source/Discord/Commands/Playdates.ts
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import {
|
||||
SlashCommandBuilder,
|
||||
Interaction,
|
||||
CommandInteraction,
|
||||
AutocompleteInteraction,
|
||||
GuildMember,
|
||||
EmbedBuilder, MessageFlags, ChatInputCommandInteraction, ModalSubmitFields
|
||||
} from "discord.js";
|
||||
import {AutocompleteCommand, ChatInteractionCommand, Command} from "./Command";
|
||||
import {Container} from "../../Container/Container";
|
||||
import {GroupRepository} from "../../Repositories/GroupRepository";
|
||||
import {GroupSelection} from "../CommandPartials/GroupSelection";
|
||||
import {setFlagsFromString} from "node:v8";
|
||||
import {UserError} from "../UserError";
|
||||
import Playdate from "../../Database/tables/Playdate";
|
||||
import {PlaydateModel} from "../../Models/PlaydateModel";
|
||||
import {PlaydateRepository} from "../../Repositories/PlaydateRepository";
|
||||
import {GroupModel} from "../../Models/GroupModel";
|
||||
import playdate from "../../Database/tables/Playdate";
|
||||
|
||||
export class PlaydatesCommand implements Command, AutocompleteCommand, ChatInteractionCommand {
|
||||
static REGEX = [
|
||||
|
||||
]
|
||||
|
||||
definition(): SlashCommandBuilder {
|
||||
// @ts-ignore
|
||||
return new SlashCommandBuilder()
|
||||
.setName("playdates")
|
||||
.setDescription("Manage your playdates")
|
||||
.addSubcommand((subcommand) => subcommand
|
||||
.setName("create")
|
||||
.setDescription("Creates a new playdate")
|
||||
.addStringOption(GroupSelection.createOptionSetup())
|
||||
.addStringOption((option) => option
|
||||
.setName("from")
|
||||
.setDescription("Defines the start date & time. Format: YYYY-MM-DD HH:mm")
|
||||
)
|
||||
.addStringOption((option) => option
|
||||
.setName("to")
|
||||
.setDescription("Defines the end date & time. Format: YYYY-MM-DD HH:mm")
|
||||
)
|
||||
.addAttachmentOption((option) => option
|
||||
.setName("calendar-entry")
|
||||
.setDescription("Optional, you can upload a iCal file and the from and to-values are read from it.")
|
||||
)
|
||||
)
|
||||
.addSubcommand((subcommand) => subcommand
|
||||
.setName("list")
|
||||
.setDescription("Lists all playdates")
|
||||
.addStringOption(GroupSelection.createOptionSetup())
|
||||
)
|
||||
.addSubcommand((subcommand) => subcommand
|
||||
.setName("remove")
|
||||
.setDescription("Removes a playdate")
|
||||
.addStringOption(GroupSelection.createOptionSetup())
|
||||
.addIntegerOption((option) => option
|
||||
.setName("playdate")
|
||||
.setDescription("Selects a playdate")
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async execute(interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
const group = GroupSelection.getGroup(interaction);
|
||||
|
||||
switch (interaction.options.getSubcommand()) {
|
||||
case "create":
|
||||
await this.create(interaction, group);
|
||||
break;
|
||||
case "remove":
|
||||
await this.delete(interaction, group);
|
||||
break;
|
||||
case "list":
|
||||
await this.list(interaction, group);
|
||||
break;
|
||||
default:
|
||||
throw new UserError("This subcommand is not yet implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
async create(interaction: CommandInteraction, group: GroupModel): Promise<void> {
|
||||
const fromDate = Date.parse(<string>interaction.options.get("from")?.value ?? '');
|
||||
const toDate = Date.parse(<string>interaction.options.get("to")?.value ?? '');
|
||||
|
||||
if (isNaN(fromDate)) {
|
||||
throw new UserError("No date or invalid date format for the from parameter.");
|
||||
}
|
||||
|
||||
if (isNaN(toDate)) {
|
||||
throw new UserError("No date or invalid date format for the to parameter.");
|
||||
}
|
||||
|
||||
const playdate: Partial<PlaydateModel> = {
|
||||
group: group,
|
||||
from_time: new Date(fromDate),
|
||||
to_time: new Date(toDate),
|
||||
}
|
||||
|
||||
const id = Container.get<PlaydateRepository>(PlaydateRepository.name).create(playdate);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("Created a play-date.")
|
||||
.setDescription(":white_check_mark: Your playdate has been created! You and your group get notified, when its time.")
|
||||
.setFooter({
|
||||
text: `Group: ${group.name}`
|
||||
})
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.name != 'playdate') {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupname = interaction.options.getString("group")
|
||||
const group = Container.get<GroupRepository>(GroupRepository.name).findGroupByName((groupname ?? '').toString());
|
||||
if (!group) {
|
||||
throw new UserError("No group found");
|
||||
}
|
||||
|
||||
const playdates = Container.get<PlaydateRepository>(PlaydateRepository.name).findFromGroup(group);
|
||||
await interaction.respond(
|
||||
playdates.map(playdate => {
|
||||
return {
|
||||
name: `${playdate.from_time.toLocaleString()} - ${playdate.to_time.toLocaleString()}`,
|
||||
value: playdate.id
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
private async list(interaction: ChatInputCommandInteraction, group: GroupModel) {
|
||||
const playdates = Container.get<PlaydateRepository>(PlaydateRepository.name).findFromGroup(group);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("The next playdates:")
|
||||
.setFields(
|
||||
playdates.map((playdate) =>
|
||||
{
|
||||
return {
|
||||
name: `${playdate.from_time.toLocaleString()} - ${playdate.to_time.toLocaleString()}`,
|
||||
value: ``
|
||||
}
|
||||
})
|
||||
)
|
||||
.setFooter({
|
||||
text: `Group: ${group.name}`
|
||||
})
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
})
|
||||
}
|
||||
|
||||
private async delete(interaction: ChatInputCommandInteraction, group: GroupModel): Promise<void> {
|
||||
const playdateId = interaction.options.getInteger("playdate", true)
|
||||
|
||||
const repo = Container.get<PlaydateRepository>(PlaydateRepository.name);
|
||||
const selected = repo.getById(playdateId);
|
||||
if (!selected) {
|
||||
throw new UserError("No playdate found");
|
||||
}
|
||||
|
||||
console.log(selected, group);
|
||||
|
||||
if (selected.group?.id != group.id) {
|
||||
throw new UserError("No playdate found");
|
||||
}
|
||||
|
||||
repo.delete(playdateId);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("Playdate deleted")
|
||||
.setDescription(
|
||||
`:x: Deleted \`${selected.from_time.toLocaleString()} - ${selected.to_time.toLocaleString()}\``
|
||||
)
|
||||
.setFooter({
|
||||
text: `Group: ${group.name}`
|
||||
})
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue