Moved commands to extra function; added timetable, start, stop commands; general changes

This commit is contained in:
ByteDream 2020-12-04 18:19:59 +01:00
parent cecaf526d4
commit 88ad913bca
16 changed files with 516 additions and 287 deletions

0
Dockerfile Normal file → Executable file
View File

0
LICENCE Normal file → Executable file
View File

13
README.md Normal file → Executable file
View File

@ -23,15 +23,18 @@ To see all available commands and get infos about it, simply type `help`.
| command | usage | example | default | | command | usage | example | default |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `channel` | In the channel where this command is entered, the bot shows the timetable changes | `channel` | - | `channel` | In the channel where this command is entered, the bot shows the timetable changes | `channel` | - |
| `clear` | Clears the given untis data, given from the `data` command | `clear` | - | `clear` | Clears the given untis data, given from the `data` command | `clear` | - |
| `data <username> <password> <login page url> [class name]` | Sets the data with which the bot logs in to untis and checks for timetable changes. The data is stored encrypted on the server. `username` and `password` are the normal untis login data with which one also logs in to the untis website / app. To gain the login page url you have to go to webuntis.com, type in your school and choose it. Then you will be redirected to the untis login page, The url of this page is the login page url, for example `https://example.webuntis.com/WebUntis/?school=myschool#/basic/main`. `class name` is just the name of the class you want to check (eg. `12AB`). If `class name` is not specified, the bot tries to get the default class which is assigned to the given account. | `data myname secure https://example.webuntis.com/WebUntis/?school=example#/basic/main 12AB` | - | | `data <username> <password> <login page url> [class name]` | Sets the data with which the bot logs in to untis and checks for timetable changes. The data is stored encrypted on the server. `username` and `password` are the normal untis login data with which one also logs in to the untis website / app. To gain the login page url you have to go to webuntis.com, type in your school and choose it. Then you will be redirected to the untis login page, The url of this page is the login page url, for example `https://example.webuntis.com/WebUntis/?school=myschool#/basic/main`. `class name` is just the name of the class you want to check (eg. `12AB`). As `class name` you can use any class from your school. If it isn't specified, the bot tries to get the default class which is assigned to the given account. | `data myname secure https://example.webuntis.com/WebUntis/?school=example#/basic/main 12AB` | - |
| `help` | Displays help to a given command | `help data` | - | | `help [command]` | Displays help to a given command | `help data` | - |
| `language <language>` | Changes the language in which the timetable information are displayed. Currently only `de` (german) and `en` (english) are supported | `language de` | `en` | | `language <language>` | Changes the language in which the timetable information are displayed. Currently only `de` (german) and `en` (english) are supported | `language de` | `en` |
| `prefix <new prefix>` | Changes the prefix with which commands are called | `prefix $` | `!untis ` | | `prefix <new prefix>` | Changes the prefix with which commands are called | `prefix $` | `!untis ` |
| `stats` | Displays a message with some stats (total cancelled lessons, etc.) | `stats` | - | | `stats` | Displays a message with some stats (total cancelled lessons, etc.) | `stats` | - |
| `start` | Starts the stopped timetable listener. Only works if data was set with the `data` command | `start` | - |
| `stop` | Stops timetable listening. Only works if data was set with the `data` command | `stop` | - |
| `timetable [date] [class name]` | Displays the timetable for a specific date. As `date` you can use 3 formats. 1: Only the day (`12`); 2. Day and month (`13.04`); 3. Day, month and year (`31.12.2020`). Only works if data was set with the `data` command. If no date is given, the timetable for the current date is displayed. As `class name` you can use any class from your school. If class is not given, the class which was assigned in the `data` command is used | `timetable 11.11` | - |
Note: All commands except for `help <command>` and `<stats>` can only be executed by a member with admin rights. Note: All commands except for `help [command]`, `timetable [] [class]` and `<stats>` can only be executed by a member with admin rights.
## Self-hosting ## Self-hosting

0
files/database.sql Normal file → Executable file
View File

0
src/org/bytedream/untisbot/Crypt.java Normal file → Executable file
View File

0
src/org/bytedream/untisbot/Main.java Normal file → Executable file
View File

0
src/org/bytedream/untisbot/Utils.java Normal file → Executable file
View File

2
src/org/bytedream/untisbot/data/Data.java Normal file → Executable file
View File

