diff options
| author | Pinapelz <donaldshan1@outlook.com> | 2023-04-13 14:27:33 -0700 |
|---|---|---|
| committer | Pinapelz <donaldshan1@outlook.com> | 2023-04-13 14:27:33 -0700 |
| commit | c96487a2562c841f9f9f890864727575e35b2243 (patch) | |
| tree | 11d39001b1b99aa706f05973c5b5642946edc71d /src/main | |
Initial Commit
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/java/Main.java | 93 | ||||
| -rw-r--r-- | src/main/java/builders/ScheduleMessageBuilder.java | 69 | ||||
| -rw-r--r-- | src/main/java/commands/CommandManager.java | 52 | ||||
| -rw-r--r-- | src/main/java/commands/StatusHandler.java | 22 | ||||
| -rw-r--r-- | src/main/java/common/OrgChannelTuple.java | 37 | ||||
| -rw-r--r-- | src/main/java/fileutils/FileDataProcessor.java | 58 | ||||
| -rw-r--r-- | src/main/java/vtuber/ScheduleHandler.java | 65 |
7 files changed, 396 insertions, 0 deletions
diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..78a2f4d --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,93 @@ +import commands.CommandManager; +import commands.StatusHandler; +import common.OrgChannelTuple; +import fileutils.FileDataProcessor; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import javax.security.auth.login.LoginException; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + + + +public class Main extends ListenerAdapter{ + private int REFRESH_INTERVAL = 15; + private JDA jda; + private StatusHandler statusHandler; + private JDABuilder jdaBuilder; + private FileDataProcessor fileDataProcessor; + private CommandManager commandManager; + + public void initializeBot(){ + fileDataProcessor = new FileDataProcessor(); + commandManager = new CommandManager(fileDataProcessor.readCredential("holodexAPIKey")); + jdaBuilder = JDABuilder.createDefault(fileDataProcessor.readCredential("discordToken")); + jdaBuilder.addEventListeners(commandManager); + try { + jda = jdaBuilder.build(); + statusHandler = new StatusHandler(jda); + statusHandler.updateSlashCommands(); + System.out.println("Bot is ready!"); + initializeAutoRefresh(); + } + catch (LoginException e) { + System.out.println("Unable to login with the provided token. Please check your token and try again."); + throw new RuntimeException(e); + } + + } + + public void initializeAutoRefresh(){ + ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); + ses.scheduleAtFixedRate(() -> { + try { + System.out.println("Refreshing upcoming channels"); + List<OrgChannelTuple> refreshChannels = fileDataProcessor.getRefreshChannels(); + if (refreshChannels.size() == 0) { + System.out.println("No channels to refresh"); + return; + } + for (OrgChannelTuple orgChannelTuple : refreshChannels) { + System.out.println("Refreshing " + orgChannelTuple.getType() + " " + orgChannelTuple.getName()); + List<MessageEmbed> messageEmbeds = commandManager.updateUpcomingChannel(orgChannelTuple.getName(), orgChannelTuple.getType()); + if (messageEmbeds.size() == 0) { + continue; + } + jda.getTextChannelById(orgChannelTuple.getDiscordChannelId()).purgeMessages( + jda.getTextChannelById(orgChannelTuple.getDiscordChannelId()).getIterableHistory().complete()); + for (MessageEmbed messageEmbed : messageEmbeds) { + jda.getTextChannelById(orgChannelTuple.getDiscordChannelId()).sendMessageEmbeds(messageEmbed).queue(); + } + } + } + catch(NullPointerException ex){ + System.out.println("Channel is empty. Skipping refresh there"); + } + catch (Exception e) { + System.out.println("Error occurred while refreshing upcoming channels"); + e.printStackTrace(); + } + }, 0, REFRESH_INTERVAL, TimeUnit.MINUTES); + } + + + @Override + public void onMessageReceived(MessageReceivedEvent e) { + JDA jda = e.getJDA(); + Message message = e.getMessage(); + String msg = message.getContentDisplay(); + + } + public static void main(String args[]) { + Main main = new Main(); + main.initializeBot(); + } +} + diff --git a/src/main/java/builders/ScheduleMessageBuilder.java b/src/main/java/builders/ScheduleMessageBuilder.java new file mode 100644 index 0000000..9dd77bf --- /dev/null +++ b/src/main/java/builders/ScheduleMessageBuilder.java @@ -0,0 +1,69 @@ +package builders; + +import com.pina.datatypes.SimpleVideo; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; + +import java.awt.*; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; + +public class ScheduleMessageBuilder { + private int COLOR = 8805674; + public MessageEmbed buildLiveAndUpcomingMessage(List<SimpleVideo> videos){ + if (videos.size() == 0){ + return new EmbedBuilder() + .setTitle("No streams upcoming or live. Check back later!") + .setDescription("If you think this is a mistake, please check that you've spelled the organization as listed on Holodex") + .setColor(new Color(COLOR)) + .setTimestamp(OffsetDateTime.now()) + .build(); + } + String pfp = videos.get(0).channel.photo; + EmbedBuilder messageBuilder = new EmbedBuilder() + .setThumbnail(pfp) + .setTitle("Upcoming and Live Streams for " + videos.get(0).channel.org) + .setDescription("The schedule you asked for") + .setColor(new Color(COLOR)) + .setTimestamp(OffsetDateTime.now()); + + for (SimpleVideo video : videos){ + String gmtStartTime = video.start_scheduled; + long unixTime = OffsetDateTime.parse(gmtStartTime).toEpochSecond(); + String unixTimeStr = "<t:" + Long.toString(unixTime) + ":R> ⏰"; + if (video.status.equals("live")){ + unixTimeStr = "LIVE \uD83D\uDD34"; + } + String titleText = video.channel.english_name + " - " + unixTimeStr; + String videoURL = "https://www.youtube.com/watch?v=" + video.id; + messageBuilder.addField(titleText, "["+video.title+"]"+"("+videoURL+")", false); + } + return messageBuilder.build(); + + } + + public ArrayList<MessageEmbed> getUpcomingLiveListMessages(List<SimpleVideo> simpleVideos){ + ArrayList<MessageEmbed> messageEmbeds = new ArrayList<>(); + for (SimpleVideo video : simpleVideos){ + String title = video.channel.english_name + " is streaming soon! ⏰"; + String fieldTitle = "Scheduled Start Time"; + if (video.status.equals("live")){ + title = video.channel.english_name + " is live! \uD83D\uDD34"; + fieldTitle = "Live Since"; + } + String gmtStartTime = video.start_scheduled; + long unixTime = OffsetDateTime.parse(gmtStartTime).toEpochSecond(); + EmbedBuilder embedBuilder = new EmbedBuilder() + .setTitle(title) + .setDescription("["+video.title+"]"+"(https://www.youtube.com/watch?v="+video.id+")") + .addField(fieldTitle, "<t:" + unixTime + ":R>", false) + .setThumbnail(video.channel.photo) + .setImage("https://img.youtube.com/vi/"+video.id+"/maxresdefault.jpg") + .setTimestamp(OffsetDateTime.now()); + messageEmbeds.add(embedBuilder.build()); + } + return messageEmbeds; + + } +} diff --git a/src/main/java/commands/CommandManager.java b/src/main/java/commands/CommandManager.java new file mode 100644 index 0000000..e374b21 --- /dev/null +++ b/src/main/java/commands/CommandManager.java @@ -0,0 +1,52 @@ +package commands; + +import builders.ScheduleMessageBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import vtuber.ScheduleHandler; + +import java.util.ArrayList; + +public class CommandManager extends ListenerAdapter { + ScheduleHandler scheduleHandler; + ScheduleMessageBuilder scb; + public CommandManager(String holodexAPIKey) { + scheduleHandler = new ScheduleHandler(holodexAPIKey); + scb = new ScheduleMessageBuilder(); + System.out.println("CommandManager initialized"); + } + @Override + public void onSlashCommand(SlashCommandEvent e) { + String command = e.getName(); + switch (command) { + case "schedule": + String organization = e.getOption("organization").getAsString(); + organization = organization.replaceAll(" ", "%20"); + MessageEmbed scheduleMessage = scb.buildLiveAndUpcomingMessage(scheduleHandler.getSchedule(organization, 10)); + e.deferReply().queue(); + e.getHook().sendMessageEmbeds(scheduleMessage).queue(); + break; + default: + e.reply("Unknown command received").queue(); + break; + } + } + + public ArrayList<MessageEmbed> updateUpcomingChannel(String name, String type){ + ArrayList<MessageEmbed> messageEmbeds = new ArrayList<>(); + switch (type) { + case "org": + messageEmbeds = scb.getUpcomingLiveListMessages(scheduleHandler.getSchedule(name)); + break; + case "channel": + messageEmbeds = scb.getUpcomingLiveListMessages(scheduleHandler.getScheduleChannelId(name)); + break; + default: + System.out.println("Unknown type"); + break; + } + return messageEmbeds; + } + +} diff --git a/src/main/java/commands/StatusHandler.java b/src/main/java/commands/StatusHandler.java new file mode 100644 index 0000000..ff48632 --- /dev/null +++ b/src/main/java/commands/StatusHandler.java @@ -0,0 +1,22 @@ +package commands; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; + +public class StatusHandler { + JDA jda; + public StatusHandler(JDA jda) { + this.jda = jda; + + } + public void updateSlashCommands(){ + jda.upsertCommand(new CommandData("schedule", "Shows upcoming streams and events for a given organization") + .addOption(OptionType.STRING, "organization", + "Holodex Organization Name (e.g Hololive, Nijisanji, Phase Connect, PRISM, Production Kawaii)", + true)) + .queue(); + + } + + +} diff --git a/src/main/java/common/OrgChannelTuple.java b/src/main/java/common/OrgChannelTuple.java new file mode 100644 index 0000000..8a78dcc --- /dev/null +++ b/src/main/java/common/OrgChannelTuple.java @@ -0,0 +1,37 @@ +package common; + +public class OrgChannelTuple { + private String name; + private long discordChannelId; + private String type; + + public OrgChannelTuple(String type, String name, long discordChannelId) { + this.name = name; + this.type = type; + this.discordChannelId = discordChannelId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getDiscordChannelId() { + return discordChannelId; + } + + public void setDiscordChannelId(long discordChannelId) { + this.discordChannelId = discordChannelId; + } +} diff --git a/src/main/java/fileutils/FileDataProcessor.java b/src/main/java/fileutils/FileDataProcessor.java new file mode 100644 index 0000000..0315fbb --- /dev/null +++ b/src/main/java/fileutils/FileDataProcessor.java @@ -0,0 +1,58 @@ +package fileutils; + +import common.OrgChannelTuple; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class FileDataProcessor { + + public static String readCredential(String parameter){ + try { + Object obj = new JSONParser().parse(new FileReader("settings//config.json")); + JSONObject jo = (JSONObject) obj; + return (String) jo.get(parameter); + } + catch(FileNotFoundException e){ + System.out.println("Credential file could not be found. Please create it at settings//config.json"); + } + catch(ParseException ex){ + System.out.println("Ensure that your credential file is valid JSON"); + } + catch(IOException ex){ + System.out.println("An error occurred while reading the credential file"); + } + return ""; + + } + + public List<OrgChannelTuple> getRefreshChannels(){ + List<OrgChannelTuple> orgChannelTuples = new ArrayList<>(); + try{ + File channelFile = new File("settings//upcomingChannels.txt"); + if(channelFile.createNewFile()){ + System.out.println("upcomingChannels.txt created. Please fill it out with the organizations you want to track (refer to README)"); + } + for (String line : Files.readAllLines(Paths.get("settings//upcomingChannels.txt"))) { + String type = line.split(":")[0]; + String name = line.split(":")[1]; + String channelIdStr = line.split(":")[2]; + long channelId = Long.parseLong(channelIdStr); + orgChannelTuples.add(new OrgChannelTuple(type, name, channelId)); + } + } catch (IOException e) { + System.out.println("Unable to create upcomingChannels.txt file for updating Discord Channels"); + } + return orgChannelTuples; + } + +} diff --git a/src/main/java/vtuber/ScheduleHandler.java b/src/main/java/vtuber/ScheduleHandler.java new file mode 100644 index 0000000..094309b --- /dev/null +++ b/src/main/java/vtuber/ScheduleHandler.java @@ -0,0 +1,65 @@ +package vtuber; + +import com.pina.Holodex; +import com.pina.HolodexException; +import com.pina.datatypes.SimpleVideo; +import com.pina.query.VideoQueryBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class ScheduleHandler { + Holodex holodex; + + public ScheduleHandler(String apikey) { + System.out.println("ScheduleHandler initialized"); + holodex = new Holodex(apikey); + + } + + public List<SimpleVideo> getSchedule(String org, int limit) { + System.out.println("Getting schedule for " + org); + List<SimpleVideo> upcomingAndLiveVideos = new ArrayList<>(); + try { + List<SimpleVideo> upcomingVideos = holodex.getLiveAndUpcomingVideos(new VideoQueryBuilder().setOrg(org).setLimit(limit).setStatus("upcoming")); + List<SimpleVideo> liveVideos = holodex.getLiveAndUpcomingVideos(new VideoQueryBuilder().setStatus("live").setOrg(org).setLimit(limit)); + upcomingAndLiveVideos.addAll(liveVideos); + upcomingAndLiveVideos.addAll(upcomingVideos); + } catch (HolodexException e) { + System.out.println("Error getting schedule for " + org); + System.out.println(e.getMessage()); + } + return upcomingAndLiveVideos; + } + + public List<SimpleVideo> getSchedule(String org) { + System.out.println("Getting schedule for " + org); + List<SimpleVideo> upcomingAndLiveVideos = new ArrayList<>(); + try { + List<SimpleVideo> upcomingVideos = holodex.getLiveAndUpcomingVideos(new VideoQueryBuilder().setOrg(org).setStatus("upcoming")); + List<SimpleVideo> liveVideos = holodex.getLiveAndUpcomingVideos(new VideoQueryBuilder().setStatus("live").setOrg(org)); + upcomingAndLiveVideos.addAll(liveVideos); + upcomingAndLiveVideos.addAll(upcomingVideos); + } catch (HolodexException e) { + System.out.println("Error getting schedule for " + org); + System.out.println(e.getMessage()); + } + return upcomingAndLiveVideos; + } + + public List<SimpleVideo> getScheduleChannelId(String channelId) { + System.out.println("Getting schedule for " + channelId); + List<SimpleVideo> upcomingAndLiveVideos = new ArrayList<>(); + try { + List<SimpleVideo> upcomingVideos = holodex.getLiveAndUpcomingVideos(new VideoQueryBuilder().setChannelId(channelId).setStatus("upcoming")); + List<SimpleVideo> liveVideos = holodex.getLiveAndUpcomingVideos(new VideoQueryBuilder().setStatus("live").setChannelId(channelId)); + upcomingAndLiveVideos.addAll(liveVideos); + upcomingAndLiveVideos.addAll(upcomingVideos); + } catch (HolodexException e) { + System.out.println("Error getting schedule for " + channelId); + System.out.println(e.getMessage()); + } + return upcomingAndLiveVideos; + + } +} |
