Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New reminding system #140

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/main/java/com/eternalcode/discordapp/DiscordApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.controller.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;
Expand All @@ -61,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;

Expand All @@ -71,6 +74,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();
Expand Down Expand Up @@ -104,6 +108,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);
Expand Down Expand Up @@ -144,7 +149,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();

Expand All @@ -169,6 +177,9 @@ public static void main(String... args) throws InterruptedException {

// leaderboard
new LeaderboardButtonController(leaderboardService)

// meeting
/* new MeetingButtonController()*/
)

.setAutoReconnect(true)
Expand All @@ -183,6 +194,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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
Expand All @@ -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) {
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/com/eternalcode/discordapp/meeting/Meeting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.eternalcode.discordapp.meeting;

import java.time.Instant;
import java.util.HashSet;
import java.util.Set;

public class Meeting {

private final Set<Long> presentMembers = new HashSet<>();
private final Set<Long> absentMembers = new HashSet<>();

private final Long requesterId;

private final Instant issuedAt;
private final Instant startTime;

public Meeting(Long requesterId, Instant issuedAt, Instant startTime, Set<Long> presentMembers, Set<Long> absentMembers) {
this.requesterId = requesterId;
this.issuedAt = issuedAt;
this.startTime = startTime;
this.presentMembers.addAll(presentMembers);
this.absentMembers.addAll(absentMembers);
}

public Instant getIssuedAt() {
return issuedAt;
}

public Instant getStartTime() {
return startTime;
}

public Long getRequesterId() {
return requesterId;
}

public Set<Long> getPresentMembers() {
return presentMembers;
}

public Set<Long> getAbsentMembers() {
return absentMembers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.eternalcode.discordapp.meeting;

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;

public class MeetingService {

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, null, null);

this.meetingRepository.saveMeeting(meeting);
this.observerRegistry.publish(new MeetingCreateEvent(meeting, requester, channelId));
}

// 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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.eternalcode.discordapp.meeting.command;

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.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;

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 {

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.ADMINISTRATOR };

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;

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();
}
}
Original file line number Diff line number Diff line change
@@ -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) {

}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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;
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.entities.emoji.Emoji;
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<MeetingCreateEvent> {

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")
.withEmoji(Emoji.fromUnicode("✅"));
Button absentButton = Button.danger("meeting:absent:" + meeting.getIssuedAt().toEpochMilli(), "Absent")
.withEmoji(Emoji.fromUnicode("✖"));

textChannelById
.sendMessageEmbeds(embed)
.addActionRow(presentButton, absentButton)
.queue();
}

}
Original file line number Diff line number Diff line change
@@ -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<Meeting> findMeeting(Meeting meeting);

CompletableFuture<Meeting> saveMeeting(Meeting meeting);

CompletableFuture<Meeting> deleteMeeting(Meeting meeting);

}
Loading
Loading