@ -72,7 +72,7 @@ public class Data {
} }
public Short getKlasseId() { public Short getKlasseId() {
return (short) data[6]; return (Short) data[6];
} }
public Long getChannelId() { public Long getChannelId() {

0
src/org/bytedream/untisbot/data/DataConnector.java Normal file → Executable file
View File

0
src/org/bytedream/untisbot/data/StoreType.java Normal file → Executable file
View File

0
src/org/bytedream/untisbot/discord/Discord.java Normal file → Executable file
View File

View File

@ -12,7 +12,6 @@ import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.bytedream.untis4j.LoginException;
import org.bytedream.untis4j.Session; import org.bytedream.untis4j.Session;
import org.bytedream.untis4j.responseObjects.Klassen; import org.bytedream.untis4j.responseObjects.Klassen;
import org.bytedream.untis4j.responseObjects.Teachers; import org.bytedream.untis4j.responseObjects.Teachers;
@ -39,12 +38,11 @@ import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Adapter to handle all events * Adapter to handle all events
* *
* @version 1.0 * @version 1.1
* @since 1.0 * @since 1.0
*/ */
public class DiscordCommandListener extends ListenerAdapter { public class DiscordCommandListener extends ListenerAdapter {
@ -53,6 +51,7 @@ public class DiscordCommandListener extends ListenerAdapter {
private final DataConnector.Stats statsDataConnector; private final DataConnector.Stats statsDataConnector;
private final JSONObject languages; private final JSONObject languages;
private final HashMap<Long, Session> allUntisSessions = new HashMap<>();
private final HashMap<Long, Timer> allTimetableChecker = new HashMap<>(); private final HashMap<Long, Timer> allTimetableChecker = new HashMap<>();
private final Logger logger = Main.getLogger(); private final Logger logger = Main.getLogger();
@ -77,13 +76,411 @@ public class DiscordCommandListener extends ListenerAdapter {
embedBuilder.setColor(Color.GREEN); embedBuilder.setColor(Color.GREEN);
} }
/**
* Runs a command
*
* @param guild guild from which the command came
* @param channel channel from which the command came
* @param permission if true, commands which needs (admin) permission to run, can be executed
* @param command command to execute
* @param args extra arguments for the command
*
* @since 1.1
*/
private void runCommand(Guild guild, TextChannel channel, boolean permission, String command, String[] args) {
long guildId = guild.getIdLong();
String guildName = guild.getName();
Data.Guild data = guildDataConnector.get(guildId);
switch (command) {
case "timetable": // `timetable [day | [day.month] | [day.month.year]] [class name]` command
if (args.length < 3) {
Session session = allUntisSessions.get(guildId);
LocalDate now = LocalDate.now();
Short classId = null;
LocalDate date = null;
if (data.getServer() == null && data.getSchool() == null) {
channel.sendMessage("Please set your data with the `data` command first, before you use this command. Type `" + data.getPrefix() + "help data` to get information").queue();
return;
}
if (args.length == 0) {
classId = data.getKlasseId();
date = now;
} else {
for (String arg : args) {
if (date == null) {
Integer number = null;
try {
number = Integer.parseInt(arg);
} catch (NumberFormatException ignore) {
}
if (number != null && number <= 31 && number >= 1) {
date = LocalDate.of(now.getYear(), now.getMonth(), number);
continue;
} else if (arg.contains(".")) {
String[] splitDate = args[0].split("\\.");
try {
switch (splitDate.length) {
case 1:
date = LocalDate.of(now.getYear(), now.getMonth(), Integer.parseInt(splitDate[0]));
break;
case 2:
date = LocalDate.of(now.getYear(), Integer.parseInt(splitDate[1]), Integer.parseInt(splitDate[0]));
break;
case 3:
date = LocalDate.of(Integer.parseInt(splitDate[2]), Integer.parseInt(splitDate[1]), Integer.parseInt(splitDate[0]));
break;
default:
channel.sendMessage("Couldn't get date. Type `" + data.getPrefix() + "help timetable` for help").queue();
}
continue;
} catch (NumberFormatException e) {
channel.sendMessage("Couldn't get date. Type `" + data.getPrefix() + "help timetable` for help").queue();
return;
}
}
}
System.out.println("sss");
try {
classId = (short) session.getKlassen().findByName(arg).getId();
} catch (IOException e) {
logger.warn(guildId + " ran into an exception while trying to receive classes for a timetable", e);
channel.sendMessage("Couldn't search the class. Try again (later) or contact my author <@650417934073593886>, if the problem won't go away").queue();
} catch (NullPointerException e) {
channel.sendMessage("Couldn't find any class with the name '" + arg + "'").queue();
}
}
}
EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(new Color(138, 43, 226));
JSONObject language;
if (data.getLanguage() == null) {
language = languages.getJSONObject("en");
} else {
language = languages.getJSONObject(data.getLanguage());
}
String className = "-";
try {
className = session.getKlassen().findById(data.getKlasseId()).getName();
} catch (IOException ignore) {}
String finalClassName = className; // yea java...
LocalDate finalDate = date; // yea java part two...
embedBuilder.setTitle(Utils.advancedFormat(language.getString("timetable-title"), new HashMap<String, Object>() {{
put("class", finalClassName);
put("date", finalDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));
}}));
LocalTime lastStartTime = LocalTime.MIN;
boolean multipleLessonAtOnce = false;
TreeMap<LocalTime, ArrayList<Timetable.Lesson>> lessons = new TreeMap<>();
try {
for (Timetable.Lesson lesson : Timetable.sortByStartTime(allUntisSessions.get(guildId).getTimetableFromKlasseId(date, date, classId))) {
lessons.putIfAbsent(lesson.getStartTime(), new ArrayList<>());
lessons.get(lesson.getStartTime()).add(lesson);
if (lastStartTime.equals(lesson.getStartTime())) {
multipleLessonAtOnce = true;
}
lastStartTime = lesson.getStartTime();
}
if (multipleLessonAtOnce) {
for (ArrayList<Timetable.Lesson> listLessons: lessons.values()) {
for (Timetable.Lesson lesson: listLessons) {
embedBuilder.addField(Utils.advancedFormat(language.getString("timetable-lesson-title"), new HashMap<String, Object>() {{
put("lesson-number", lesson.getTimeUnitObject().getName());
put("start-time", lesson.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", lesson.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
}}), Utils.advancedFormat(language.getString("timetable-teachers"), new HashMap<String, Object>() {{
put("teachers", String.join(", ", lesson.getTeachers().getFullNames()));
}}) + "\n" + Utils.advancedFormat(language.getString("timetable-subjects"), new HashMap<String, Object>() {{
put("subjects", String.join(", ", lesson.getSubjects().getLongNames()));
}}) + "\n" + Utils.advancedFormat(language.getString("timetable-rooms"), new HashMap<String, Object>() {{
put("rooms", String.join(", ", lesson.getRooms().getLongNames()));
}}), listLessons.size() > 1);
}
}
} else {
/*int halfSize = (int) Math.ceil((lessons.values().size() / 2));
for (int i = 0; i <= halfSize; i++) {
int j = i + 1;
Timetable.Lesson lesson = ((ArrayList<Timetable.Lesson>) lessons.values().toArray()[i]).get(0);
Timetable.Lesson[] leftRightLessons;
if (j + halfSize > lessons.values().size() - 1) {
leftRightLessons = new Timetable.Lesson[]{lesson};
} else {
leftRightLessons = new Timetable.Lesson[]{lesson, ((ArrayList<Timetable.Lesson>) lessons.values().toArray()[j + halfSize]).get(0)};
}
for (Timetable.Lesson lesson1: leftRightLessons) {
embedBuilder.addField(Utils.advancedFormat(language.getString("timetable-lesson-title"), new HashMap<String, Object>() {{
put("lesson-number", lesson1.getTimeUnitObject().getName());
put("start-time", lesson1.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", lesson1.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
}}), Utils.advancedFormat(language.getString("timetable-teachers"), new HashMap<String, Object>() {{
put("teachers", String.join(", ", lesson1.getTeachers().getFullNames()));
}}) + "\n" + Utils.advancedFormat(language.getString("timetable-subjects"), new HashMap<String, Object>() {{
put("subjects", String.join(", ", lesson1.getSubjects().getLongNames()));
}}) + "\n" + Utils.advancedFormat(language.getString("timetable-rooms"), new HashMap<String, Object>() {{
put("rooms", String.join(", ", lesson1.getRooms().getLongNames()));
}}), true);
}
embedBuilder.addBlankField(true);
}*/
for (ArrayList<Timetable.Lesson> listLesson: lessons.values()) {
Timetable.Lesson lesson = listLesson.get(0);
embedBuilder.addField(Utils.advancedFormat(language.getString("timetable-lesson-title"), new HashMap<String, Object>() {{
put("lesson-number", lesson.getTimeUnitObject().getName());
put("start-time", lesson.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", lesson.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
}}), Utils.advancedFormat(language.getString("timetable-teachers"), new HashMap<String, Object>() {{
put("teachers", String.join(", ", lesson.getTeachers().getFullNames()));
}}) + "\n" + Utils.advancedFormat(language.getString("timetable-subjects"), new HashMap<String, Object>() {{
put("subjects", String.join(", ", lesson.getSubjects().getLongNames()));
}}) + "\n" + Utils.advancedFormat(language.getString("timetable-rooms"), new HashMap<String, Object>() {{
put("rooms", String.join(", ", lesson.getRooms().getLongNames()));
}}), true);
}
}
channel.sendMessage(embedBuilder.build()).queue();
} catch (IOException e) {
logger.warn(guildId + " ran into an exception while trying to receive a timetable", e);
channel.sendMessage("Couldn't get timetable. Try again (later) or contact my author <@650417934073593886>, if the problem won't go away").queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0 or 1, got " + args.length + "), type `" + data.getPrefix() + "help timetable` for help").queue();
}
return;
case "stats": // `stats` command
if (args.length == 0) {
Data.Stats stats = statsDataConnector.get(guildId);
EmbedBuilder embedBuilder = new EmbedBuilder();
if (guildName.trim().endsWith("s")) {
embedBuilder.setTitle(guild.getName() + " untis status");
} else {
embedBuilder.setTitle(guild.getName() + "'s untis status");
}
ArrayList<String> mostMissedTeachers = new ArrayList<>();
short missedLessons = 0;
for (Map.Entry<String, Short> entry : stats.getAbsentTeachers().entrySet()) {
if (entry.getValue() > missedLessons) {
mostMissedTeachers.clear();
mostMissedTeachers.add(entry.getKey());
missedLessons = entry.getValue();
} else if (entry.getValue() == missedLessons) {
mostMissedTeachers.add(entry.getKey());
}
}
String mostMissedTeachersText;
if (missedLessons == 0) {
mostMissedTeachersText = "n/a";
} else {
mostMissedTeachersText = String.join(", ", mostMissedTeachers) + " - " + missedLessons + " missed lessons";
}
String timetableChecking;
if (data.isCheckActive()) {
timetableChecking = "\uD83D\uDFE2 Active";
embedBuilder.setColor(Color.GREEN);
} else {
timetableChecking = "\uD83D\uDD34 Inactive";
embedBuilder.setFooter("To start timetable checking, type `" + data.getPrefix() + "set-data <username> <password> <loginpage url>` - type `" + data.getPrefix() + "help` for more details");
embedBuilder.setColor(Color.RED);
}
embedBuilder.addField("Timetable checking", timetableChecking, true);
//embedBuilder.addField("Checking interval", data.getSleepTime() / 60000 + " minutes", true);
embedBuilder.addField("Total timetable requests", String.valueOf(stats.getTotalRequests()), true);
embedBuilder.addField("Total lessons checked", String.valueOf(stats.getTotalLessons()), true);
embedBuilder.addField("Total weeks checked", String.valueOf((int) (Math.floor((float) stats.getTotalDays() / 7))), true);
embedBuilder.addField("Total cancelled lessons", String.valueOf(stats.getTotalCancelledLessons()), true);
embedBuilder.addField("Total moved lessons", String.valueOf(stats.getTotalMovedLessons()), true);
embedBuilder.addField("Average cancelled lessons per week", String.valueOf(stats.getAverageCancelledLessonsPerWeek()), true);
embedBuilder.addField("Average moved lessons per week", String.valueOf(stats.getAverageMovedLessonsPerWeek()), true);
embedBuilder.addField("Most missed teacher", mostMissedTeachersText, false);
channel.sendMessage(embedBuilder.build()).queue();
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help stats` for help").queue();
}
return;
}
if (permission) {
switch (command) {
case "channel": // `channel` command
if (args.length == 0) {
guildDataConnector.update(guild.getIdLong(), null, null, null, null, null, null, channel.getIdLong(), null, null, null, null);
logger.info(guildName + " set a new channel to send the timetable changes to");
channel.sendMessage("This channel is now set as the channel where I send the timetable changes in").queue();
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help channel` for help").queue();
}
break;
case "clear": // `clear` command
if (args.length == 0) {
guildDataConnector.update(guild.getIdLong(), null, "", "", "", "", (short) 0, null, null, null, false, null);
logger.info(guildName + " cleared their data");
channel.sendMessage("Cleared untis data and stopped timetable listening").queue();
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help clear` for help").queue();
}
break;
case "data": // `data <username> <password> <server> <school name>` command
if (args.length >= 3 && args.length <= 4) {
if (dataUpdated.getOrDefault(guildId, LocalDateTime.MIN).plusMinutes(1).isAfter(LocalDateTime.now())) {
// this gives the server a little decay time and prevents additional load (because of the untis data encryption) caused by spamming
channel.sendMessage("The data was changed recently, try again in about one minute").queue();
} else {
dataUpdated.put(guildId, LocalDateTime.now());
String schoolName;
String className;
try {
schoolName = new URL(args[2]).getQuery().split("=")[1];
} catch (MalformedURLException | ArrayIndexOutOfBoundsException e) {
channel.sendMessage("The given login data is invalid").queue();
return;
}
String server = args[2].replace("https://", "").replace("http://", "");
server = "https://" + server.substring(0, server.indexOf("/"));
short klasseId;
try {
channel.sendMessage("Verifying data...").queue();
Session session = Session.login(args[0], args[1], server, schoolName);
if (args.length == 3) {
klasseId = (short) session.getInfos().getKlasseId();
className = session.getKlassen().findById(klasseId).getName();
} else {
try {
Klassen.KlasseObject klasse = session.getKlassen().findByName(args[3]);
klasseId = (short) klasse.getId();
className = klasse.getName();
} catch (NullPointerException e) {
channel.sendMessage("❌ Cannot find the given class").queue();
return;
}
}
allUntisSessions.putIfAbsent(guildId, session);
} catch (IOException e) {
channel.sendMessage("❌ The given login data is invalid").queue();
return;
}
if (data.getChannelId() == null) {
guildDataConnector.update(guildId, null, args[0], args[1], server, schoolName, klasseId, channel.getIdLong(), null, null, true, null);
} else {
guildDataConnector.update(guildId, null, args[0], args[1], server, schoolName, klasseId, null, null, null, true, null);
}
if (data.isCheckActive()) {
Timer timer = allTimetableChecker.get(guildId);
allTimetableChecker.remove(guildId);
timer.cancel();
timer.purge();
runTimetableChecker(guild);
channel.sendMessage("✅ Updated data and restarted timetable listening for class " + className).queue();
} else if (data.getLastChecked() != null) {
channel.sendMessage("✅ Updated data. Timetable listening were manually stopped a while ago. To re-enable it, type `" + data.getPrefix() + "start`").queue();
} else {
runTimetableChecker(guild);
channel.sendMessage("✅ Timetable listening has been started for class " + className).queue();
}
logger.info(guildName + " set new data");
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 3 or 4, got " + args.length + "), type `" + data.getPrefix() + "help data` for help").queue();
}
break;
case "language": // `language <language>` command
if (args.length == 1) {
String language = args[0];
if (!languages.has(language)) {
channel.sendMessage("The language `" + language + "` is not supported. Type `" + data.getPrefix() + "help` to see all available languages").queue();
} else {
guildDataConnector.update(guildId, language, null, null, null, null, null, null, null, null, null, null);
logger.info(guildName + " set their language to " + language);
channel.sendMessage("Updated language to `" + language + "`").queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 1, got " + args.length + "), type `" + data.getPrefix() + "help language` for help").queue();
}
break;
case "prefix": // `prefix <new prefix>` command
if (args.length == 1) {
String prefix = args[0];
if (prefix.length() == 0 || prefix.length() > 6) {
channel.sendMessage("The prefix must be between 1 and 6 characters long").queue();
} else {
String note = "";
if (prefix.contains("'") || prefix.contains("\"")) {
channel.sendMessage("Cannot use `'` or `\"` in prefix").queue();
return;
}
if (prefix.length() == 1) {
if ("!?$¥§%&@€#|/\\=.:-_+,;*+~<>^°".indexOf(prefix.charAt(0)) == -1) {
note += "\n_Note_: Because the prefix is not in `!?$¥§%&@€#|/\\=.:-_+,;*+~<>^°` you have to call commands with a blank space between it and the prefix";
}
} else {
prefix += " ";
note += "\n_Note_: Because the prefix is longer than 1 character you have to call commands with a blank space between it and the prefix";
}
guildDataConnector.update(guildId, null, null, null, null, null, null, null, prefix, null, null, null);
logger.info(guildName + " set their prefix to " + prefix);
channel.sendMessage("Updated prefix to `" + prefix + "`" + note).queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 3, got " + args.length + "), type `" + data.getPrefix() + "help prefix` for help").queue();
}
break;
case "start": // `start` command
if (args.length == 0) {
if (data.isCheckActive()) {
channel.sendMessage("Timetable listening already started").queue();
} else {
runTimetableChecker(guild);
logger.info(guildName + " started timetable listening");
channel.sendMessage("✅ Timetable listening has been started").queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help start` for help").queue();
}
break;
case "stop": // `stop` command
if (args.length == 0) {
if (data.isCheckActive()) {
Timer timer = allTimetableChecker.get(guildId);
allTimetableChecker.remove(guildId);
timer.cancel();
timer.purge();
logger.info(guildName + " stopped timetable listening");
channel.sendMessage("Stopped timetable listening. Use `" + data.getPrefix() + "start` to re-enable it").queue();
} else {
channel.sendMessage("Timetable listening is already stopped").queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help stop` for help").queue();
}
break;
case "help": // is handled in DiscordCommandListener.onMessageReceived()
return;
default:
channel.sendMessage("Unknown command").queue();
}
}
}
/** /**
* Checks the timetable from the given guild and sends an embed if the timetable has changes * Checks the timetable from the given guild and sends an embed if the timetable has changes
* *
* @param guild guild to send the timetable * @param guild guild to send the timetable
* @since 1.0 * @since 1.0
*/ */
public void runTimetableChecker(Guild guild) { private void runTimetableChecker(Guild guild) {
long guildId = guild.getIdLong(); long guildId = guild.getIdLong();
String guildName = guild.getName(); String guildName = guild.getName();
Timer timer = new Timer(); Timer timer = new Timer();
@ -99,20 +496,7 @@ public class DiscordCommandListener extends ListenerAdapter {
"If you want that I send these messages into another channel, type `" + data.getPrefix() + "channel` in the channel where I should send the messages in").queue(); "If you want that I send these messages into another channel, type `" + data.getPrefix() + "channel` in the channel where I should send the messages in").queue();
} }
} }
try { timetableChecker = new TimetableChecker(allUntisSessions.get(guildId), data.getKlasseId());
timetableChecker = new TimetableChecker(data.getUsername(), data.getPassword(), data.getServer(), data.getSchool(), data.getKlasseId());
} catch (LoginException e) {
e.printStackTrace();
logger.warn(guildName + " failed to login", e);
textChannel.sendMessage("Failed to login. Please try to re-set your data").queue();
return;
} catch (IOException e) {
e.printStackTrace();
logger.warn(guildName + " ran into an exception while trying to setup the timetable checker", e);
textChannel.sendMessage("An error occurred while trying to setup the timetable checking process. " +
"You should try to re-set your data or trying to contact my author <@650417934073593886> (:3) if the problem won't go away").queue();
return;
}
timer.scheduleAtFixedRate(new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
private long latestImportTime = 0; private long latestImportTime = 0;
@ -175,7 +559,7 @@ public class DiscordCommandListener extends ListenerAdapter {
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(Color.CYAN); embedBuilder.setColor(Color.CYAN);
embedBuilder.setTitle(Utils.advancedFormat(language.getString("title"), new HashMap<String, Object>() {{ embedBuilder.setTitle(Utils.advancedFormat(language.getString("change-title"), new HashMap<String, Object>() {{
put("weekday", language.getString(localDate.getDayOfWeek().name().toLowerCase())); put("weekday", language.getString(localDate.getDayOfWeek().name().toLowerCase()));
put("date", localDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))); put("date", localDate.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));
}})); }}));
@ -183,7 +567,7 @@ public class DiscordCommandListener extends ListenerAdapter {
for (Timetable.Lesson lesson : cancelledLessons) { for (Timetable.Lesson lesson : cancelledLessons) {
TimeUnits.TimeUnitObject timeUnitObject = lesson.getTimeUnitObject(); TimeUnits.TimeUnitObject timeUnitObject = lesson.getTimeUnitObject();
HashMap<String, Object> formatMap = new HashMap<String, Object>() {{ HashMap<String, Object> formatMap = new HashMap<String, Object>() {{
put("lesson-name", timeUnitObject.getName()); put("lesson-number", timeUnitObject.getName());
put("date", lesson.getDate()); put("date", lesson.getDate());
put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
@ -200,8 +584,8 @@ public class DiscordCommandListener extends ListenerAdapter {
Timetable.Lesson to = lesson[0]; Timetable.Lesson to = lesson[0];
Timetable.Lesson from = lesson[1]; Timetable.Lesson from = lesson[1];
HashMap<String, Object> formatMap = new HashMap<String, Object>() {{ HashMap<String, Object> formatMap = new HashMap<String, Object>() {{
put("from-lesson-name", from.getTimeUnitObject().getName()); put("from-lesson-number", from.getTimeUnitObject().getName());
put("to-lesson-name", to.getTimeUnitObject().getName()); put("to-lesson-number", to.getTimeUnitObject().getName());
put("date", from.getDate()); put("date", from.getDate());
put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
@ -216,7 +600,7 @@ public class DiscordCommandListener extends ListenerAdapter {
for (Timetable.Lesson lesson : notCancelledLessons) { for (Timetable.Lesson lesson : notCancelledLessons) {
TimeUnits.TimeUnitObject timeUnitObject = lesson.getTimeUnitObject(); TimeUnits.TimeUnitObject timeUnitObject = lesson.getTimeUnitObject();
HashMap<String, Object> formatMap = new HashMap<String, Object>() {{ HashMap<String, Object> formatMap = new HashMap<String, Object>() {{
put("lesson-name", timeUnitObject.getName()); put("lesson-number", timeUnitObject.getName());
put("date", lesson.getDate()); put("date", lesson.getDate());
put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
@ -233,8 +617,8 @@ public class DiscordCommandListener extends ListenerAdapter {
Timetable.Lesson from = lesson[0]; Timetable.Lesson from = lesson[0];
Timetable.Lesson to = lesson[1]; Timetable.Lesson to = lesson[1];
HashMap<String, Object> formatMap = new HashMap<String, Object>() {{ HashMap<String, Object> formatMap = new HashMap<String, Object>() {{
put("from-lesson-name", from.getTimeUnitObject().getName()); put("from-lesson-number", from.getTimeUnitObject().getName());
put("to-lesson-name", to.getTimeUnitObject().getName()); put("to-lesson-number", to.getTimeUnitObject().getName());
put("date", from.getDate()); put("date", from.getDate());
put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("start-time", timeUnitObject.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm"))); put("end-time", timeUnitObject.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
@ -325,7 +709,7 @@ public class DiscordCommandListener extends ListenerAdapter {
} }
} }
} catch (IOException e) { } catch (IOException e) {
logger.info("Running main through IOException (" + e.getCause() + ")"); logger.info("Running main through IOException", e);
main(); main();
} }
} }
@ -334,33 +718,6 @@ public class DiscordCommandListener extends ListenerAdapter {
logger.info(guildName + " started timetable listening"); logger.info(guildName + " started timetable listening");
} }
@Override
public void onReady(ReadyEvent event) {
ArrayList<Long> allGuilds = new ArrayList<>();
for (Guild guild : event.getJDA().getGuilds()) {
long guildId = guild.getIdLong();
if (!guildDataConnector.has(guildId)) {
guildDataConnector.add(guildId);
}
if (!statsDataConnector.has(guildId)) {
statsDataConnector.add(guildId);
}
if (guildDataConnector.get(guildId).isCheckActive()) {
runTimetableChecker(guild);
}
allGuilds.add(guildId);
}
for (Data.Guild data : guildDataConnector.getAll()) {
if (!allGuilds.contains(data.getGuildId())) {
guildDataConnector.remove(data.getGuildId());
statsDataConnector.remove(data.getGuildId());
}
}
logger.info("Bot is ready | Total guilds: " + guildDataConnector.getAll().size());
}
@Override @Override
public void onGuildMessageReceived(GuildMessageReceivedEvent event) { public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
@ -374,204 +731,14 @@ public class DiscordCommandListener extends ListenerAdapter {
// if (for example) a picture is sent, the bot checks for the first letter from the message an a because a picture has no letters, this error gets thrown // if (for example) a picture is sent, the bot checks for the first letter from the message an a because a picture has no letters, this error gets thrown
return; return;
} }
String guildName = event.getGuild().getName();
Guild guild = event.getGuild();
String userInput = event.getMessage().getContentDisplay().substring(data.getPrefix().length()).trim().replaceAll(" +", " "); String userInput = event.getMessage().getContentDisplay().substring(data.getPrefix().length()).trim().replaceAll(" +", " ");
String userInputLow = userInput.toLowerCase(); String userInputLow = userInput.toLowerCase();
String[] splitCommand = userInputLow.split(" "); String[] splitCommand = userInputLow.split(" ");
String command = splitCommand[0]; String command = splitCommand[0];
String[] args = Arrays.copyOfRange(splitCommand, 1, splitCommand.length); String[] args = Arrays.copyOfRange(splitCommand, 1, splitCommand.length);
TextChannel channel = event.getChannel();
try { try {
if (command.equals("stats")) { runCommand(event.getGuild(), event.getChannel(), event.getMember().getPermissions().contains(Permission.ADMINISTRATOR), command, args);
if (args.length == 0) {
Data.Stats stats = statsDataConnector.get(guildId);
EmbedBuilder embedBuilder = new EmbedBuilder();
if (guildName.trim().endsWith("s")) {
embedBuilder.setTitle(guild.getName() + " untis status");
} else {
embedBuilder.setTitle(guild.getName() + "'s untis status");
}
ArrayList<String> mostMissedTeachers = new ArrayList<>();
short missedLessons = 0;
for (Map.Entry<String, Short> entry : stats.getAbsentTeachers().entrySet()) {
if (entry.getValue() > missedLessons) {
mostMissedTeachers.clear();
mostMissedTeachers.add(entry.getKey());
missedLessons = entry.getValue();
} else if (entry.getValue() == missedLessons) {
mostMissedTeachers.add(entry.getKey());
}
}
String mostMissedTeachersText;
if (missedLessons == 0) {
mostMissedTeachersText = "n/a";
} else {
mostMissedTeachersText = String.join(", ", mostMissedTeachers) + " - " + missedLessons + " missed lessons";
}
String timetableChecking;
if (data.isCheckActive()) {
timetableChecking = "\uD83D\uDFE2 Active";
embedBuilder.setColor(Color.GREEN);
} else {
timetableChecking = "\uD83D\uDD34 Inactive";
embedBuilder.setFooter("To start timetable checking, type `" + data.getPrefix() + "set-data <username> <password> <loginpage url>` - type `" + data.getPrefix() + "help` for more details");
embedBuilder.setColor(Color.RED);
}
embedBuilder.addField("Timetable checking", timetableChecking, true);
//embedBuilder.addField("Checking interval", data.getSleepTime() / 60000 + " minutes", true);
embedBuilder.addField("Total timetable requests", String.valueOf(stats.getTotalRequests()), true);
embedBuilder.addField("Total lessons checked", String.valueOf(stats.getTotalLessons()), true);
embedBuilder.addField("Total weeks checked", String.valueOf((int) (Math.floor((float) stats.getTotalDays() / 7))), true);
embedBuilder.addField("Total cancelled lessons", String.valueOf(stats.getTotalCancelledLessons()), true);
embedBuilder.addField("Total moved lessons", String.valueOf(stats.getTotalMovedLessons()), true);
embedBuilder.addField("Average cancelled lessons per week", String.valueOf(stats.getAverageCancelledLessonsPerWeek()), true);
embedBuilder.addField("Average moved lessons per week", String.valueOf(stats.getAverageMovedLessonsPerWeek()), true);
embedBuilder.addField("Most missed teacher", mostMissedTeachersText, false);
channel.sendMessage(embedBuilder.build()).queue();
} else {
channel.sendMessage("Wrong number of arguments were given, type `" + data.getPrefix() + "help` for help").queue();
}
} else if (event.getMember().getPermissions().contains(Permission.ADMINISTRATOR)) {
switch (command) {
case "channel": // `channel` command
if (args.length == 0) {
guildDataConnector.update(guild.getIdLong(), null, null, null, null, null, null, channel.getIdLong(), null, null, null, null);
logger.info(guildName + " set a new channel to send the timetable changes to");
channel.sendMessage("This channel is now set as the channel where I send the timetable changes in").queue();
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help channel` for help").queue();
}
break;
case "clear": // `clear` command
if (args.length == 0) {
guildDataConnector.update(guild.getIdLong(), null, "", "", "", "", (short) 0, null, null, null, false, null);
logger.info(guildName + " cleared their data");
channel.sendMessage("Cleared untis data and stopped timetable listening").queue();
} else {
channel.sendMessage("Wrong number of arguments were given (expected 0, got " + args.length + "), type `" + data.getPrefix() + "help clear` for help").queue();
}
break;
case "data": // `data <username> <password> <server> <school name>` command
if (args.length >= 3 && args.length <= 4) {
if (dataUpdated.getOrDefault(guildId, LocalDateTime.MIN).plusMinutes(1).isAfter(LocalDateTime.now())) {
// this gives the server a little decay time and prevents additional load (because of the untis data encryption) caused by spamming
channel.sendMessage("The data was changed recently, try again in about one minute").queue();
} else {
dataUpdated.put(guildId, LocalDateTime.now());
String schoolName;
String className;
try {
schoolName = new URL(args[2]).getQuery().split("=")[1];
} catch (MalformedURLException | ArrayIndexOutOfBoundsException e) {
channel.sendMessage("The given login data is invalid").queue();
return;
}
String server = args[2].replace("https://", "").replace("http://", "");
server = "https://" + server.substring(0, server.indexOf("/"));
short klasseId;
try {
channel.sendMessage("Verifying data...").queue();
Session session = Session.login(args[0], args[1], server, schoolName);
if (args.length == 3) {
klasseId = (short) session.getInfos().getKlasseId();
className = session.getKlassen().findById(klasseId).getName();
} else {
try {
Klassen.KlasseObject klasse = session.getKlassen().findByName(args[3]);
klasseId = (short) klasse.getId();
className = klasse.getName();
} catch (NullPointerException e) {
channel.sendMessage("❌ Cannot find the given class").queue();
return;
}
}
session.logout();
} catch (IOException e) {
channel.sendMessage("❌ The given login data is invalid").queue();
return;
}
boolean isCheckActive = data.isCheckActive();
if (data.getChannelId() == null) {
guildDataConnector.update(guildId, null, args[0], args[1], server, schoolName, klasseId, channel.getIdLong(), null, null, true, null);
} else {
guildDataConnector.update(guildId, null, args[0], args[1], server, schoolName, klasseId, null, null, null, true, null);
}
if (isCheckActive) {
Timer timer = allTimetableChecker.get(guildId);
allTimetableChecker.remove(guildId);
timer.cancel();
timer.purge();
runTimetableChecker(guild);
channel.sendMessage("✅ Updated data and restarted timetable listening for class " + className).queue();
} else {
runTimetableChecker(guild);
channel.sendMessage("✅ Timetable listening has been started for class " + className).queue();
}
logger.info(guildName + " set new data");
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 3 or 4, got " + args.length + "), type `" + data.getPrefix() + "help data` for help").queue();
}
break;
case "language": // `language <language>` command
if (args.length == 1) {
String language = args[0];
if (!languages.has(language)) {
channel.sendMessage("The language `" + language + "` is not supported. Type `" + data.getPrefix() + "help` to see all available languages").queue();
} else {
guildDataConnector.update(guildId, language, null, null, null, null, null, null, null, null, null, null);
logger.info(guildName + " set their language to " + language);
channel.sendMessage("Updated language to `" + language + "`").queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 1, got " + args.length + "), type `" + data.getPrefix() + "help language` for help").queue();
}
break;
case "prefix": // `prefix <new prefix>` command
if (args.length == 1) {
String prefix = args[0];
if (prefix.length() == 0 || prefix.length() > 6) {
channel.sendMessage("The prefix must be between 1 and 6 characters long").queue();
} else {
String note = "";
if (prefix.contains("'") || prefix.contains("\"")) {
channel.sendMessage("Cannot use `'` or `\"` in prefix").queue();
return;
}
if (prefix.length() == 1) {
if ("!?$¥§%&@€#|/\\=.:-_+,;*+~<>^°".indexOf(prefix.charAt(0)) == -1) {
note += "\n_Note_: Because the prefix is not in `!?$¥§%&@€#|/\\=.:-_+,;*+~<>^°` you have to call commands with a blank space between it and the prefix";
}
} else {
prefix += " ";
note += "\n_Note_: Because the prefix is longer than 1 character you have to call commands with a blank space between it and the prefix";
}
guildDataConnector.update(guildId, null, null, null, null, null, null, null, prefix, null, null, null);
logger.info(guildName + " set their prefix to " + prefix);
channel.sendMessage("Updated prefix to `" + prefix + "`" + note).queue();
}
} else {
channel.sendMessage("Wrong number of arguments were given (expected 3, got " + args.length + "), type `" + data.getPrefix() + "help prefix` for help").queue();
}
break;
default:
}
}
} catch (NullPointerException ignore) { } catch (NullPointerException ignore) {
} }
}); });
@ -588,14 +755,13 @@ public class DiscordCommandListener extends ListenerAdapter {
String message = event.getMessage().getContentDisplay().trim().toLowerCase(); String message = event.getMessage().getContentDisplay().trim().toLowerCase();
MessageChannel channel = event.getChannel(); MessageChannel channel = event.getChannel();
EmbedBuilder embedBuilder = new EmbedBuilder();
String prefix; String prefix;
if (message.contains("help")) { // `help` command if (message.contains("help")) { // `help` command
if (event.isFromGuild()) { if (event.isFromGuild()) {
prefix = guildDataConnector.get(event.getGuild().getIdLong()).getPrefix(); prefix = guildDataConnector.get(event.getGuild().getIdLong()).getPrefix();
embedBuilder.setFooter("Note: Every command must be called with the set prefix ('" + prefix + "')");
if (!event.getMessage().getContentDisplay().startsWith(prefix + "help")) { if (!event.getMessage().getContentDisplay().startsWith(prefix + "help")) {
System.out.println("sss");
return; return;
} }
} else if (message.equals("help") || message.startsWith("help ")) { } else if (message.equals("help") || message.startsWith("help ")) {
@ -611,7 +777,7 @@ public class DiscordCommandListener extends ListenerAdapter {
String[] args = Arrays.copyOfRange(splitMessage, 1, splitMessage.length); String[] args = Arrays.copyOfRange(splitMessage, 1, splitMessage.length);
String help = "Use `" + prefix + "help <command>` to get help / information about a command.\n\n" + String help = "Use `" + prefix + "help <command>` to get help / information about a command.\n\n" +
"All available commands are:\n" + "All available commands are:\n" +
"`channel` `clear` `data` `help` `language` `prefix` `stats`"; "`channel` `clear` `data` `help` `language` `prefix` `stats` `start` `stop` `timetable`";
if (args.length > 1) { if (args.length > 1) {
channel.sendMessage("Wrong number of arguments are given (expected 0 or 1, got " + splitMessage.length + "). " + help).queue(); channel.sendMessage("Wrong number of arguments are given (expected 0 or 1, got " + splitMessage.length + "). " + help).queue();
} else if (args.length == 0) { } else if (args.length == 0) {
@ -620,7 +786,7 @@ public class DiscordCommandListener extends ListenerAdapter {
String title; String title;
String description; String description;
String example; String example;
String _default = null; String default_ = null;
switch (args[0]) { switch (args[0]) {
case "channel": case "channel":
title = "`channel` command"; title = "`channel` command";
@ -639,7 +805,7 @@ public class DiscordCommandListener extends ListenerAdapter {
"Then you will be redirected to the untis login page, The url of this page is the login page url, for example `https://example.webuntis.com/WebUntis/?school=myschool#/basic/main`.\n" + "Then you will be redirected to the untis login page, The url of this page is the login page url, for example `https://example.webuntis.com/WebUntis/?school=myschool#/basic/main`.\n" +
"`class name` is just the name of the class you want to check (eg. `12AB`). If `class name` is not specified, the bot tries to get the default class which is assigned to the given account."; "`class name` is just the name of the class you want to check (eg. `12AB`). If `class name` is not specified, the bot tries to get the default class which is assigned to the given account.";
example = "`data myname secure https://example.webuntis.com/WebUntis/?school=example#/basic/main 12AB`"; example = "`data myname secure https://example.webuntis.com/WebUntis/?school=example#/basic/main 12AB`";
_default = "`en`"; default_ = "`en`";
break; break;
case "help": case "help":
title = "`help <command>` command"; title = "`help <command>` command";
@ -650,35 +816,88 @@ public class DiscordCommandListener extends ListenerAdapter {
title = "`language <language>` command"; title = "`language <language>` command";
description = "Changes the language in which the timetable information are displayed. Currently only 'de' (german) and 'en' (english) are supported"; description = "Changes the language in which the timetable information are displayed. Currently only 'de' (german) and 'en' (english) are supported";
example = "`language de`"; example = "`language de`";
_default = "`en`"; default_ = "`en`";
break; break;
case "prefix": case "prefix":
title = "`prefix <new prefix>` command"; title = "`prefix <new prefix>` command";
description = "Changes the prefix with which commands are called"; description = "Changes the prefix with which commands are called";
example = "`prefix $`"; example = "`prefix $`";
_default = "`!untis `"; default_ = "`!untis `";
break; break;
case "stats": case "stats":
title = "`stats` command"; title = "`stats` command";
description = "Displays a message with some stats (total cancelled lessons, etc.)"; description = "Displays a message with some stats (total cancelled lessons, etc.)";
example = "`stats`"; example = "`stats`";
break; break;
case "start":
title = "`start` command";
description = "Starts the stopped timetable listener. Only works if data was set with the `data` command";
example = "`start`";
break;
case "stop":
title = "`stop` command";
description = "Stops timetable listening. Only works if data was set with the `data` command";
example = "`stop`";
break;
case "timetable":
title = "`timetable [date] [class name]` command";
description = "Displays the timetable for a specific date. As `date` you can use 3 formats." +
"1: Only the day (`12`); 2. Day and month (`13.04`); 3. Day, month and year (`31.12.2020`)." +
"Only works if data was set with the `data` command. If no date is given, the timetable for the current date is displayed." +
"As `class name` you can use any class from your school. If class is not given, the class which was assigned in the `data` command is used";
example = "`timetable 11.11`";
break;
default: default:
channel.sendMessage("Unknown command was given. " + help).queue(); channel.sendMessage("Unknown command was given. " + help).queue();
return; return;
} }
EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(Color.CYAN); embedBuilder.setColor(Color.CYAN);
embedBuilder.setTitle(title); embedBuilder.setTitle(title);
embedBuilder.addField("Description", description, false); embedBuilder.addField("Description", description, false);
embedBuilder.addField("Example", example, false); embedBuilder.addField("Example", example, false);
if (_default != null) { if (default_ != null) {
embedBuilder.addField("Default", _default, false); embedBuilder.addField("Default", default_, false);
} }
embedBuilder.setFooter("`<>` = required; `[]` = optional"); embedBuilder.setFooter("`<>` = required; `[]` = optional");
channel.sendMessage(embedBuilder.build()).queue(); channel.sendMessage(embedBuilder.build()).queue();
} }
}); });
t.setName("Message handler"); t.setName("Message handler");
t.start();
}
@Override
public void onReady(ReadyEvent event) {
ArrayList<Long> allGuilds = new ArrayList<>();
for (Guild guild : event.getJDA().getGuilds()) {
long guildId = guild.getIdLong();
if (!guildDataConnector.has(guildId)) {
guildDataConnector.add(guildId);
}
if (!statsDataConnector.has(guildId)) {
statsDataConnector.add(guildId);
}
if (guildDataConnector.get(guildId).isCheckActive()) {
Data.Guild data = guildDataConnector.get(guildId);
try {
allUntisSessions.put(guildId, Session.login(data.getUsername(), data.getPassword(), data.getServer(), data.getSchool()));
runTimetableChecker(guild);
} catch (IOException e) {
logger.error("Error for guild " + guild.getName() + " (" + guildId + ") while setting up untis session", e);
}
}
allGuilds.add(guildId);
}
for (Data.Guild data : guildDataConnector.getAll()) {
if (!allGuilds.contains(data.getGuildId())) {
guildDataConnector.remove(data.getGuildId());
statsDataConnector.remove(data.getGuildId());
}
}
logger.info("Bot is ready | Total guilds: " + guildDataConnector.getAll().size());
} }
@Override @Override

46
src/org/bytedream/untisbot/language.json Normal file → Executable file
View File

@ -1,15 +1,20 @@
{ {
"de": { "de": {
"language": "German", "language": "German",
"title": "Stunden Ausfall Information für {weekday}, den {date}", "timetable-title": "Stundenplan für Klasse {class} am {date}",
"cancelled-title": "Ausfall {lesson-name}. Stunde ({start-time} Uhr - {end-time} Uhr)", "timetable-lesson-title": "{lesson-number}. Stunde ({start-time} - {end-time})",
"cancelled-body": "Ausfall bei {teachers}, in {subjects}, in der {lesson-name}. Stunde", "timetable-teachers": "_Lehrer_: {teachers}",
"moved-title": "{from-lesson-name}. Stunde wird zur {to-lesson-name}. Stunde umverlegt", "timetable-subjects": "_Unterricht_: {subjects}",
"moved-body": "Die {from-lesson-name}. Stunde bei {teachers} in {subjects} wird zur {to-lesson-name}. Stunde umverlegt", "timetable-rooms": "_Raum_: {rooms}",
"not-cancelled-title": "KEIN Ausfall {lesson-name}. Stunde ({start-time} Uhr - {end-time} Uhr)", "change-title": "Stunden Ausfall Information für {weekday}, den {date}",
"not-cancelled-body": "KEIN Ausfall bei {teachers}, in {subjects}, in der {lesson-name}. Stunde", "cancelled-title": "Ausfall {lesson-number}. Stunde ({start-time} Uhr - {end-time} Uhr)",
"not-moved-title": "{from-lesson-name}. Stunde wird NICHT zur {to-lesson-name}. Stunde umverlegt", "cancelled-body": "Ausfall bei {teachers}, in {subjects}, in der {lesson-number}. Stunde",
"not-moved-body": "Die {from-lesson-name}. Stunde bei {teachers} wird NICHT zur {to-lesson-name}. Stunde umverlegt", "moved-title": "{from-lesson-number}. Stunde wird zur {to-lesson-number}. Stunde umverlegt",
"moved-body": "Die {from-lesson-number}. Stunde bei {teachers} in {subjects} wird zur {to-lesson-number}. Stunde umverlegt",
"not-cancelled-title": "KEIN Ausfall {lesson-number}. Stunde ({start-time} Uhr - {end-time} Uhr)",
"not-cancelled-body": "KEIN Ausfall bei {teachers}, in {subjects}, in der {lesson-number}. Stunde",
"not-moved-title": "{from-lesson-number}. Stunde wird NICHT zur {to-lesson-number}. Stunde umverlegt",
"not-moved-body": "Die {from-lesson-number}. Stunde bei {teachers} wird NICHT zur {to-lesson-number}. Stunde umverlegt",
"monday": "Montag", "monday": "Montag",
"tuesday": "Dienstag", "tuesday": "Dienstag",
"wednesday": "Mittwoch", "wednesday": "Mittwoch",
@ -20,15 +25,20 @@
}, },
"en": { "en": {
"language": "English", "language": "English",
"title": "Irregular lesson information for {weekday}, {date}", "timetable-title": "Timetable for class {class} on {date}",
"cancelled-title": "Cancelled {lesson-name}. lesson ({start-time} - {end-time})", "timetable-lesson-title": "{lesson-number}. lesson ({start-time} - {end-time})",
"cancelled-body": "The {lesson-name}. lesson with {teachers} in {subjects} is cancelled", "timetable-teachers": "_Teacher_: {teachers}",
"moved-title": "The {from-lesson-name}. lesson is moved to {to-lesson-name}. lesson", "timetable-subjects": "_Subject_: {subjects}",
"moved-body": "The {from-lesson-name}. lesson with {teachers} in {subjects} is moved to the {to-lesson-name}. lesson", "timetable-rooms": "_Room_: {rooms}",
"not-cancelled-title": "{lesson-name}. lesson ({start-time} - {end-time}) is NO cancelled", "change-title": "Irregular lesson information for {weekday}, {date}",
"not-cancelled-body": "The {lesson-name}. lesson with {teachers} in {subjects} is NOT cancelled", "cancelled-title": "Cancelled {lesson-number}. lesson ({start-time} - {end-time})",
"not-moved-title": "The {from-lesson-name}. lesson is NOT moved to {to-lesson-name}.", "cancelled-body": "The {lesson-number}. lesson with {teachers} in {subjects} is cancelled",
"not-moved-body": "The {from-lesson-name}. lesson with {teachers} in {subjects} is NOT moved to the {to-lesson-name}. lesson", "moved-title": "The {from-lesson-number}. lesson is moved to {to-lesson-number}. lesson",
"moved-body": "The {from-lesson-number}. lesson with {teachers} in {subjects} is moved to the {to-lesson-number}. lesson",
"not-cancelled-title": "{lesson-number}. lesson ({start-time} - {end-time}) is NO cancelled",
"not-cancelled-body": "The {lesson-number}. lesson with {teachers} in {subjects} is NOT cancelled",
"not-moved-title": "The {from-lesson-number}. lesson is NOT moved to {to-lesson-number}.",
"not-moved-body": "The {from-lesson-number}. lesson with {teachers} in {subjects} is NOT moved to the {to-lesson-number}. lesson",
"monday": "monday", "monday": "monday",
"tuesday": "tuesday", "tuesday": "tuesday",
"wednesday": "wednesday", "wednesday": "wednesday",

0
src/org/bytedream/untisbot/resources/logback.xml Normal file → Executable file
View File

0
src/org/bytedream/untisbot/untis/CheckCallback.java Normal file → Executable file
View File

17
src/org/bytedream/untisbot/untis/TimetableChecker.java Normal file → Executable file
View File

@ -19,7 +19,7 @@ import java.util.HashSet;
public class TimetableChecker { public class TimetableChecker {
private final Session session; private final Session session;
private final int klasseId; private final int classId;
private final LocalDate[] cancelledLessonsDay = new LocalDate[7]; private final LocalDate[] cancelledLessonsDay = new LocalDate[7];
private final LocalDate[] ignoredLessonsDay = new LocalDate[7]; private final LocalDate[] ignoredLessonsDay = new LocalDate[7];
private final LocalDate[] movedLessonsDay = new LocalDate[7]; private final LocalDate[] movedLessonsDay = new LocalDate[7];
@ -30,16 +30,13 @@ public class TimetableChecker {
/** /**
* Sets all necessary configurations and connects to the untis account with the given untis credentials * Sets all necessary configurations and connects to the untis account with the given untis credentials
* *
* @param username username of the untis account * @param session user session
* @param password user password of the untis account * @param classId id of the class to check the timetable
* @param server the server from the school as URL
* @param schoolName name of the school
* @throws IOException if any {@link IOException} while the login occurs
* @since 1.0 * @since 1.0
*/ */
public TimetableChecker(String username, String password, String server, String schoolName, int klasseId) throws IOException { public TimetableChecker(Session session, int classId) {
session = Session.login(username, password, server, schoolName); this.session = session;
this.klasseId = klasseId; this.classId = classId;
for (LocalDate[] localDates : new HashSet<LocalDate[]>() {{ for (LocalDate[] localDates : new HashSet<LocalDate[]>() {{
add(cancelledLessonsDay); add(cancelledLessonsDay);
@ -65,7 +62,7 @@ public class TimetableChecker {
* @since 1.0 * @since 1.0
*/ */
public CheckCallback check(LocalDate dateToCheck) throws IOException { public CheckCallback check(LocalDate dateToCheck) throws IOException {
Timetable timetable = session.getTimetableFromKlasseId(dateToCheck, dateToCheck, klasseId); Timetable timetable = session.getTimetableFromKlasseId(dateToCheck, dateToCheck, classId);
timetable.sortByStartTime(); timetable.sortByStartTime();
int dayOfWeekInArray = dateToCheck.getDayOfWeek().getValue() - 1; int dayOfWeekInArray = dateToCheck.getDayOfWeek().getValue() - 1;