python_loser
python_loser

Reputation: 11

How to send message at specific time on Discord using Discord JDA in Java

I am trying to get my discord bot to send a message at specific times. I am utilizing the onGuildAvailable(GuildAvailableEvent event) method in Discord JDA's ListenerAdapter. I have also tried onGuildReady(GuildReadyEvent event), but that doesn't seem to work either. Any help is appreciated. Here is my code thus far:

private static GuildAvailableEvent e;
private static final Timer timer = new Timer(1000, new Listener());

public void onGuildAvailable(GuildAvailableEvent event) {
    e = event;
    timer.setRepeats(true);
    timer.start();
    timer.restart();
}

private static class Listener implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("e HH:mm");
        String time = dtf.format(LocalDateTime.now());
        if(time.charAt(0) == '0' || time.charAt(0) == '2' || time.charAt(0) == '3' || time.charAt(0) == '4' || time.charAt(0) == '5') {
            String message = "Class is starting! Get to class!";
            if(time.substring(2, time.length() - 1).equalsIgnoreCase("08:05")) {
                Objects.requireNonNull(e.getGuild().getDefaultChannel()).sendMessage(message).queue();
            } else if(time.substring(2, time.length() - 1).equalsIgnoreCase("09:25")) {
                Objects.requireNonNull(e.getGuild().getDefaultChannel()).sendMessage(message).queue();
            } else if(time.substring(2, time.length() - 1).equalsIgnoreCase("11:55")) {
                Objects.requireNonNull(e.getGuild().getDefaultChannel()).sendMessage(message).queue();
            } else if(time.substring(2, time.length() - 1).equalsIgnoreCase("13:30")) {
                Objects.requireNonNull(e.getGuild().getDefaultChannel()).sendMessage(message).queue();
            } else if(time.substring(2, time.length() - 1).equalsIgnoreCase("15:39")) { // test time
                Objects.requireNonNull(e.getGuild().getDefaultChannel()).sendMessage(message).queue();
            }
        }
    }
}

Upvotes: 1

Views: 2881

Answers (2)

Bitrot
Bitrot

Reputation: 54

Here is a working, readable & aesthetic solution for your problem:

public class TestListener extends ListenerAdapter {

private final ScheduledExecutorService scheduler;
private final Set<LocalTime> targetTimes;
private final ZoneId zoneId = ZoneId.of("Your_Region_ID"); // Replace "Your_Region_ID" with the ID of your desired region

public TestListener() {
    this.scheduler = Executors.newScheduledThreadPool(1);

    // Add your target times to the set
    this.targetTimes = Set.of(
            LocalTime.of(8, 5),
            LocalTime.of(9, 25),
            LocalTime.of(11, 55),
            LocalTime.of(13, 30)
    );
}

@Override
public void onGuildReady(GuildReadyEvent event) {
    TextChannel channel = event.getGuild().getTextChannelById("Your-channel-id"); // The channel that you want to send the message
    if (channel == null) return;

    Runnable runnable = () -> {
        LocalTime now = LocalTime.now(zoneId); // Obtain current time for the specified region
        for (LocalTime time : targetTimes) {
            if (time.equals(now)) {
                String message = "Class is starting! Get to class!";
                channel.sendMessage(message).queue();
            }
        }
    };

    scheduler.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.MINUTES); // Check every minute
}

}

Here is a little more detailed explanation for this code:

The Instance Variables: ScheduledExecutorService scheduler: This is a service provided by Java's java.util.concurrent package that allows scheduling tasks to be executed periodically or after a delay.

Set targetTimes: This set contains the target times when the bot should send a message. It's initialized with the specified class start times.

ZoneId zoneId: This variable holds the time zone ID for the desired region. It's initialized with the ID of the desired time zone (e.g., "America/New_York"). Here is a list of time zones you can use

The Constructor:

  • Initializes the scheduler with a single-threaded executor service.
  • Initializes the targetTimes set with the specified class start times.

The onGuildReady Method:

  • This method is called when the bot is ready to operate in a guild (server).

  • Obtains the text channel (TextChannel) where the bot should send messages. The channel is identified by its unique ID.

  • Creates a Runnable object that checks the current time every minute (scheduleAtFixedRate).

If the current time matches any of the target times, a message is sent to the text channel indicating that the class is starting.

Upvotes: 0

Danny
Danny

Reputation: 842

You can use the ReadyEvent but I would suggest sending those messages using a ScheduledExecutorService.

At first you have to compare the current time to the time you want to schedule your message at.

public void onReady(@NotNull ReadyEvent event) {

    // get the current ZonedDateTime of your TimeZone
    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/Berlin"));

    // set the ZonedDateTime of the first lesson at 8:05
    ZonedDateTime nextFirstLesson = now.withHour(8).withMinute(05).withSecond(0);

    // if it's already past the time (in this case 8:05) the first lesson will be scheduled for the next day
    if (now.compareTo(nextFirstLesson) > 0) {
        nextFirstLesson = nextFirstLesson.plusDays(1);
    }

    // duration between now and the beginning of the next first lesson
    Duration durationUntilFirstLesson = Duration.between(now, nextFirstLesson);
    // in seconds
    long initialDelayFirstLesson = durationUntilFirstLesson.getSeconds();

    // schedules the reminder at a fixed rate of one day
    ScheduledExecutorService schedulerFirstLesson = Executors.newScheduledThreadPool(1);
    schedulerFirstLesson.scheduleAtFixedRate(() -> {
        // send a message
        /*
        String message = "Class is starting! Get to class!";
        JDA jda = event.getJDA();
        for (Guild guild : jda.getGuilds()) {
            guild.getDefaultChannel().sendMessage(message).queue();
        }
        */

                },
            initialDelayFirstLesson,
            TimeUnit.DAYS.toSeconds(1),
            TimeUnit.SECONDS);
}

This is just a basic idea for the first lesson. It is up to you on implementing the rest.

For example, you might want to check which day it is in order to not send messages on weekends, or use only one scheduler for all the lessons.

I don't know whether you want to send those messages only to one specific server (in which case you might just want to hardcode the guild id) or to multiple servers (here you could initialize a list of guilds or just get every guild the bot is in).

Upvotes: 2

Related Questions