From 2ac98d8be45af7432476c255a99ced77f46c39aa Mon Sep 17 00:00:00 2001 From: igoyek Date: Sun, 10 Dec 2023 13:39:22 +0100 Subject: [PATCH 1/5] Base meeting structure Took 1 hour 54 minutes --- .../discordapp/config/AppConfig.java | 9 ++ .../meeting/MeetingNotificationType.java | 16 ++++ .../discordapp/meeting/MeetingService.java | 42 +++++++++ .../discordapp/meeting/MeetingUser.java | 37 ++++++++ .../meeting/command/MeetingCommand.java | 48 +++++++++++ .../meeting/command/child/CreateChild.java | 86 +++++++++++++++++++ .../command/child/NotificationChild.java | 30 +++++++ 7 files changed, 268 insertions(+) create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java diff --git a/src/main/java/com/eternalcode/discordapp/config/AppConfig.java b/src/main/java/com/eternalcode/discordapp/config/AppConfig.java index 44a1ffd0..76b3339e 100644 --- a/src/main/java/com/eternalcode/discordapp/config/AppConfig.java +++ b/src/main/java/com/eternalcode/discordapp/config/AppConfig.java @@ -53,6 +53,9 @@ public static class EmbedSettings { @Description("# Settings of the success embeds") public SuccessEmbed successEmbed = new SuccessEmbed(); + @Description("# Settings of the meeting embeds") + public MeetingEmbed meetingEmbed = new MeetingEmbed(); + @Contextual public static class ErrorEmbed { public String thumbnail = "https://i.imgur.com/2oTkWsr.png"; @@ -64,6 +67,12 @@ public static class SuccessEmbed { public String thumbnail = "https://i.imgur.com/QkNxIL3.png"; public String color = "#00ff77"; } + + @Contextual + public static class MeetingEmbed { + public String thumbnail = "https://imgur.com/a/fd0ukx7"; + public String color = "#62cdff"; + } } @Contextual diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java new file mode 100644 index 00000000..ed31b72e --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java @@ -0,0 +1,16 @@ +package com.eternalcode.discordapp.meeting; + +public enum MeetingNotificationType { + + DM, + SERVER, + BOTH; + + public boolean isDmNotify() { + return this == DM || this == BOTH; + } + + public boolean isServerNotify() { + return this == SERVER || this == BOTH; + } +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java new file mode 100644 index 00000000..8352eaad --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java @@ -0,0 +1,42 @@ +package com.eternalcode.discordapp.meeting; + +import com.eternalcode.discordapp.config.AppConfig; +import com.eternalcode.discordapp.config.ConfigManager; +import com.eternalcode.discordapp.meeting.command.child.CreateChild; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.Channel; + +import java.awt.*; +import java.time.Instant; +import java.util.ArrayList; + +public class MeetingService { + + private final AppConfig appConfig; + private final ConfigManager configManager; + private final CreateChild createChild; + + public MeetingService(AppConfig appConfig, ConfigManager configManager, CreateChild createChild) { + this.appConfig = appConfig; + this.configManager = configManager; + this.createChild = createChild; + } + + public void createMeeting(String title, String description, String dateTime, Member requester, Member chairperson, Channel announcementChannel) { + ArrayList presentMembers = new ArrayList<>(); + ArrayList absentMembers = new ArrayList<>(); + + MessageEmbed embed = new EmbedBuilder() + .setTitle("📅 | Meeting requested") + .setColor(Color.decode(this.appConfig.embedSettings.meetingEmbed.color)) + .setThumbnail(this.appConfig.embedSettings.meetingEmbed.thumbnail) + .setDescription("") + .addField("Present", "", true) + .addField("Absent", "", true) + .setFooter("Requested by " + requester.getUser().getName(), requester.getAvatarUrl()) + .setTimestamp(Instant.now()) + .build(); + } +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java new file mode 100644 index 00000000..2400d0eb --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java @@ -0,0 +1,37 @@ +package com.eternalcode.discordapp.meeting; + +import net.dzikoysk.cdn.entity.Contextual; +import net.dzikoysk.cdn.entity.Exclude; + +@Contextual +public class MeetingUser { + + private Long discordId; + private MeetingNotificationType notificationType; + private Boolean isPresent; + + public MeetingUser(Long discordId, MeetingNotificationType notificationType, Boolean isPresent) { + this.discordId = discordId; + this.notificationType = notificationType; + this.isPresent = isPresent; + } + + public MeetingUser() {} + + public MeetingNotificationType getNotificationType() { + return this.notificationType; + } + + @Exclude + public void setNotificationType(MeetingNotificationType notificationType) { + this.notificationType = notificationType; + } + + public Long getDiscordId() { + return this.discordId; + } + + public Boolean getIsPresent() { + return this.isPresent; + } +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java b/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java new file mode 100644 index 00000000..c3f2d602 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java @@ -0,0 +1,48 @@ +package com.eternalcode.discordapp.meeting.command; + +import com.eternalcode.discordapp.config.AppConfig; +import com.eternalcode.discordapp.meeting.MeetingService; +import com.eternalcode.discordapp.meeting.command.child.CreateChild; +import com.eternalcode.discordapp.meeting.command.child.NotificationChild; +import com.jagrosh.jdautilities.command.SlashCommand; +import com.jagrosh.jdautilities.command.SlashCommandEvent; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.MessageEmbed; + +import java.awt.*; +import java.time.Instant; + +public class MeetingCommand extends SlashCommand { + + private final AppConfig appConfig; + private final MeetingService meetingService; + + public MeetingCommand(AppConfig appConfig, MeetingService meetingService) { + this.appConfig = appConfig; + this.meetingService = meetingService; + + this.name = "meeting"; + this.help = "Sheduling meetings and reminds team members about it"; + this.userPermissions = new Permission[]{ Permission.MESSAGE_MANAGE }; + + this.children = new SlashCommand[]{ + new CreateChild(this.appConfig, this.meetingService), + new NotificationChild() + }; + } + + @Override + public void execute(SlashCommandEvent event) { + + MessageEmbed build = new EmbedBuilder() + .setTitle("📅 | Dzisiaj spotkanie!") + .setColor(Color.decode(this.appConfig.embedSettings.successEmbed.color)) + .setThumbnail(this.appConfig.embedSettings.successEmbed.thumbnail) + .addField("Będę obecny", "", true) + .addField("Nie będzie mnie", "", true) + .setFooter("Requested by " + event.getUser().getName(), event.getUser().getAvatarUrl()) + .setTimestamp(Instant.now()) + .build(); + } +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java b/src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java new file mode 100644 index 00000000..e8cdda0d --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java @@ -0,0 +1,86 @@ +package com.eternalcode.discordapp.meeting.command.child; + +import com.eternalcode.discordapp.config.AppConfig; +import com.eternalcode.discordapp.meeting.MeetingService; +import com.jagrosh.jdautilities.command.SlashCommand; +import com.jagrosh.jdautilities.command.SlashCommandEvent; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.Channel; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import net.dv8tion.jda.api.interactions.components.ActionRow; +import net.dv8tion.jda.api.interactions.components.text.TextInput; +import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; +import net.dv8tion.jda.api.interactions.modals.Modal; + +import java.util.List; + +public class CreateChild extends SlashCommand { + + private final AppConfig appConfig; + private final MeetingService meetingService; + + public CreateChild(AppConfig appConfig, MeetingService meetingService) { + this.appConfig = appConfig; + this.meetingService = meetingService; + + this.name = "create"; + this.help = "Create meeting"; + this.userPermissions = new Permission[]{ Permission.ADMINISTRATOR }; + + this.options = List.of( + new OptionData(OptionType.CHANNEL, "channel", "Notification channel") + .setRequired(false), + + new OptionData(OptionType.USER, "chairperson", "Chairperson of meeting") + .setRequired(true) + ); + } + + @Override + public void execute(SlashCommandEvent event) { + event.replyModal(this.getModal()).queue(); + + Member chairperson = event.getOption("chairperson").getAsMember(); + Channel announcementChannel = event.getOption("channel").getAsChannel(); + + this.meetingService.createMeeting(this.getSubject(), this.getDescription(), this.getDateTime(), (Member) event.getUser(), chairperson, announcementChannel); + } + + Modal getModal() { + TextInput subject = TextInput.create("subject", "Subject", TextInputStyle.SHORT) + .setPlaceholder("Subject of meeting") + .setRequiredRange(1, 100) + .setRequired(true) + .build(); + + TextInput description = TextInput.create("description", "Description", TextInputStyle.PARAGRAPH) + .setPlaceholder("Description of meeting") + .setRequiredRange(30, 1000) + .setRequired(false) + .build(); + + TextInput dateTime = TextInput.create("dateTime", "Date and time", TextInputStyle.SHORT) + .setPlaceholder("01/01/2024 19:00") + .setRequiredRange(16, 16) + .setRequired(true) + .build(); + + return Modal.create("meetingModal", "Create meeting") + .addComponents(ActionRow.of(subject), ActionRow.of(description), ActionRow.of(dateTime)) + .build(); + } + + public String getSubject() { + return this.getModal().getComponents().get(1).toString(); + } + + public String getDescription() { + return this.getModal().getComponents().get(2).toString(); + } + + public String getDateTime() { + return this.getModal().getComponents().get(3).toString(); + } +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java b/src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java new file mode 100644 index 00000000..5ea69dd6 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java @@ -0,0 +1,30 @@ +package com.eternalcode.discordapp.meeting.command.child; + +import com.eternalcode.discordapp.meeting.MeetingNotificationType; +import com.jagrosh.jdautilities.command.SlashCommand; +import com.jagrosh.jdautilities.command.SlashCommandEvent; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +import java.util.List; + +public class NotificationChild extends SlashCommand { + + public NotificationChild() { + this.name = "notification"; + + this.options = List.of( + new OptionData(OptionType.STRING, "notification-type", "type of notification send by review system") + .addChoice("DM", MeetingNotificationType.DM.toString()) + .addChoice("SERVER", MeetingNotificationType.SERVER.toString()) + .addChoice("BOTH", MeetingNotificationType.BOTH.toString()) + .setRequired(true) + ); + } + + @Override + public void execute(SlashCommandEvent slashCommandEvent) { + String notificationTypeString = slashCommandEvent.getOption("notification-type").getAsString(); + MeetingNotificationType notificationType = MeetingNotificationType.valueOf(notificationTypeString); + } +} From b72c15d5b9d88d4b560af646d3bb1c211418a008 Mon Sep 17 00:00:00 2001 From: Osnixer Date: Sun, 10 Dec 2023 15:32:42 +0100 Subject: [PATCH 2/5] Add meeting object --- .../eternalcode/discordapp/meeting/Meeting.java | 17 +++++++++++++++++ .../discordapp/meeting/MeetingUser.java | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/Meeting.java diff --git a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java new file mode 100644 index 00000000..9af54949 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java @@ -0,0 +1,17 @@ +package com.eternalcode.discordapp.meeting; + +import java.time.Instant; +import java.util.HashSet; +import java.util.Set; + +public class Meeting { + + private Instant issuedAt; + private final Set presentMembers = new HashSet<>(); + private final Set absentMembers = new HashSet<>(); + + void addPresentMemeber(long id) { + this.presentMembers.add(id); + } + +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java index 2400d0eb..d71a8833 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java @@ -6,7 +6,7 @@ @Contextual public class MeetingUser { - private Long discordId; + private long discordId; private MeetingNotificationType notificationType; private Boolean isPresent; From b8200aab73a536b05adce7245fd0eac0281f6cfc Mon Sep 17 00:00:00 2001 From: Osnixer Date: Sun, 10 Dec 2023 15:33:14 +0100 Subject: [PATCH 3/5] issuedAt -> startTime --- src/main/java/com/eternalcode/discordapp/meeting/Meeting.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java index 9af54949..11091ad1 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java @@ -6,7 +6,7 @@ public class Meeting { - private Instant issuedAt; + private Instant startTime; private final Set presentMembers = new HashSet<>(); private final Set absentMembers = new HashSet<>(); From 6f995214ccb4aa9d7075393e6add8185ed6c4a05 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sun, 10 Dec 2023 18:20:07 +0100 Subject: [PATCH 4/5] working working --- .../eternalcode/discordapp/DiscordApp.java | 12 ++- .../discordapp/command/ServerCommand.java | 2 +- .../LeaderboardButtonController.java | 9 +- .../discordapp/meeting/Meeting.java | 33 ++++++- .../discordapp/meeting/MeetingController.java | 82 ++++++++++++++++++ .../meeting/MeetingNotificationType.java | 16 ---- .../discordapp/meeting/MeetingService.java | 62 ++++++------- .../discordapp/meeting/MeetingUser.java | 37 -------- .../meeting/command/MeetingCommand.java | 64 ++++++++++---- .../meeting/command/child/CreateChild.java | 86 ------------------- .../command/child/NotificationChild.java | 30 ------- .../meeting/database/MeetingRepository.java | 15 ++++ .../database/MeetingRepositoryException.java | 8 ++ .../database/MeetingRepositoryImpl.java | 47 ++++++++++ .../meeting/database/MeetingWrapper.java | 47 ++++++++++ .../meeting/event/MeetingCreateEvent.java | 6 ++ .../discordapp/util/DiscordTagFormat.java | 11 ++- .../discordapp/util/InstantFormatUtil.java | 21 +++++ 18 files changed, 357 insertions(+), 231 deletions(-) create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java delete mode 100644 src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java delete mode 100644 src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java delete mode 100644 src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java delete mode 100644 src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepository.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryException.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/event/MeetingCreateEvent.java create mode 100644 src/main/java/com/eternalcode/discordapp/util/InstantFormatUtil.java diff --git a/src/main/java/com/eternalcode/discordapp/DiscordApp.java b/src/main/java/com/eternalcode/discordapp/DiscordApp.java index 61ebef55..097ba1b8 100644 --- a/src/main/java/com/eternalcode/discordapp/DiscordApp.java +++ b/src/main/java/com/eternalcode/discordapp/DiscordApp.java @@ -39,6 +39,10 @@ import com.eternalcode.discordapp.leveling.leaderboard.LeaderboardButtonController; import com.eternalcode.discordapp.leveling.leaderboard.LeaderboardCommand; import com.eternalcode.discordapp.leveling.leaderboard.LeaderboardService; +import com.eternalcode.discordapp.meeting.MeetingController; +import com.eternalcode.discordapp.meeting.MeetingService; +import com.eternalcode.discordapp.meeting.command.MeetingCommand; +import com.eternalcode.discordapp.meeting.event.MeetingCreateEvent; import com.eternalcode.discordapp.observer.ObserverRegistry; import com.eternalcode.discordapp.review.GitHubReviewService; import com.eternalcode.discordapp.review.GitHubReviewTask; @@ -71,6 +75,7 @@ public class DiscordApp { private static ExperienceService experienceService; private static LevelService levelService; + private static MeetingService meetingService; public static void main(String... args) throws InterruptedException { ObserverRegistry observerRegistry = new ObserverRegistry(); @@ -104,6 +109,7 @@ public static void main(String... args) throws InterruptedException { experienceService = new ExperienceService(databaseManager, observerRegistry); levelService = new LevelService(databaseManager); + meetingService = new MeetingService(databaseManager, observerRegistry); } catch (SQLException exception) { Sentry.captureException(exception); @@ -144,7 +150,10 @@ public static void main(String... args) throws InterruptedException { // Leveling new LevelCommand(levelService), - new LeaderboardCommand(leaderboardService) + new LeaderboardCommand(leaderboardService), + + // meeting + new MeetingCommand(config, meetingService) ) .build(); @@ -183,6 +192,7 @@ public static void main(String... args) throws InterruptedException { .awaitReady(); observerRegistry.observe(ExperienceChangeEvent.class, new LevelController(levelConfig, levelService, jda)); + observerRegistry.observe(MeetingCreateEvent.class, new MeetingController(jda, config)); GuildStatisticsService guildStatisticsService = new GuildStatisticsService(config, jda); diff --git a/src/main/java/com/eternalcode/discordapp/command/ServerCommand.java b/src/main/java/com/eternalcode/discordapp/command/ServerCommand.java index 94137f95..79481b82 100644 --- a/src/main/java/com/eternalcode/discordapp/command/ServerCommand.java +++ b/src/main/java/com/eternalcode/discordapp/command/ServerCommand.java @@ -30,7 +30,7 @@ public void execute(SlashCommandEvent event) { String members = String.valueOf(guild.getMembers().size()); String roles = String.valueOf(guild.getRoles().size()); String channels = String.valueOf(guild.getChannels().size()); - String createdAt = DiscordTagFormat.offsetTime(guild.getTimeCreated()); + String createdAt = DiscordTagFormat.toDiscordDate(guild.getTimeCreated()); MessageEmbed embeds = new EmbedBuilder() .setTitle("🌐 | " + guild.getName() + "'s information") diff --git a/src/main/java/com/eternalcode/discordapp/leveling/leaderboard/LeaderboardButtonController.java b/src/main/java/com/eternalcode/discordapp/leveling/leaderboard/LeaderboardButtonController.java index 15e1b193..e6599e60 100644 --- a/src/main/java/com/eternalcode/discordapp/leveling/leaderboard/LeaderboardButtonController.java +++ b/src/main/java/com/eternalcode/discordapp/leveling/leaderboard/LeaderboardButtonController.java @@ -31,7 +31,8 @@ public void onButtonInteraction(ButtonInteractionEvent event) { int totalRecords = this.leaderboardService.getTotalRecords(); int totalPages = this.leaderboardService.getTotalPages(totalRecords); - int currentPage = this.currentPageMap.getOrDefault(messageId, 1); + int oldPage = this.currentPageMap.getOrDefault(messageId, 1); + int currentPage = oldPage; if (componentId.equals("leaderboard_next")) { currentPage++; @@ -50,9 +51,11 @@ public void onButtonInteraction(ButtonInteractionEvent event) { } currentPage = Math.max(1, Math.min(currentPage, totalPages)); - this.currentPageMap.put(messageId, currentPage); - this.updateLeaderboard(event, currentPage); + if (oldPage != currentPage) { + this.currentPageMap.put(messageId, currentPage); + this.updateLeaderboard(event, currentPage); + } } private void updateLeaderboard(ButtonInteractionEvent event, int currentPage) { diff --git a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java index 11091ad1..9410798e 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java @@ -6,12 +6,37 @@ public class Meeting { + private Set presentMembers = new HashSet<>(); + private Set absentMembers = new HashSet<>(); + + private Long requesterId; + + private Instant issuedAt; private Instant startTime; - private final Set presentMembers = new HashSet<>(); - private final Set absentMembers = new HashSet<>(); - void addPresentMemeber(long id) { - this.presentMembers.add(id); + public Meeting(Long requesterId, Instant issuedAt, Instant startTime) { + this.requesterId = requesterId; + this.issuedAt = issuedAt; + this.startTime = startTime; + } + + public Instant getIssuedAt() { + return issuedAt; + } + + public Instant getStartTime() { + return startTime; } + public Long getRequesterId() { + return requesterId; + } + + public Set getPresentMembers() { + return presentMembers; + } + + public Set getAbsentMembers() { + return absentMembers; + } } diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java new file mode 100644 index 00000000..6ba36a8a --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java @@ -0,0 +1,82 @@ +package com.eternalcode.discordapp.meeting; + +import com.eternalcode.discordapp.config.AppConfig; +import com.eternalcode.discordapp.meeting.event.MeetingCreateEvent; +import com.eternalcode.discordapp.observer.Observer; +import com.eternalcode.discordapp.util.DiscordTagFormat; +import com.eternalcode.discordapp.util.InstantFormatUtil; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.interactions.components.buttons.Button; + +import java.awt.*; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; + +public class MeetingController implements Observer { + + private final JDA jda; + private final AppConfig appConfig; + + public MeetingController(JDA jda, AppConfig appConfig) { + this.jda = jda; + this.appConfig = appConfig; + } + + @Override + public void update(MeetingCreateEvent event) { + Meeting meeting = event.meeting(); + Long channelId = event.channelId(); + + TextChannel textChannelById = this.jda.getTextChannelById(channelId); + + if (textChannelById == null) { + return; + } + + Long requesterId = meeting.getRequesterId(); + + Member memberById = this.jda.getGuildById(this.appConfig.guildId) + .getMemberById(requesterId); + + if (memberById == null) { + return; + } + + String requesterName = memberById.getEffectiveName(); + + // TODO: add everyone ping at created meeting + + Instant startTime = meeting.getStartTime(); + ZoneOffset offset = ZoneId.systemDefault().getRules().getOffset(startTime); + + String timeFieldValue = String.format("%s (%s)", + InstantFormatUtil.format(startTime), + DiscordTagFormat.toDiscordWhen(startTime.atOffset(offset))); + + MessageEmbed embed = new EmbedBuilder() + .setTitle("📅 | Meeting requested") + .setColor(Color.decode(this.appConfig.embedSettings.meetingEmbed.color)) + .setThumbnail(this.appConfig.embedSettings.meetingEmbed.thumbnail) + .setDescription("TODO: Some description, dodałbym wzmiankę o przewodniczącym spotkania") + .addField("Meeting Leader:", requesterName, false) + .addField("Time:", timeFieldValue, false) + .addField("Present", "", true) + .addField("Absent", "", true) + .setTimestamp(meeting.getIssuedAt()) + .build(); + + Button presentButton = Button.primary("meeting:present:" + meeting.getIssuedAt().toEpochMilli(), "Present"); + Button absentButton = Button.danger("meeting:absent:" + meeting.getIssuedAt().toEpochMilli(), "Absent"); + + textChannelById + .sendMessageEmbeds(embed) + .addActionRow(presentButton, absentButton) + .queue(); + } + +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java deleted file mode 100644 index ed31b72e..00000000 --- a/src/main/java/com/eternalcode/discordapp/meeting/MeetingNotificationType.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.eternalcode.discordapp.meeting; - -public enum MeetingNotificationType { - - DM, - SERVER, - BOTH; - - public boolean isDmNotify() { - return this == DM || this == BOTH; - } - - public boolean isServerNotify() { - return this == SERVER || this == BOTH; - } -} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java index 8352eaad..7f0414c3 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java @@ -1,42 +1,42 @@ package com.eternalcode.discordapp.meeting; -import com.eternalcode.discordapp.config.AppConfig; -import com.eternalcode.discordapp.config.ConfigManager; -import com.eternalcode.discordapp.meeting.command.child.CreateChild; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.channel.Channel; - -import java.awt.*; +import com.eternalcode.discordapp.database.DatabaseManager; +import com.eternalcode.discordapp.meeting.database.MeetingRepository; +import com.eternalcode.discordapp.meeting.database.MeetingRepositoryImpl; +import com.eternalcode.discordapp.meeting.event.MeetingCreateEvent; +import com.eternalcode.discordapp.observer.ObserverRegistry; + import java.time.Instant; -import java.util.ArrayList; public class MeetingService { - private final AppConfig appConfig; - private final ConfigManager configManager; - private final CreateChild createChild; + private final MeetingRepository meetingRepository; + private final ObserverRegistry observerRegistry; + + public MeetingService(DatabaseManager databaseManager, ObserverRegistry observerRegistry) { + this.meetingRepository = MeetingRepositoryImpl.create(databaseManager); + this.observerRegistry = observerRegistry; + } + + public void createMeeting(Instant issuedAt, Instant startTime, Long requester, Long channelId) { + Meeting meeting = new Meeting(requester, issuedAt, startTime); + + this.meetingRepository.saveMeeting(meeting); + this.observerRegistry.publish(new MeetingCreateEvent(meeting, requester, channelId)); + } + + public void deleteMeeting(Instant issuedAt, Instant startTime, Long requester) { + Meeting meeting = new Meeting(requester, issuedAt, startTime); - public MeetingService(AppConfig appConfig, ConfigManager configManager, CreateChild createChild) { - this.appConfig = appConfig; - this.configManager = configManager; - this.createChild = createChild; + this.meetingRepository.deleteMeeting(meeting); } - public void createMeeting(String title, String description, String dateTime, Member requester, Member chairperson, Channel announcementChannel) { - ArrayList presentMembers = new ArrayList<>(); - ArrayList absentMembers = new ArrayList<>(); - - MessageEmbed embed = new EmbedBuilder() - .setTitle("📅 | Meeting requested") - .setColor(Color.decode(this.appConfig.embedSettings.meetingEmbed.color)) - .setThumbnail(this.appConfig.embedSettings.meetingEmbed.thumbnail) - .setDescription("") - .addField("Present", "", true) - .addField("Absent", "", true) - .setFooter("Requested by " + requester.getUser().getName(), requester.getAvatarUrl()) - .setTimestamp(Instant.now()) - .build(); + public void findMeeting(Instant issuedAt, Instant startTime, Long requester) { + Meeting meeting = new Meeting(requester, issuedAt, startTime); + + this.meetingRepository.findMeeting(meeting); } + + //zw + } diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java deleted file mode 100644 index d71a8833..00000000 --- a/src/main/java/com/eternalcode/discordapp/meeting/MeetingUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.eternalcode.discordapp.meeting; - -import net.dzikoysk.cdn.entity.Contextual; -import net.dzikoysk.cdn.entity.Exclude; - -@Contextual -public class MeetingUser { - - private long discordId; - private MeetingNotificationType notificationType; - private Boolean isPresent; - - public MeetingUser(Long discordId, MeetingNotificationType notificationType, Boolean isPresent) { - this.discordId = discordId; - this.notificationType = notificationType; - this.isPresent = isPresent; - } - - public MeetingUser() {} - - public MeetingNotificationType getNotificationType() { - return this.notificationType; - } - - @Exclude - public void setNotificationType(MeetingNotificationType notificationType) { - this.notificationType = notificationType; - } - - public Long getDiscordId() { - return this.discordId; - } - - public Boolean getIsPresent() { - return this.isPresent; - } -} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java b/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java index c3f2d602..b961c0ef 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/command/MeetingCommand.java @@ -2,16 +2,18 @@ import com.eternalcode.discordapp.config.AppConfig; import com.eternalcode.discordapp.meeting.MeetingService; -import com.eternalcode.discordapp.meeting.command.child.CreateChild; -import com.eternalcode.discordapp.meeting.command.child.NotificationChild; import com.jagrosh.jdautilities.command.SlashCommand; import com.jagrosh.jdautilities.command.SlashCommandEvent; -import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import java.awt.*; import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; +import java.util.List; public class MeetingCommand extends SlashCommand { @@ -24,25 +26,49 @@ public MeetingCommand(AppConfig appConfig, MeetingService meetingService) { this.name = "meeting"; this.help = "Sheduling meetings and reminds team members about it"; - this.userPermissions = new Permission[]{ Permission.MESSAGE_MANAGE }; + this.userPermissions = new Permission[]{ Permission.ADMINISTRATOR }; - this.children = new SlashCommand[]{ - new CreateChild(this.appConfig, this.meetingService), - new NotificationChild() - }; + this.options = List.of( + new OptionData(OptionType.STRING, "time", "time of the meeting e.g: 21:00") + .setRequired(true) + ); } @Override public void execute(SlashCommandEvent event) { + String time = event.getOption("time") != null ? event.getOption("time").getAsString() : null; - MessageEmbed build = new EmbedBuilder() - .setTitle("📅 | Dzisiaj spotkanie!") - .setColor(Color.decode(this.appConfig.embedSettings.successEmbed.color)) - .setThumbnail(this.appConfig.embedSettings.successEmbed.thumbnail) - .addField("Będę obecny", "", true) - .addField("Nie będzie mnie", "", true) - .setFooter("Requested by " + event.getUser().getName(), event.getUser().getAvatarUrl()) - .setTimestamp(Instant.now()) - .build(); + if (time == null) { + event.reply("You need to specify time of the meeting e.g: 21:00") + .setEphemeral(true) + .queue(); + return; + } + + LocalTime localTime; + try { + localTime = LocalTime.parse(time); + } + catch (DateTimeParseException exception) { + event.reply("Invalid time format, you can use 24h format e.g: 21:00, 12:00, 21:30 etc.") + .setEphemeral(true) + .queue(); + return; + } + + Instant meetingTime = localTime.atDate(LocalDate.now()).atZone(ZoneId.systemDefault()).toInstant(); + + if (meetingTime.isBefore(Instant.now())) { + event.reply("Meeting time cannot be in the past") + .setEphemeral(true) + .queue(); + return; + } + + this.meetingService.createMeeting(Instant.now(), meetingTime, event.getUser().getIdLong(), event.getChannel().getIdLong()); + + event.reply("Meeting created") + .setEphemeral(true) + .queue(); } } diff --git a/src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java b/src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java deleted file mode 100644 index e8cdda0d..00000000 --- a/src/main/java/com/eternalcode/discordapp/meeting/command/child/CreateChild.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.eternalcode.discordapp.meeting.command.child; - -import com.eternalcode.discordapp.config.AppConfig; -import com.eternalcode.discordapp.meeting.MeetingService; -import com.jagrosh.jdautilities.command.SlashCommand; -import com.jagrosh.jdautilities.command.SlashCommandEvent; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.channel.Channel; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; - -import java.util.List; - -public class CreateChild extends SlashCommand { - - private final AppConfig appConfig; - private final MeetingService meetingService; - - public CreateChild(AppConfig appConfig, MeetingService meetingService) { - this.appConfig = appConfig; - this.meetingService = meetingService; - - this.name = "create"; - this.help = "Create meeting"; - this.userPermissions = new Permission[]{ Permission.ADMINISTRATOR }; - - this.options = List.of( - new OptionData(OptionType.CHANNEL, "channel", "Notification channel") - .setRequired(false), - - new OptionData(OptionType.USER, "chairperson", "Chairperson of meeting") - .setRequired(true) - ); - } - - @Override - public void execute(SlashCommandEvent event) { - event.replyModal(this.getModal()).queue(); - - Member chairperson = event.getOption("chairperson").getAsMember(); - Channel announcementChannel = event.getOption("channel").getAsChannel(); - - this.meetingService.createMeeting(this.getSubject(), this.getDescription(), this.getDateTime(), (Member) event.getUser(), chairperson, announcementChannel); - } - - Modal getModal() { - TextInput subject = TextInput.create("subject", "Subject", TextInputStyle.SHORT) - .setPlaceholder("Subject of meeting") - .setRequiredRange(1, 100) - .setRequired(true) - .build(); - - TextInput description = TextInput.create("description", "Description", TextInputStyle.PARAGRAPH) - .setPlaceholder("Description of meeting") - .setRequiredRange(30, 1000) - .setRequired(false) - .build(); - - TextInput dateTime = TextInput.create("dateTime", "Date and time", TextInputStyle.SHORT) - .setPlaceholder("01/01/2024 19:00") - .setRequiredRange(16, 16) - .setRequired(true) - .build(); - - return Modal.create("meetingModal", "Create meeting") - .addComponents(ActionRow.of(subject), ActionRow.of(description), ActionRow.of(dateTime)) - .build(); - } - - public String getSubject() { - return this.getModal().getComponents().get(1).toString(); - } - - public String getDescription() { - return this.getModal().getComponents().get(2).toString(); - } - - public String getDateTime() { - return this.getModal().getComponents().get(3).toString(); - } -} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java b/src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java deleted file mode 100644 index 5ea69dd6..00000000 --- a/src/main/java/com/eternalcode/discordapp/meeting/command/child/NotificationChild.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.eternalcode.discordapp.meeting.command.child; - -import com.eternalcode.discordapp.meeting.MeetingNotificationType; -import com.jagrosh.jdautilities.command.SlashCommand; -import com.jagrosh.jdautilities.command.SlashCommandEvent; -import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; - -import java.util.List; - -public class NotificationChild extends SlashCommand { - - public NotificationChild() { - this.name = "notification"; - - this.options = List.of( - new OptionData(OptionType.STRING, "notification-type", "type of notification send by review system") - .addChoice("DM", MeetingNotificationType.DM.toString()) - .addChoice("SERVER", MeetingNotificationType.SERVER.toString()) - .addChoice("BOTH", MeetingNotificationType.BOTH.toString()) - .setRequired(true) - ); - } - - @Override - public void execute(SlashCommandEvent slashCommandEvent) { - String notificationTypeString = slashCommandEvent.getOption("notification-type").getAsString(); - MeetingNotificationType notificationType = MeetingNotificationType.valueOf(notificationTypeString); - } -} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepository.java b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepository.java new file mode 100644 index 00000000..0c75fa4e --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepository.java @@ -0,0 +1,15 @@ +package com.eternalcode.discordapp.meeting.database; + +import com.eternalcode.discordapp.meeting.Meeting; + +import java.util.concurrent.CompletableFuture; + +public interface MeetingRepository { + + CompletableFuture findMeeting(Meeting meeting); + + CompletableFuture saveMeeting(Meeting meeting); + + CompletableFuture deleteMeeting(Meeting meeting); + +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryException.java b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryException.java new file mode 100644 index 00000000..d84204c7 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryException.java @@ -0,0 +1,8 @@ +package com.eternalcode.discordapp.meeting.database; + +public class MeetingRepositoryException extends RuntimeException { + + public MeetingRepositoryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java new file mode 100644 index 00000000..7f40e1f1 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java @@ -0,0 +1,47 @@ +package com.eternalcode.discordapp.meeting.database; + +import com.eternalcode.discordapp.database.DataAccessException; +import com.eternalcode.discordapp.database.DatabaseManager; +import com.eternalcode.discordapp.database.repository.AbstractRepository; +import com.eternalcode.discordapp.meeting.Meeting; +import com.j256.ormlite.table.TableUtils; +import io.sentry.Sentry; + +import java.sql.SQLException; +import java.util.concurrent.CompletableFuture; + +public class MeetingRepositoryImpl extends AbstractRepository implements MeetingRepository { + + protected MeetingRepositoryImpl(DatabaseManager databaseManager) { + super(databaseManager, Meeting.class); + } + + public static MeetingRepository create(DatabaseManager databaseManager) { + try { + TableUtils.createTableIfNotExists(databaseManager.getConnectionSource(), MeetingWrapper.class); + } + catch (SQLException sqlException) { + Sentry.captureException(sqlException); + throw new DataAccessException("Failed to create table", sqlException); + } + + + return new MeetingRepositoryImpl(databaseManager); + } + + @Override + public CompletableFuture findMeeting(Meeting meeting) { + return null; + } + + @Override + public CompletableFuture saveMeeting(Meeting meeting) { + return null; + } + + @Override + public CompletableFuture deleteMeeting(Meeting meeting) { + return null; + } + +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java new file mode 100644 index 00000000..591763de --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java @@ -0,0 +1,47 @@ +package com.eternalcode.discordapp.meeting.database; + +import com.eternalcode.discordapp.meeting.Meeting; +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import java.time.Instant; + +@DatabaseTable(tableName = "officer_meetings") +class MeetingWrapper { + + @DatabaseField(id = true) + private long id; + + @DatabaseField(dataType = DataType.LONG) + private long requesterId; + + @DatabaseField(columnName = "issuedAt", dataType = DataType.SERIALIZABLE) + private Instant issuedAt; + + @DatabaseField(columnName = "startTime", dataType = DataType.SERIALIZABLE) + private Instant startTime; +/* + @DatabaseField(dataType = DataType.SERIALIZABLE) + private final Set presentMembers = new HashSet<>(); + + @DatabaseField(dataType = DataType.SERIALIZABLE) + private final Set absentMembers = new HashSet<>();*/ + + public MeetingWrapper(Instant issuedAt, Instant startTime) { + this.issuedAt = issuedAt; + this.startTime = startTime; + } + + public MeetingWrapper() { + } + + public static MeetingWrapper from(Meeting meeting) { + return new MeetingWrapper(meeting.getIssuedAt(), meeting.getStartTime()); + } + + public Meeting toMeeting() { + return new Meeting(this.requesterId, this.issuedAt, this.startTime); + } + +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingCreateEvent.java b/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingCreateEvent.java new file mode 100644 index 00000000..b04c7138 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingCreateEvent.java @@ -0,0 +1,6 @@ +package com.eternalcode.discordapp.meeting.event; + +import com.eternalcode.discordapp.meeting.Meeting; + +public record MeetingCreateEvent(Meeting meeting, Long requester, Long channelId) { +} diff --git a/src/main/java/com/eternalcode/discordapp/util/DiscordTagFormat.java b/src/main/java/com/eternalcode/discordapp/util/DiscordTagFormat.java index db85c1f0..09b4cba6 100644 --- a/src/main/java/com/eternalcode/discordapp/util/DiscordTagFormat.java +++ b/src/main/java/com/eternalcode/discordapp/util/DiscordTagFormat.java @@ -7,7 +7,8 @@ public final class DiscordTagFormat { private static final String MEMBER_TAG = "<@%s>"; - private static final String TIMESTAMP_TAG = ""; + private static final String TIMESTAMP_TAG_FULL_DATE = ""; + private static final String TIMESTAMP_TAG_WHEN = ""; private DiscordTagFormat() { throw new UnsupportedOperationException("This class cannot be instantiated"); @@ -17,8 +18,12 @@ public static String memberTag(User user) { return String.format(MEMBER_TAG, user.getId()); } - public static String offsetTime(OffsetDateTime offsetDateTime) { - return String.format(TIMESTAMP_TAG, offsetDateTime.toEpochSecond()); + public static String toDiscordDate(OffsetDateTime offsetDateTime) { + return String.format(TIMESTAMP_TAG_FULL_DATE, offsetDateTime.toEpochSecond()); + } + + public static String toDiscordWhen(OffsetDateTime offsetDateTime) { + return String.format(TIMESTAMP_TAG_WHEN, offsetDateTime.toEpochSecond()); } } diff --git a/src/main/java/com/eternalcode/discordapp/util/InstantFormatUtil.java b/src/main/java/com/eternalcode/discordapp/util/InstantFormatUtil.java new file mode 100644 index 00000000..7d690fa1 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/util/InstantFormatUtil.java @@ -0,0 +1,21 @@ +package com.eternalcode.discordapp.util; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +public final class InstantFormatUtil { + + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter + .ofPattern("dd.MM.yyyy HH:mm", Locale.ROOT) + .withZone(ZoneId.systemDefault()); + + private InstantFormatUtil() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static String format(Instant instant) { + return DATE_FORMAT.format(instant); + } +} From fce3e115e19601e2c426e6faae476b1bcf42c9bf Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sun, 10 Dec 2023 19:02:41 +0100 Subject: [PATCH 5/5] next step of work --- .../eternalcode/discordapp/DiscordApp.java | 6 ++++-- .../discordapp/meeting/Meeting.java | 14 +++++++------ .../discordapp/meeting/MeetingService.java | 19 +++++------------ .../controller/MeetingButtonController.java | 21 +++++++++++++++++++ .../{ => controller}/MeetingController.java | 10 ++++++--- .../database/MeetingRepositoryImpl.java | 9 ++++---- .../meeting/database/MeetingWrapper.java | 17 ++++++++------- .../event/MeetingMemberAbsentEvent.java | 4 ++++ .../event/MeetingMemberPresentEvent.java | 4 ++++ 9 files changed, 67 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingButtonController.java rename src/main/java/com/eternalcode/discordapp/meeting/{ => controller}/MeetingController.java (88%) create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberAbsentEvent.java create mode 100644 src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberPresentEvent.java diff --git a/src/main/java/com/eternalcode/discordapp/DiscordApp.java b/src/main/java/com/eternalcode/discordapp/DiscordApp.java index 097ba1b8..d295e1a9 100644 --- a/src/main/java/com/eternalcode/discordapp/DiscordApp.java +++ b/src/main/java/com/eternalcode/discordapp/DiscordApp.java @@ -39,7 +39,7 @@ import com.eternalcode.discordapp.leveling.leaderboard.LeaderboardButtonController; import com.eternalcode.discordapp.leveling.leaderboard.LeaderboardCommand; import com.eternalcode.discordapp.leveling.leaderboard.LeaderboardService; -import com.eternalcode.discordapp.meeting.MeetingController; +import com.eternalcode.discordapp.meeting.controller.MeetingController; import com.eternalcode.discordapp.meeting.MeetingService; import com.eternalcode.discordapp.meeting.command.MeetingCommand; import com.eternalcode.discordapp.meeting.event.MeetingCreateEvent; @@ -65,7 +65,6 @@ import java.io.File; import java.sql.SQLException; import java.time.Duration; -import java.time.Instant; import java.util.EnumSet; import java.util.Timer; @@ -178,6 +177,9 @@ public static void main(String... args) throws InterruptedException { // leaderboard new LeaderboardButtonController(leaderboardService) + + // meeting +/* new MeetingButtonController()*/ ) .setAutoReconnect(true) diff --git a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java index 9410798e..a38c2321 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/Meeting.java @@ -6,18 +6,20 @@ public class Meeting { - private Set presentMembers = new HashSet<>(); - private Set absentMembers = new HashSet<>(); + private final Set presentMembers = new HashSet<>(); + private final Set absentMembers = new HashSet<>(); - private Long requesterId; + private final Long requesterId; - private Instant issuedAt; - private Instant startTime; + private final Instant issuedAt; + private final Instant startTime; - public Meeting(Long requesterId, Instant issuedAt, Instant startTime) { + public Meeting(Long requesterId, Instant issuedAt, Instant startTime, Set presentMembers, Set absentMembers) { this.requesterId = requesterId; this.issuedAt = issuedAt; this.startTime = startTime; + this.presentMembers.addAll(presentMembers); + this.absentMembers.addAll(absentMembers); } public Instant getIssuedAt() { diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java b/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java index 7f0414c3..3db54a13 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/MeetingService.java @@ -19,24 +19,15 @@ public MeetingService(DatabaseManager databaseManager, ObserverRegistry observer } public void createMeeting(Instant issuedAt, Instant startTime, Long requester, Long channelId) { - Meeting meeting = new Meeting(requester, issuedAt, startTime); + Meeting meeting = new Meeting(requester, issuedAt, startTime, null, null); this.meetingRepository.saveMeeting(meeting); this.observerRegistry.publish(new MeetingCreateEvent(meeting, requester, channelId)); } - public void deleteMeeting(Instant issuedAt, Instant startTime, Long requester) { - Meeting meeting = new Meeting(requester, issuedAt, startTime); - - this.meetingRepository.deleteMeeting(meeting); - } - - public void findMeeting(Instant issuedAt, Instant startTime, Long requester) { - Meeting meeting = new Meeting(requester, issuedAt, startTime); - - this.meetingRepository.findMeeting(meeting); - } - - //zw + // TODO; tutaj sie zrobi metode od update i create embed, a z obiektu z bazy bedzie sie pobierac absent i present osoby + // TODO: i bedzie sie je dodawac do embeda, a potem edytowac embed juz wczesniej wyslany za pomoca metody od update + // TODO: i sa eventy MeetinbMemberAbsentEvent ii MeetingMemberPresent i bedzie sie je nasluchiwac i dodawac do listy albo present albo absent + // TODO: ten kod co napisalem juz to troche sensu w niektorych momentach nie ma, ale ogolnie to jest to co trzeba zrobic ^^, jak ktos chce to niech zrobi } diff --git a/src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingButtonController.java b/src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingButtonController.java new file mode 100644 index 00000000..28709cc5 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingButtonController.java @@ -0,0 +1,21 @@ +package com.eternalcode.discordapp.meeting.controller; + +import com.eternalcode.discordapp.meeting.database.MeetingRepository; +import com.eternalcode.discordapp.meeting.database.MeetingRepositoryImpl; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +public class MeetingButtonController extends ListenerAdapter { + + private final MeetingRepository meetingRepository; + + public MeetingButtonController(MeetingRepository meetingRepository) { + this.meetingRepository = meetingRepository; + } + + @Override + public void onButtonInteraction(ButtonInteractionEvent event) { + + } +} + diff --git a/src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java b/src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingController.java similarity index 88% rename from src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java rename to src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingController.java index 6ba36a8a..40691347 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/MeetingController.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/controller/MeetingController.java @@ -1,6 +1,7 @@ -package com.eternalcode.discordapp.meeting; +package com.eternalcode.discordapp.meeting.controller; import com.eternalcode.discordapp.config.AppConfig; +import com.eternalcode.discordapp.meeting.Meeting; import com.eternalcode.discordapp.meeting.event.MeetingCreateEvent; import com.eternalcode.discordapp.observer.Observer; import com.eternalcode.discordapp.util.DiscordTagFormat; @@ -10,6 +11,7 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.interactions.components.buttons.Button; import java.awt.*; @@ -70,8 +72,10 @@ public void update(MeetingCreateEvent event) { .setTimestamp(meeting.getIssuedAt()) .build(); - Button presentButton = Button.primary("meeting:present:" + meeting.getIssuedAt().toEpochMilli(), "Present"); - Button absentButton = Button.danger("meeting:absent:" + meeting.getIssuedAt().toEpochMilli(), "Absent"); + Button presentButton = Button.primary("meeting:present:" + meeting.getIssuedAt().toEpochMilli(), "Present") + .withEmoji(Emoji.fromUnicode("✅")); + Button absentButton = Button.danger("meeting:absent:" + meeting.getIssuedAt().toEpochMilli(), "Absent") + .withEmoji(Emoji.fromUnicode("✖")); textChannelById .sendMessageEmbeds(embed) diff --git a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java index 7f40e1f1..b44d00aa 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingRepositoryImpl.java @@ -8,6 +8,7 @@ import io.sentry.Sentry; import java.sql.SQLException; +import java.util.Optional; import java.util.concurrent.CompletableFuture; public class MeetingRepositoryImpl extends AbstractRepository implements MeetingRepository { @@ -25,23 +26,21 @@ public static MeetingRepository create(DatabaseManager databaseManager) { throw new DataAccessException("Failed to create table", sqlException); } - return new MeetingRepositoryImpl(databaseManager); } @Override public CompletableFuture findMeeting(Meeting meeting) { - return null; + return this.select(meeting.getRequesterId()).thenApply(Optional::get); } @Override public CompletableFuture saveMeeting(Meeting meeting) { - return null; + return this.save(meeting).thenApply(status -> meeting); } @Override public CompletableFuture deleteMeeting(Meeting meeting) { - return null; + return this.delete(meeting).thenApply(deleted -> meeting); } - } diff --git a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java index 591763de..f74086cd 100644 --- a/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java +++ b/src/main/java/com/eternalcode/discordapp/meeting/database/MeetingWrapper.java @@ -6,6 +6,8 @@ import com.j256.ormlite.table.DatabaseTable; import java.time.Instant; +import java.util.HashSet; +import java.util.Set; @DatabaseTable(tableName = "officer_meetings") class MeetingWrapper { @@ -21,14 +23,15 @@ class MeetingWrapper { @DatabaseField(columnName = "startTime", dataType = DataType.SERIALIZABLE) private Instant startTime; -/* - @DatabaseField(dataType = DataType.SERIALIZABLE) + + @DatabaseField(columnName = "presentMembers", dataType = DataType.SERIALIZABLE) private final Set presentMembers = new HashSet<>(); - @DatabaseField(dataType = DataType.SERIALIZABLE) - private final Set absentMembers = new HashSet<>();*/ + @DatabaseField(columnName = "absentMembers", dataType = DataType.SERIALIZABLE) + private final Set absentMembers = new HashSet<>(); - public MeetingWrapper(Instant issuedAt, Instant startTime) { + public MeetingWrapper(long requesterId, Instant issuedAt, Instant startTime) { + this.requesterId = requesterId; this.issuedAt = issuedAt; this.startTime = startTime; } @@ -37,11 +40,11 @@ public MeetingWrapper() { } public static MeetingWrapper from(Meeting meeting) { - return new MeetingWrapper(meeting.getIssuedAt(), meeting.getStartTime()); + return new MeetingWrapper(meeting.getRequesterId(), meeting.getIssuedAt(), meeting.getStartTime()); } public Meeting toMeeting() { - return new Meeting(this.requesterId, this.issuedAt, this.startTime); + return new Meeting(this.requesterId, this.issuedAt, this.startTime, this.presentMembers, this.absentMembers); } } diff --git a/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberAbsentEvent.java b/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberAbsentEvent.java new file mode 100644 index 00000000..7d93b055 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberAbsentEvent.java @@ -0,0 +1,4 @@ +package com.eternalcode.discordapp.meeting.event; + +public record MeetingMemberAbsentEvent(Long absentUserId) { +} diff --git a/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberPresentEvent.java b/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberPresentEvent.java new file mode 100644 index 00000000..e0ad0144 --- /dev/null +++ b/src/main/java/com/eternalcode/discordapp/meeting/event/MeetingMemberPresentEvent.java @@ -0,0 +1,4 @@ +package com.eternalcode.discordapp.meeting.event; + +public record MeetingMemberPresentEvent(Long presentUserId) { +}