commit 418efd2f47e0199de26bddabe76daa83a82ac40f Author: Ocame Date: Wed Sep 4 19:50:49 2024 +0200 Init diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..0762c79 --- /dev/null +++ b/build.gradle @@ -0,0 +1,45 @@ +plugins { + id 'application' + id("com.github.johnrengelman.shadow") version "7.1.2" +} + +application.mainClass = "de.ocame.ffdiscordbot.Main" +group 'de.ocame' +version '1.1-SNAPSHOT' + + +repositories { + mavenCentral() +} + + +sourceCompatibility = '17' +targetCompatibility = '17' + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} +/* +application { + mainModule = 'de.ocame.ffdiscordbot' + mainClass = 'de.ocame.ffdiscordbot.Main' +} +*/ + + +dependencies { + implementation "net.dv8tion:JDA:5.0.1" + implementation 'org.jsoup:jsoup:1.15.3' + implementation 'com.google.code.gson:gson:2.8.9' + implementation 'org.xerial:sqlite-jdbc:3.41.2.2' + implementation 'org.json:json:20231013' + + + implementation "ch.qos.logback:logback-classic:1.5.6" +// compile "org.json:json:20141113" +} + +test { + useJUnitPlatform() +} + diff --git a/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java b/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java new file mode 100644 index 0000000..9257d20 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java @@ -0,0 +1,32 @@ +package de.ocame.ffdiscordbot; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +public class ConfigLoader { + + private final Properties properties; + + public ConfigLoader() { + properties = new Properties(); + try (FileInputStream input = new FileInputStream("ffBot.conf")) { + properties.load(input); + } catch (IOException e) { + System.err.println("Konfigurationsdatei konnte nicht geladen werden: " + e.getMessage()); + e.printStackTrace(); + } + } + + public String getToken() { + return properties.getProperty("api.token"); + } + + public String getUsername() { + return properties.getProperty("ff.username"); + } + + public String getPassword() { + return properties.getProperty("ff.password"); + } + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java b/src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java new file mode 100644 index 0000000..2a7d6c6 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java @@ -0,0 +1,242 @@ +package de.ocame.ffdiscordbot; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; + +import java.awt.*; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +public class DiscordOutput { + + private final List chapterText; + public DiscordOutput() { + + chapterText = new ArrayList<>(); + chapterText.add(new StoryOutputText( + "Oh, wie mein Herz singt! **%autor%** hat ein neues Kapitel unserer Geschichte \"**%storyName%**\" geschrieben, " + + "und es heißt \"**%chapterName%**\". \n\nIch spüre die Aufregung in der Luft, als ob jeder Flügelschlag mich höher trägt. " + + "Die Welt leuchtet in neuen Farben, und jeder Sonnenstrahl erzählt von Abenteuern, die uns erwarten. Dieses Kapitel " + + "verspricht so viel – ich kann es kaum erwarten, in die Worte zu tauchen und die Geschichten, die sie verbergen, " + + "zu entdecken!" + , "https://media.discordapp.net/stickers/1219043798101786624.webp?size=160")); + chapterText.add(new StoryOutputText( + "Aye, Text in Sicht, ihr Seeratten! **%autor%** hat ein neues Kapitel in der Geschichte \"**%storyName%**\" aufgetan, und " + + "es trägt den Namen \"**%chapterName%**\". Macht die Fässer klar, holt die besten Getränke, und besetzt die Bildschirme! \n" + + "Keiner rührt sich, bis wir jedes Wort gelesen haben, verstanden? Und hört gut hin: Wer es wagt, auch nur ein Wort " + + "zu spoilern, wird schneller gekielholt, als er \"Piratenkodex\" sagen kann! Jetzt aber volle Fahrt voraus – das " + + "Abenteuer ruft!", "https://cdn.discordapp.com/attachments/542202034330271759/1272999183673987133/image.png?ex=66bd0460&is=66bbb2e0&hm=708f10708d0616ed212d0c288cfde503fb27785ddd68c1ba30bb4d3f7698acee&")); + chapterText.add(new StoryOutputText( + "Mesdames et Messieurs, heute habe ich die Ehre, Ihnen ein exquisites Gericht zu präsentieren: " + + "das \"**%chapterName%**\" à la \"**%storyName%**\", nach der feinen Art unseres geschätzten Chef-Autors \"**%autor%**\". \n" + + "Es ist ein Meisterwerk der literarischen Küche, perfekt gewürzt mit Spannung und serviert mit einer Prise Abenteuer. " + + "Lassen Sie sich von den Aromen dieser neuen Geschichte verführen und genießen Sie jeden Bissen – bon appétit!", + "https://cdn.discordapp.com/attachments/542202034330271759/1272999507587629188/image.png?ex=66bd04ad&is=66bbb32d&hm=d4d7325f233c27a75305ee9622c9f87b03e1aa507acb534c2fd6c3de62d435d1&", + new Color(255, 0, 229))); + + chapterText.add(new StoryOutputText( + "........................\n " + + "........................\n " + + "........................\n " + + "........................\n " + + "Na los, Grünschnabel, hör auf zu trödeln und lies endlich das neue Kapitel \"**%chapterName%**\" aus der Geschichte \"**%storyName%**\" " + + "vom \"**%autor%**\" vor! Meine Zeit ist nicht unendlich, und ich hab keine Lust, hier ewig zu warten. Also mach schon, bevor ich " + + "noch selbst anfangen muss, die Seiten zu ertasten!" + )); + chapterText.add(new StoryOutputText( + "Das neue Kapitel \"**%chapterName%**\" aus der Geschichte \"**%storyName%**\" vom \"**%autor%**\" ist erschienen, und ich werde es als Erster lesen. " + + "Während die Piraten noch ihren Rum genießen, verschaffe ich mir den Wissensvorsprung. Mit jedem Satz, den ich lese, " + + "wird meine Strategie schärfer, und bald werde ich ihnen einen Schritt voraus sein. Ein kluger Kopf gewinnt schließlich immer " + + "das Spiel.", + "https://cdn.discordapp.com/attachments/542202034330271759/1273002039923965952/image.png?ex=66bd0709&is=66bbb589&hm=77130cbe420a9e941e58e68010c12cc2e53c54c288b8563fd6a1a856ffebd0df&" + )); + + chapterText.add(new StoryOutputText( + "hcilrütan ebah hci sad etseuen Letipak nohcs neseleg \n\n" + + "Erika Rückwerts die Worte auszuprechen ist kein Reptiloidisch!\n\n" + + "Menschen! Natürlich habe ich das neueste Kapitel schon gelesen. Kein Zweifel, es ist genauso grandios, " + + "wie ich es erwartet habe. Jede Wendung, jeder Dialog – einfach meisterhaft. Und falls du es wissen willst: Es handelt sich " + + "um \"**%chapterName%**\" aus der Geschichte \"**%storyName%**\" vom Autor \"**%autor%**\". Wenn du wirklich etwas Gutes lesen willst, dann ist das genau das " + + "Richtige für dich. Aber hey, das hab ich ja gleich gewusst.", + "https://cdn.discordapp.com/attachments/542202034330271759/1273005878974349313/image.png?ex=66bd0a9c&is=66bbb91c&hm=8e7701f259b7bd84ea3b91b5c8fb63c4b8807dd297a05fc9289aead8c1978fac&", + new Color(40, 145, 6) + )); + chapterText.add(new StoryOutputText( + "Ah, wie wunderbar! Endlich halte ich das neue Kapitel \"**%chapterName%**\" aus der Geschichte \"**%storyName%**\" " + + "vom \"**%autor%**\" in den Händen. Ein wahres Vergnügen für den Geist! Und bitte, meine Lieben, lasst den Kronleuchter in Ruhe. " + + "Er spendet gerade das perfekte Licht zum Lesen – eine solch erhabene Lektüre verdient schließlich die besten Bedingungen." + )); + chapterText.add(new StoryOutputText("Ich habe die Zukunft gesehen, und glaub mir, das neue Kapitel von \"**%chapterName%**\" aus der Geschichte " + + "\"**%storyName%**\" vom \"**%autor%**\" ist etwas ganz Besonderes. Es ist, als ob die Worte selbst eine Melodie singen, die so sanft und " + + "doch so kraftvoll ist, dass sie dich mitreißt, egal wohin sie dich führt. Jedes Wort, jede Zeile – sie fließen wie ein ruhiger " + + "Fluss, der dich in eine Welt entführt, die so fesselnd und lebendig ist, dass du alles um dich herum vergisst.\n" + + "\n" + + "Es ist wie der erste Schluck eines perfekt gebrühten Tees, dieser Moment, in dem sich die Wärme in deinem Körper ausbreitet " + + "und du spürst, wie jeder Tropfen dich beruhigt und gleichzeitig belebt. Ach, Tee... Was für ein wunderbares Getränk, nicht wahr? " + + "So viel Sorgfalt und Liebe steckt in jeder Tasse, genau wie in diesen Worten, die ich gerade gelesen habe. Die Teeblätter, " + + "die mit so viel Bedacht und Geduld gepflückt werden, erinnern mich an die frischen Pflanzen im Frühling, wenn alles grünt und blüht. " + + "Sie sind voller Leben, voller Möglichkeiten, genau wie die Geschichten, die uns erzählen, wohin unser Weg führen könnte.\n" + + "\n" + + "Wie wunderschön es doch ist, in solchen einfachen Freuden Trost zu finden – sei es in einem guten Buch, einer heißen Tasse Tee oder dem " + + "Anblick von Pflanzen, die in ihrer vollen Pracht stehen. Manchmal sind es gerade diese kleinen, stillen Momente, die das Leben wirklich " + + "lebenswert machen.")); + } + + + + public void newChapter(JDA jda, DataChapter chapter, DataStory story) { + newChapter(jda, chapter, story, null, null); + } + public void newChapter(JDA jda, DataChapter chapter, DataStory story, Integer version, Long tagetChannel) { + try { + String message; + EmbedBuilder outputBox = new EmbedBuilder(); + + outputBox.setTitle("Neues Kapitel:", chapter.link()); + + List chapterTexte; + if(version != null) { + chapterTexte = SQLiteStorage.getChapterTexte(version); + } + else { + chapterTexte = SQLiteStorage.getChapterTexte(story); + } + + + StoryOutputText versionText = null; + if(chapterTexte.size() == 0) + return; + + Random random = new Random(); + + versionText = chapterTexte.get(random.nextInt(chapterTexte.size())); + + message = versionText.getMessage(); + + message = message.replace("%chapterName%", "**" + chapter.name() + "**") + .replace("%storyName%", "**" + story.name() + "**") + .replace("%autor%", "**" + story.author() + "**"); + + + outputBox.setDescription(message); + + outputBox.setColor(versionText.getColor()); + + Instant instant = Instant.ofEpochSecond(chapter.date()); + LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.of("Europe/Berlin")); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yy HH:mm 'Uhr'"); + + + outputBox.setFooter("Veröffentlicht am " + dateTime.format(formatter)); + if(versionText.getImage() != null && !Objects.equals(versionText.getImage(), "")) + outputBox.setThumbnail(versionText.getImage()); + + Long[] channelIds = SQLiteStorage.getChannelIds(); + if(tagetChannel != null) { + Objects.requireNonNull(jda.getTextChannelById(tagetChannel)) + .sendMessage("Hier ist ein Text vor dem Embed <@&423156046115242005> :") + .setEmbeds(outputBox.build()) + .queue(); + } + else if(Objects.requireNonNull(channelIds).length == 0) { + Objects.requireNonNull(jda.getTextChannelById(542202034330271759L)).sendMessageEmbeds(outputBox.build()).queue(); + } + else { + for (Long channelId : channelIds ) { + try { + if (Objects.requireNonNull(jda.getTextChannelById(channelId)).canTalk()) + Objects.requireNonNull(jda.getTextChannelById(channelId)).sendMessageEmbeds(outputBox.build()).queue(); + } + catch (NullPointerException e) { + // + } + } + } + } + catch (NumberFormatException e) { + //jda.getTextChannelById(channelId).sendMessage("Nope").queue(); + System.out.println("problem: " + e.getMessage()); + } + catch (Exception e) { + System.out.println("problem: " + e.getMessage()); + } + } + + + public void newStory(JDA jda, DataStory story) { + newStory(jda, story, null); + } + public void newStory(JDA jda, DataStory story, Integer version) { + try { + String message; + EmbedBuilder outputBox = new EmbedBuilder(); + + + outputBox.setTitle("Neue Gesichte:", story.link()); + + List storyTexte = SQLiteStorage.getStoryTexte(); + StoryOutputText versionText = null; + if(storyTexte.size() == 0) + return; + + if(version != null) { + versionText = storyTexte.get(version); + } + if(version == null || versionText == null) { + Random random = new Random(); + versionText = storyTexte.get(random.nextInt(storyTexte.size())); + } + + message = versionText.getMessage(); + + message = message.replace("%storyName%", "**" + story.name() + "**") + .replace("%autor%", "**" + story.author() + "**"); + + + outputBox.setDescription(message); + + outputBox.setColor(versionText.getColor()); + + Instant instant = Instant.ofEpochSecond(story.date()); + LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.of("Europe/Berlin")); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yy HH:mm 'Uhr'"); + + + outputBox.setFooter("Veröffentlicht am " + dateTime.format(formatter)); + if(versionText.getImage() != null && !Objects.equals(versionText.getImage(), "")) + outputBox.setThumbnail(versionText.getImage()); + + Long[] channelIds = SQLiteStorage.getChannelIds(); + if(channelIds.length == 0) { + jda.getTextChannelById(542202034330271759L).sendMessageEmbeds(outputBox.build()).queue(); + } + else { + for (Long channelId : channelIds ) { + try { + if (jda.getTextChannelById(channelId).canTalk()) + jda.getTextChannelById(channelId).sendMessageEmbeds(outputBox.build()) + .queue(); + } + catch (NullPointerException e) { + // + } + } + } + } + catch (NumberFormatException e) { + //jda.getTextChannelById(channelId).sendMessage("Nope").queue(); + } + catch (NullPointerException e ) { + // + } + catch (Exception e) { + System.out.println("problem: " + e.getMessage()); + } + } +} diff --git a/src/main/java/de/ocame/ffdiscordbot/FanFiktionReader.java b/src/main/java/de/ocame/ffdiscordbot/FanFiktionReader.java new file mode 100644 index 0000000..86ca8eb --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/FanFiktionReader.java @@ -0,0 +1,361 @@ +package de.ocame.ffdiscordbot; + +import net.dv8tion.jda.api.JDA; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import java.util.Random; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + + +public class FanFiktionReader { + + private final HtmlFetcher fetcher; + + private final DiscordOutput dco; + + private final JDA jda; + + private long time = 30*60; + + + public FanFiktionReader(JDA jda) { + + fetcher = new HtmlFetcher(); + SQLiteStorage.createTable(); + dco = new DiscordOutput(); + this.jda = jda; + + } + + /** + * Liest neue Ereignse von der Alert Seite aus + */ + public void checkAlerts() { + + try { + String alertURL = "https://www.fanfiktion.de/?a=i"; + + Document alertPage = fetcher.fetchURL(alertURL); + + // Login Check + Elements title = alertPage.getElementsByTag("title"); + if(title.first().text().contains("keine Berechtigung!") || title.first().text().contains("Session-Error") || title.first().text().contains("Login")) { + login(); + alertPage = fetcher.fetchURL(alertURL); + title = alertPage.getElementsByTag("title"); + if(title.first().text().contains("keine Berechtigung!") || title.first().text().contains("Session-Error") || title.first().text().contains("Login")) { + // Fehler + return; + } + } + + // Story Check + Element authorBox = alertPage.getElementById("ffcbox-alerts_author-box"); + if(authorBox != null) { + Elements rows = authorBox.select("table tbody tr"); + for (Element row : rows) { + checkNewStoryFromAlert(row); + } + } + + // Kapitel Check + Element StoryBox = alertPage.getElementById("ffcbox-alerts_story-box"); + if(StoryBox != null) { + Elements rows = StoryBox.select("table tbody tr"); + + for (Element row : rows) { + Elements cols = row.select("td"); + if (cols.size() >= 2) { + Element link = cols.get(1).selectFirst("a"); + String linkHref = (link != null) ? link.attr("href") : "No link"; + String chaptertime = cols.get(6).text(); + long chapterUnixtime = DateToTimestamp(chaptertime); + + Element delLink = cols.last().selectFirst("a"); + String delLinkHref = (delLink != null) ? delLink.attr("href") : "No link"; + + checkNewChapter("https://www.fanfiktion.de" + linkHref, chapterUnixtime); + + // Ausführung des Links, um Inhalt aus Alert zu entfernen + try { + if(!delLinkHref.equals("No link")) + fetcher.fetchURL("https://www.fanfiktion.de" + delLinkHref); + } + catch (IOException | InterruptedException | URISyntaxException e) { + e.printStackTrace(); + } + + } + } + } + + } + catch (IOException | InterruptedException | URISyntaxException e) { + e.printStackTrace(); + } + Random random = new Random(); + this.time = random.nextLong(24*60,34*60); + } + + /** + * Liest neue Storys von der Alarm Seite aus + * + * @param row + */ + public void checkNewStoryFromAlert(Element row) { + Elements cols = row.select("td"); + if (cols.size() >= 2) { // Ensure there are at least two columns + /*name, link, ffid, date, autor*/ + String storyName = cols.get(1).text(); + String storyAutor = cols.get(3).text(); + String storytime = cols.get(4).text(); + long storyUnixtime = DateToTimestamp(storytime); + + Element link = cols.get(1).selectFirst("a"); + String linkHref = (link != null) ? link.attr("href") : "No link"; + String[] hrefElements = linkHref.split("/s/",2); + String storyId = hrefElements[1]; + if(!SQLiteStorage.checkStory(storyId)) { + /* todo: neue Story gefunden Ausgabe */ + DataStory newStory = new DataStory(storyName, "https://www.fanfiktion.de/s/" + storyId, storyId,storyUnixtime,storyAutor, 0); + SQLiteStorage.insertStorys(newStory); + dco.newStory(jda, newStory); + + } + Element delLink = cols.last().selectFirst("a"); + String delLinkHref = (delLink != null) ? delLink.attr("href") : "No link"; + + // Ausführung des Links, um Inhalt aus Alert zu entfernen + try { + if(!delLinkHref.equals("No link")) + fetcher.fetchURL("https://www.fanfiktion.de" + delLinkHref); + } + catch (IOException | InterruptedException | URISyntaxException e) { + e.printStackTrace(); + } + } + } + + /** + * Prüft neues Kapitel Anhand der Seite, das Datum wird nur aus bequemlichkeit mit übergeben, da das schneller auslesbar war + * + * @param url + * @param chapterDate + */ + public void checkNewChapter(String url, long chapterDate) { + try { + + // Login Check + Document storyPage = fetcher.fetchURL(url); + Elements title = storyPage.getElementsByTag("title"); + if(title.first().text().contains("keine Berechtigung!") || title.first().text().contains("Session-Error") || title.first().text().contains("Login")) { + login(); + storyPage = fetcher.fetchURL(url); + title = storyPage.getElementsByTag("title"); + if(title.first().text().contains("keine Berechtigung!") || title.first().text().contains("Session-Error") || title.first().text().contains("Login")) { + // Fehler + return; + } + } + + // Auswahl Select für Kapitel Auswahl + Element chapterSelect = storyPage.getElementById("kA"); + if(chapterSelect != null) { + Elements options = storyPage.select("option"); + String chapterName = options.last().text(); + String chapterNumer = options.last().attr("value"); + String onChange = chapterSelect.attributes().get("onchange") + .replace("location.href = '" , "") + .replace("' + this.value + '" , chapterNumer) + .replace("'" , ""); + + // Regulärer Ausdruck für die ffid nach /s/ + String regex = "/s/([a-zA-Z0-9]+)/"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(onChange); + String ffid = ""; + if (matcher.find()) { + // Die gefundene Nummernfolge ausgeben + ffid = matcher.group(1); + } else { + // todo: fehler behandlung + } + + if(SQLiteStorage.checkChapter(ffid, Long.parseLong(chapterNumer))) { + /* todo: neue Chapter gefunden Ausgabe */ + if(!SQLiteStorage.checkStory(ffid)) { + readandSaveStory(storyPage); + } + DataStory ds = SQLiteStorage.getStory(ffid); + DataChapter newChapter = new DataChapter(chapterName, "https://www.fanfiktion.de" + onChange, ffid, chapterDate, Long.parseLong(chapterNumer)); + SQLiteStorage.insertChapter(newChapter); + //clearChapter(); + dco.newChapter(jda, newChapter, ds); + } + } + } + catch (IOException | InterruptedException | URISyntaxException e) { + e.printStackTrace(); + } + } + + /** + * List alle Infos aus einer Story Seite aus, um das relevante zu speichern + * @param storyPage + */ + public void readandSaveStory(Document storyPage) { + Elements infoBox = storyPage.select(".story-left"); + String storyName = infoBox.first().getElementsByTag("h4").text(); + String storyAutor = infoBox.select("a.no-wrap").first().text(); + String storytime = infoBox.select(".flexicon-container.small-font.block div").first().text(); + long storyUnixtime = DateToTimestamp(storytime + " | 00:00 Uhr"); + + String storyLink = storyPage.select("#ffcbox-story .topics a").last().attr("href"); + + String regex = "/s/([a-zA-Z0-9]+)/"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(storyLink); + String ffid = ""; + if (matcher.find()) { + // Die gefundene Nummernfolge ausgeben + ffid = matcher.group(1); + } else { + // todo: fehler behandlung + } + + if(!SQLiteStorage.checkStory(ffid)) { + DataStory newStory = new DataStory(storyName, "https://www.fanfiktion.de/s/" + ffid, ffid, storyUnixtime, storyAutor, 0); + SQLiteStorage.insertStorys(newStory); + } + + } + + public void outputCheapterTester(int chapterId, String chapterTextTitle, String storyTitle, String autor, Long tagetChannel) { + DataStory ds = new DataStory(storyTitle, "https://www.fanfiktion.de", "", LocalDateTime.now().toEpochSecond(ZoneOffset.UTC), autor, 0); + DataChapter dc = new DataChapter(chapterTextTitle, "https://www.fanfiktion.de", "", LocalDateTime.now().toEpochSecond(ZoneOffset.UTC), 1); + + dco.newChapter(jda, dc, ds, chapterId, tagetChannel); + } + + /** + * Wandelt String Zeit in Unix Timestamp um + * + * @param dateString + * @return + */ + public long DateToTimestamp(String dateString) { + + //dateString = "02.08.2024 | 21:21 Uhr"; + + // Definiere das Datumsformat + //DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy | HH:mm 'Uhr'").withResolverStyle(ResolverStyle.STRICT); + //DateTimeFormatter formatteralternativ = DateTimeFormatter.ofPattern("dd.MM.yyyy'"); + + String[] patterns = {"dd.MM.yyyy | HH:mm 'Uhr'" , "dd.MM.yyyy'", "dd.MM.yyyy HH:mm'"}; + LocalDateTime dateTime = parseDateTimeWithMultiplePatterns(dateString, patterns); + if(dateTime == null || dateTime.toEpochSecond(ZoneOffset.UTC) == 0) { + return 0; + } + else + return dateTime.toEpochSecond(ZoneOffset.UTC); + + + } + + private static LocalDateTime parseDateTimeWithMultiplePatterns(String dateString, String[] patterns) { + for (String pattern : patterns) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + return LocalDateTime.parse(dateString.trim(), formatter); + } catch (DateTimeParseException e) { + // Continue with the next pattern if parsing fails + } + } + return null; // If none of the patterns work, return null + } + + /** + * Führt den Login aus + */ + public void login() { + + try { + ConfigLoader configLoader = new ConfigLoader(); + + String loginURL = "https://www.fanfiktion.de/?a=l"; + Document logintPage = fetcher.fetchURL(loginURL); + + Elements nameBox = logintPage.select ("input[name='nickname']"); + Elements pwBox = logintPage.select ("input[name='passwd']"); + + if(nameBox.isEmpty()) { + // todo: Fehler wenn Login nicht geht + } + + Elements aBox = logintPage.select ("input[name='a']"); + Elements uBox = logintPage.select ("input[name='u']"); + + String uValue = fetcher.getCookieValue("https://www.fanfiktion.de/", "u"); + uValue = java.net.URLDecoder.decode(uValue, StandardCharsets.UTF_8); + + String ffNickname = configLoader.getUsername(); + String ffPw = configLoader.getPassword(); + String postData = "a=l" // + java.net.URLEncoder.encode(aBox.attr("value"), StandardCharsets.UTF_8) + + "&u=" + java.net.URLEncoder.encode(uValue, StandardCharsets.UTF_8) + + "&nickname=" + java.net.URLEncoder.encode(ffNickname, StandardCharsets.UTF_8) + + "&passwd=" + java.net.URLEncoder.encode(ffPw, StandardCharsets.UTF_8) + + "&savelogindata=" + java.net.URLEncoder.encode("1", StandardCharsets.UTF_8); + + + + Document loginRequest = fetcher.fetchURL("https://www.fanfiktion.de/", postData); + nameBox = loginRequest.select ("input[name='nickname']"); + + if(!nameBox.isEmpty()) { + // todo: Fehler wenn Login nicht geht + } + + } + catch (IOException | InterruptedException | URISyntaxException e) { + e.printStackTrace(); + } + + } + + + public long getTime() { + return time; + } + + public void clearChapter(String url) { + try { + Document storyPage = fetcher.fetchURL(url); + Elements title = storyPage.getElementsByTag("title"); + if (title.first().text().contains("keine Berechtigung!")) { + login(); + storyPage = fetcher.fetchURL(url); + title = storyPage.getElementsByTag("title"); + if (title.first().text().contains("keine Berechtigung!")) { + // Fehler + return; + } + } + } + catch (IOException | InterruptedException | URISyntaxException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/HtmlFetcher.java b/src/main/java/de/ocame/ffdiscordbot/HtmlFetcher.java new file mode 100644 index 0000000..81f45a8 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/HtmlFetcher.java @@ -0,0 +1,157 @@ +package de.ocame.ffdiscordbot; + +import java.io.IOException; +import java.io.FileReader; +import java.io.FileWriter; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.HttpCookie; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.CookieStore; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +import java.net.http.HttpRequest.BodyPublishers; + + +public class HtmlFetcher { + + private final HttpClient client; + private final CookieManager cookieManager; + private static final String COOKIE_FILE = "cookies.json"; + + public HtmlFetcher() { + cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); + client = HttpClient.newBuilder() + .cookieHandler(cookieManager) + .followRedirects(HttpClient.Redirect.ALWAYS) + .connectTimeout(Duration.ofSeconds(10)) // Verbindungs-Timeout + .build(); + + loadCookies(); + } + + public Document fetchURL(String url) throws IOException, InterruptedException, URISyntaxException { + return fetchURL(url, ""); + } + public Document fetchURL(String url, String postData) throws IOException, InterruptedException, URISyntaxException { + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0") + .header("Accept", "text/html") + .header("Accept-Language", "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7") + .timeout(Duration.ofSeconds(10));// Lese-Timeout + + if (postData == null || postData.isEmpty()) { + requestBuilder.GET(); + } + else { + // Wenn postData nicht leer ist, füge die POST-Daten hinzu + requestBuilder + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(BodyPublishers.ofString(postData)); + } + + HttpRequest request = requestBuilder.build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + // Print cookies if any + CookieStore cookieStore = cookieManager.getCookieStore(); + List cookies = cookieStore.getCookies(); + /*for (HttpCookie cookie : cookies) { + System.out.println("Cookie: " + cookie); + }*/ + + // Save cookies + saveCookies(); + + return Jsoup.parse(response.body()); + + } + + private void saveCookies() { + CookieStore cookieStore = cookieManager.getCookieStore(); + List cookies = cookieStore.getCookies(); + List serializableCookies = new ArrayList<>(); + for (HttpCookie cookie : cookies) { + serializableCookies.add(new SerializableCookie(cookie)); + } + + try (FileWriter writer = new FileWriter(COOKIE_FILE)) { + new Gson().toJson(serializableCookies, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void loadCookies() { + try (FileReader reader = new FileReader(COOKIE_FILE)) { + List serializableCookies = new Gson().fromJson(reader, new TypeToken>(){}.getType()); + if (serializableCookies != null) { + for (SerializableCookie serializableCookie : serializableCookies) { + cookieManager.getCookieStore().add(new URI(serializableCookie.uri), serializableCookie.toHttpCookie()); + } + } + } catch (IOException | URISyntaxException e) { + e.printStackTrace(); + } + } + + private static class SerializableCookie { + private String uri; + private String name; + private String value; + private String path; + private String domain; + private long maxAge; + private boolean secure; + private boolean httpOnly; + + SerializableCookie(HttpCookie cookie) { + this.uri = cookie.getDomain(); + this.name = cookie.getName(); + this.value = cookie.getValue(); + this.path = cookie.getPath(); + this.domain = cookie.getDomain(); + this.maxAge = cookie.getMaxAge(); + this.secure = cookie.getSecure(); + this.httpOnly = cookie.isHttpOnly(); + } + + HttpCookie toHttpCookie() { + HttpCookie cookie = new HttpCookie(name, value); + cookie.setPath(path); + cookie.setDomain(domain); + cookie.setMaxAge(maxAge); + cookie.setSecure(secure); + cookie.setHttpOnly(httpOnly); + return cookie; + } + } + + public CookieManager getCookieManager() { + return cookieManager; + } + + public String getCookieValue(String uri, String cookieName) { + CookieStore cookieStore = cookieManager.getCookieStore(); + List cookies = cookieStore.get(URI.create(uri)); + for (HttpCookie cookie : cookies) { + if (cookie.getName().equals(cookieName)) { + return cookie.getValue(); + } + } + return null; // Cookie wurde nicht gefunden + } +} diff --git a/src/main/java/de/ocame/ffdiscordbot/Main.java b/src/main/java/de/ocame/ffdiscordbot/Main.java new file mode 100644 index 0000000..271aaad --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/Main.java @@ -0,0 +1,71 @@ +package de.ocame.ffdiscordbot; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.requests.GatewayIntent; +import org.jsoup.nodes.Document; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +public class Main { + + public static void main(String[] args) { + + ConfigLoader configLoader = new ConfigLoader(); + + // Insert your bot's token here + //String token = "MTI3NzcxNTQ1NTI1MDkyMzU4MA.GUuRyh.07mjVRcI7MhLHo-Wdnytvxlb3Pts-hfRaNMaPA"; + // iro + String token = configLoader.getToken(); + /**/ + JDA jda = JDABuilder.createDefault(token) + .enableIntents(GatewayIntent.MESSAGE_CONTENT) + .setActivity(Activity.playing("mit der Marine fangen")) + .setStatus(OnlineStatus.ONLINE) + .setAutoReconnect(true) + .build();/**/ + + try { + TimeUnit.SECONDS.sleep(10); + } catch (InterruptedException e) { + + } + FanFiktionReader ffr = new FanFiktionReader(jda); + + // Start des Socket-Servers in einem separaten Thread + Thread socketThread = new Thread(new SocketServerRunnable(jda, ffr)); + socketThread.start(); + + + //ffr.checkAlerts(); + + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + final long[] delay = {30 * 60}; + + AtomicReference> scheduledFuture = new AtomicReference<>(); + Runnable task = new Runnable() { + @Override + public void run() { + //ffr.checkAlerts(); + // Berechne das neue Intervall basierend auf ffr.getTime() + delay[0] = ffr.getTime(); + + // Plane die Aufgabe erneut mit dem neuen Intervall + scheduledFuture.get().cancel(false); + scheduledFuture.set(scheduler.schedule(this, delay[0], TimeUnit.SECONDS)); + } + }; + + // Plane die erste Ausführung der Aufgabe + scheduledFuture.set(scheduler.schedule(task, delay[0], TimeUnit.SECONDS)); + + + } + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java b/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java new file mode 100644 index 0000000..c7853ab --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java @@ -0,0 +1,457 @@ +package de.ocame.ffdiscordbot; +import java.awt.*; +import java.sql.*; + +import org.json.JSONArray; +import java.util.ArrayList; +import java.util.List; + +public class SQLiteStorage { + + private static final String DB_URL = "jdbc:sqlite:data.db"; + private static final int MAX_RETRIES = 5; + private static final int RETRY_WAIT_TIME_MS = 1000; + + public static void createTable() { + String sql = """ + CREATE TABLE IF NOT EXISTS Storys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + link TEXT NOT NULL, + ffid TEXT NOT NULL, + date INTEGER NOT NULL, + tagId INTEGER NULL, + autor TEXT NOT NULL + );"""; + + String sqlChapter = """ + CREATE TABLE IF NOT EXISTS Chapter ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + storyId TEXT NOT NULL, + name TEXT NOT NULL, + link TEXT NOT NULL, + date INTEGER NOT NULL, + number INTEGER NOT NULL + );"""; + + String sqlChapterText = """ + CREATE TABLE IF NOT EXISTS ChapterText ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + chapterText TEXT NOT NULL, + color TEXT NOT NULL, + imageUrl TEXT NOT NULL, + active BOOLEAN NOT NULL DEFAULT 1 + );"""; + + String sqlStoryText = """ + CREATE TABLE IF NOT EXISTS StoryText ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + storyText TEXT NOT NULL, + color TEXT NOT NULL, + imageUrl TEXT NOT NULL, + active BOOLEAN NOT NULL DEFAULT 1 + );"""; + + String sqlTags = """ + CREATE TABLE IF NOT EXISTS Tags ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ); + """; + + String chapterTag = """ + CREATE TABLE IF NOT EXISTS ChapterText_Tags ( + chapterText_id INTEGER, + tag_id INTEGER, + PRIMARY KEY (chapterText_id, tag_id), + FOREIGN KEY (chapterText_id) REFERENCES ChapterText(id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE + ); + """; + + String options = """ + CREATE TABLE IF NOT EXISTS Options ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + value TEXT + ); + """; + + String createIndexSQL = "CREATE INDEX IF NOT EXISTS idx_ffid ON Storys(ffid)"; + String createIndexSQLChapter = "CREATE INDEX IF NOT EXISTS idx_ffid ON Chapter(ffid)"; + String createIndexSQLChapter2 = "CREATE INDEX IF NOT EXISTS idx_storyId ON Chapter(storyId)"; + String createIndexSQLChapterText = "CREATE INDEX IF NOT EXISTS idx_chapterText_active ON ChapterText (active);\n"; + String createIndexSQLStoryText = "CREATE INDEX IF NOT EXISTS idx_storyText_active ON StoryText (active);\n"; + + + try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement()) { + stmt.execute(sql); + stmt.execute(createIndexSQL); + + stmt.execute(sqlChapter); + stmt.execute(sqlChapterText); + stmt.execute(sqlStoryText); + stmt.execute(sqlTags); + stmt.execute(chapterTag); + stmt.execute(options); + + stmt.execute(createIndexSQLChapter); + stmt.execute(createIndexSQLChapter2); + stmt.execute(createIndexSQLChapterText); + stmt.execute(createIndexSQLStoryText); + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + } + + public static void insertStorys(DataStory record) { + String sql = "INSERT INTO Storys(name, link, ffid, date, autor) VALUES(?, ?, ?, ?, ?)"; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + executeUpdateWithRetry(conn, sql, + record.name(), + record.link(), + record.ffid(), + record.date(), + record.author()); + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + } + + + public static boolean checkStory(String ffid) { + + String sql = "SELECT COUNT(*) FROM Storys WHERE ffid = ?"; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql, ffid); + + if (rs.next()) { + int count = rs.getInt(1); + return count > 0; // Gibt true zurück, wenn die ID existiert, sonst false + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + + return false; + } + + public static boolean checkChapter(String ffid, long number) { + String sql = "SELECT COUNT(*) AS anzahl FROM Chapter WHERE storyId = ? AND number = ?"; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql, ffid, number); + + if (rs.next()) { + int count = rs.getInt(1); + return count > 0; // Gibt true zurück, wenn die ID existiert, sonst false + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + + return false; + } + + public static DataStory getStory(String ffid) { + String sql = "SELECT name, link, ffid, date, autor, tagId FROM Storys WHERE ffid = ?"; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql, ffid); + + if (rs.next()) { + return new DataStory( + rs.getString("name"), + rs.getString("link"), + ffid, + rs.getLong("date"), + rs.getString("autor"), + rs.getInt("tagId") + ); + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + return null; + } + public static DataChapter getChapter(String ffid, long number) { + String sql = "SELECT storyId, name, link, date, number FROM Chapter WHERE storyId = ? AND number = ?"; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql, ffid, number); + + if (rs != null && rs.next()) { + return new DataChapter( + rs.getString("name"), + rs.getString("link"), + ffid, + rs.getLong("date"), + number + ); + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + return null; + } + + public static void insertChapter(DataChapter record) { + String sql = "INSERT INTO Chapter(name, link, storyId, date, number) VALUES(?, ?, ?, ?, ?)"; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + executeUpdateWithRetry(conn, sql, + record.name(), + record.link(), + record.storyId(), + record.date(), + record.number()); + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + } + + + public static void main(String[] args) { + createTable(); + + /* DataStory record1 = new DataStory("Text 1", "https://link1.com", "Text 2"); + DataStory record2 = new DataStory("Text 3", "https://link2.com", "Text 4"); + + // Insert data into SQLite + insertData(record1); + insertData(record2); + + // Load data from SQLite + List loadedData = loadData(); + for (DataStory record : loadedData) { + System.out.println(record); + } + }*/ + } + + private static int executeUpdateWithRetry(Connection conn, String sql, Object... params) throws SQLException { + int attempt = 0; + boolean success = false; + while (attempt < MAX_RETRIES && !success) { + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + setParameters(pstmt, params); + int rowsAffected = pstmt.executeUpdate(); + success = true; + return rowsAffected; + } catch (SQLException e) { + handleSQLException(e, ++attempt); + } + } + throw new SQLException("Operation nach " + MAX_RETRIES + " Versuchen fehlgeschlagen."); + } + + private static ResultSet executeQueryWithRetry(Connection conn, String sql, Object... params) throws SQLException { + int attempt = 0; + boolean success = false; + ResultSet rs = null; + + while (attempt < MAX_RETRIES && !success) { + try { + PreparedStatement pstmt = conn.prepareStatement(sql); + setParameters(pstmt, params); + rs = pstmt.executeQuery(); + success = true; + + } catch (SQLException e) { + handleSQLException(e, ++attempt); + if (rs != null) { + try { + rs.close(); + } catch (SQLException ex) { + System.out.println("Failed to close ResultSet: " + ex.getMessage()); + } + } + } + if (!success) { + throw new SQLException("Operation nach " + MAX_RETRIES + " Versuchen fehlgeschlagen."); + } + return rs; + } + throw new SQLException("Operation nach " + MAX_RETRIES + " Versuchen fehlgeschlagen."); + } + + private static void setParameters(PreparedStatement pstmt, Object... params) throws SQLException { + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + } + + private static void handleSQLException(SQLException e, int attempt) throws SQLException { + if (e.getMessage().contains("database is locked")) { + System.out.println("Datenbank gesperrt, versuche erneut... (Versuch " + attempt + " von " + MAX_RETRIES + ")"); + try { + Thread.sleep(RETRY_WAIT_TIME_MS); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new SQLException("Der Thread wurde unterbrochen.", ie); + } + } else { + throw e; // Andere SQL-Fehler weiterwerfen + } + } + + public static Long[] getChannelIds() { + String sql = "SELECT name, value FROM \"Options\" WHERE name = \"channels\""; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql); + + if (rs.next()) { + String channelOption = rs.getString("value"); + if (!channelOption.isEmpty()) { + + JSONArray jsonArray = new JSONArray(channelOption); + List longList = new ArrayList<>(); + + for (int i = 0; i < jsonArray.length(); i++) { + try { + // Versuche, den Wert in ein Long zu konvertieren + long value = jsonArray.getLong(i); + longList.add(value); // Füge den Wert der Liste hinzu, wenn es ein gültiger Long ist + } catch (Exception e) { + // Wenn der Wert kein gültiges Long ist, wird er übersprungen + //System.out.println("Überspringe ungültigen Wert: " + jsonArray.get(i)); + } + } + + return longList.toArray(new Long[0]); + } + return null; + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + return null; + } + + public static List getChapterTexte(DataStory ds) { + String sql = """ + SELECT\s + CT.id, + CT.chapterText, + CT.color, + CT.imageUrl + FROM\s + ChapterText CT + LEFT JOIN\s + ChapterText_Tags CTT\s + ON\s + CT.id = CTT.chapterText_id + LEFT JOIN\s + Tags T\s + ON\s + T.id = CTT.tag_id + WHERE + T.id = ? AND CT.active = TRUE + GROUP BY\s + CT.id;"""; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql, ds.tagId()); + + List outputList = new ArrayList<>(); + + if (rs.next()) { + outputList.add(new StoryOutputText( + rs.getString("chapterText"), + rs.getString("imageUrl"), + Color.decode(rs.getString("color"))) + ); + } + return outputList; + + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + + return null; + } + + public static List getChapterTexte(int ctId) { + String sql = """ + SELECT\s + CT.id, + CT.chapterText, + CT.color, + CT.imageUrl + FROM\s + ChapterText CT + WHERE + CT.id = ? + GROUP BY\s + CT.id;"""; + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql, ctId); + + List outputList = new ArrayList<>(); + + if (rs.next()) { + outputList.add(new StoryOutputText( + rs.getString("chapterText"), + rs.getString("imageUrl"), + Color.decode(rs.getString("color"))) + ); + } + return outputList; + + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + + return null; + } + + public static List getStoryTexte() { + String sql = """ + SELECT\s + CT.id, + CT.storyText, + CT.color, + CT.imageUrl + FROM\s + StoryText CT + WHERE + CT.active = TRUE;"""; + + + try (Connection conn = DriverManager.getConnection(DB_URL)) { + ResultSet rs = executeQueryWithRetry(conn, sql); + + List outputList = new ArrayList<>(); + + if (rs.next()) { + outputList.add(new StoryOutputText( + rs.getString("storyText"), + rs.getString("imageUrl"), + Color.decode(rs.getString("color"))) + ); + } + return outputList; + + } catch (SQLException e) { + System.out.println(e.getMessage()); + } + + return null; + } +} + +record DataStory(String name, String link, String ffid, long date, String author, Integer tagId) { + +} +record DataChapter(String name, String link, String storyId, long date, long number) { + +} + + diff --git a/src/main/java/de/ocame/ffdiscordbot/SocketServerRunnable.java b/src/main/java/de/ocame/ffdiscordbot/SocketServerRunnable.java new file mode 100644 index 0000000..d16a4a2 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/SocketServerRunnable.java @@ -0,0 +1,104 @@ +package de.ocame.ffdiscordbot; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +import org.json.JSONObject; + + +import java.io.*; +import java.net.*; + +class SocketServerRunnable implements Runnable { + + private final JDA jda; + + private final FanFiktionReader ffr; + + public SocketServerRunnable(JDA jda, FanFiktionReader ffr) { + + this.jda = jda; + this.ffr = ffr; + } + + @Override + public void run() { + int port = 12345; + try (ServerSocket serverSocket = new ServerSocket(port)) { + System.out.println("Socket-Server läuft auf Port " + port); + + while (true) { + try (Socket clientSocket = serverSocket.accept(); + BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) { + + String jsonMessageFromClient = in.readLine(); + System.out.println("Nachricht vom Client erhalten: " + jsonMessageFromClient); + JSONObject jsonData = new JSONObject(jsonMessageFromClient); + String action = jsonData.getString("action"); + + JSONObject jsonResponse= new JSONObject(); + Long channelId = null; + + if(action.equals("channelCheck")) { + Object channelIdObj = jsonData.get("channelId"); + if(channelIdObj.equals("")) { + jsonResponse.put("status", "fail"); + continue; + } + + if (channelIdObj instanceof Number) { + channelId = ((Number) channelIdObj).longValue(); + } else if (channelIdObj instanceof String) { + String channelIdStr = (String) channelIdObj; + channelId = Long.parseLong(channelIdStr); + } else { + jsonResponse.put("status", "fail"); + continue; + } + + String response = ""; + + try { + TextChannel textchannel = jda.getTextChannelById(channelId); + if (textchannel.canTalk()) { + jsonResponse.put("status", "success") + .put("name", textchannel.getName()) + .put("serverName", textchannel.getGuild().getName()); + } + else { + jsonResponse.put("status", "fail"); + } + } catch (NullPointerException e) { + jsonResponse.put("status", "error"); + } + + out.println(jsonResponse); + } + else if(action.equals("check")) { + ffr.checkAlerts(); + jsonResponse.put("status", "success"); + out.println(jsonResponse); + } + else if(action.equals("chapterTest")) { + String storyName = jsonData.getString("storyName"); + String chapterName = jsonData.getString("chapterName"); + String author = jsonData.getString("author"); + Integer id = jsonData.getInt("id"); + Long target = jsonData.getLong("target"); + + + ffr.outputCheapterTester(id, chapterName, storyName, author, target); + jsonResponse.put("status", "success"); + out.println(jsonResponse); + } + + } catch (IOException | NullPointerException e) { + System.out.println("Fehler bei der Kommunikation mit dem Client: " + e.getMessage()); + } + } + } catch (IOException e) { + System.out.println("Server konnte nicht gestartet werden: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/ocame/ffdiscordbot/StoryOutputText.java b/src/main/java/de/ocame/ffdiscordbot/StoryOutputText.java new file mode 100644 index 0000000..8b0ebd7 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/StoryOutputText.java @@ -0,0 +1,39 @@ +package de.ocame.ffdiscordbot; + +import java.awt.*; + +public class StoryOutputText { + + private final String message; + private final String image; + private final Color color; + + public StoryOutputText(String message) { + this.message = message; + this.image = null; + this.color = new Color(36, 153, 245); + } + public StoryOutputText(String message, String image) { + this.message = message; + this.image = image; + this.color = new Color(36, 153, 245); + } + public StoryOutputText(String message, String image, Color color) { + this.message = message; + this.image = image; + this.color = color; + } + + public String getMessage() { + return message; + } + + public String getImage() { + return image; + } + + public Color getColor() { + return color; + } + +}