From c7a9a84dac7d53161c99d0bc4eef4a56f2513b6b Mon Sep 17 00:00:00 2001 From: Ocame Date: Mon, 9 Sep 2024 21:25:02 +0200 Subject: [PATCH] =?UTF-8?q?=C3=BCberarbeitung=20Code=20und=20hinzuf=C3=BCg?= =?UTF-8?q?en=20Auto=20Favo=20f=C3=BCr=20neue=20Storys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../de/ocame/ffdiscordbot/ConfigLoader.java | 8 + .../de/ocame/ffdiscordbot/DataChapter.java | 5 + .../java/de/ocame/ffdiscordbot/DataStory.java | 5 + .../de/ocame/ffdiscordbot/DiscordOutput.java | 242 ----------------- .../ocame/ffdiscordbot/FanFiktionReader.java | 245 +++++++++--------- src/main/java/de/ocame/ffdiscordbot/Main.java | 13 +- ...putText.java => OutputContentElement.java} | 8 +- .../de/ocame/ffdiscordbot/SQLiteStorage.java | 74 ++---- .../ffdiscordbot/SocketServerRunnable.java | 127 +++++---- .../DefaultHtmlFetcher.java} | 59 ++--- .../ffdiscordbot/fetcher/HtmlFetcher.java | 9 + .../ffdiscordbot/output/BaseNotifier.java | 132 ++++++++++ .../ffdiscordbot/output/ChapterNotifier.java | 49 ++++ .../ffdiscordbot/output/InfoNotifier.java | 54 ++++ .../ffdiscordbot/output/StoryNotifier.java | 62 +++++ 16 files changed, 579 insertions(+), 515 deletions(-) create mode 100644 src/main/java/de/ocame/ffdiscordbot/DataChapter.java create mode 100644 src/main/java/de/ocame/ffdiscordbot/DataStory.java delete mode 100644 src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java rename src/main/java/de/ocame/ffdiscordbot/{StoryOutputText.java => OutputContentElement.java} (74%) rename src/main/java/de/ocame/ffdiscordbot/{HtmlFetcher.java => fetcher/DefaultHtmlFetcher.java} (82%) create mode 100644 src/main/java/de/ocame/ffdiscordbot/fetcher/HtmlFetcher.java create mode 100644 src/main/java/de/ocame/ffdiscordbot/output/BaseNotifier.java create mode 100644 src/main/java/de/ocame/ffdiscordbot/output/ChapterNotifier.java create mode 100644 src/main/java/de/ocame/ffdiscordbot/output/InfoNotifier.java create mode 100644 src/main/java/de/ocame/ffdiscordbot/output/StoryNotifier.java diff --git a/build.gradle b/build.gradle index 0762c79..8d5bf7b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { application.mainClass = "de.ocame.ffdiscordbot.Main" group 'de.ocame' -version '1.1-SNAPSHOT' +version '1.2-SNAPSHOT' repositories { diff --git a/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java b/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java index 9257d20..f9161ec 100644 --- a/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java +++ b/src/main/java/de/ocame/ffdiscordbot/ConfigLoader.java @@ -29,4 +29,12 @@ public class ConfigLoader { return properties.getProperty("ff.password"); } + public String getFfFavList() { + return properties.getProperty("ff.favList"); + } + + public Long getDebugChannel() { + return Long.parseLong(properties.getProperty("debug.Channel")); + } + } diff --git a/src/main/java/de/ocame/ffdiscordbot/DataChapter.java b/src/main/java/de/ocame/ffdiscordbot/DataChapter.java new file mode 100644 index 0000000..18120d4 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/DataChapter.java @@ -0,0 +1,5 @@ +package de.ocame.ffdiscordbot; + +public record DataChapter(String name, String link, String storyId, long date, long number) { + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/DataStory.java b/src/main/java/de/ocame/ffdiscordbot/DataStory.java new file mode 100644 index 0000000..f8fd053 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/DataStory.java @@ -0,0 +1,5 @@ +package de.ocame.ffdiscordbot; + +public record DataStory(String name, String link, String ffid, long date, String author, Integer tagId) { + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java b/src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java deleted file mode 100644 index 2a7d6c6..0000000 --- a/src/main/java/de/ocame/ffdiscordbot/DiscordOutput.java +++ /dev/null @@ -1,242 +0,0 @@ -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 index 86ca8eb..7bcc784 100644 --- a/src/main/java/de/ocame/ffdiscordbot/FanFiktionReader.java +++ b/src/main/java/de/ocame/ffdiscordbot/FanFiktionReader.java @@ -1,69 +1,60 @@ package de.ocame.ffdiscordbot; +import de.ocame.ffdiscordbot.fetcher.DefaultHtmlFetcher; +import de.ocame.ffdiscordbot.output.ChapterNotifier; +import de.ocame.ffdiscordbot.output.InfoNotifier; +import de.ocame.ffdiscordbot.output.StoryNotifier; 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.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.Instant; 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 DefaultHtmlFetcher fetcher; // Use the HtmlFetcher interface private final JDA jda; - private long time = 30*60; + private long time = 30 * 60; + + private final String URL = "https://www.fanfiktion.de"; public FanFiktionReader(JDA jda) { - - fetcher = new HtmlFetcher(); + this.fetcher = new DefaultHtmlFetcher(); SQLiteStorage.createTable(); - dco = new DiscordOutput(); this.jda = jda; - } /** - * Liest neue Ereignse von der Alert Seite aus + * 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; - } - } + Document alertPage = fetchHTMLWithAuthentication(URL + "/?a=i"); + if (alertPage == null) + return; // Story Check Element authorBox = alertPage.getElementById("ffcbox-alerts_author-box"); - if(authorBox != null) { + if (authorBox != null) { Elements rows = authorBox.select("table tbody tr"); for (Element row : rows) { checkNewStoryFromAlert(row); @@ -72,7 +63,7 @@ public class FanFiktionReader { // Kapitel Check Element StoryBox = alertPage.getElementById("ffcbox-alerts_story-box"); - if(StoryBox != null) { + if (StoryBox != null) { Elements rows = StoryBox.select("table tbody tr"); for (Element row : rows) { @@ -80,20 +71,19 @@ public class FanFiktionReader { 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(); + String chaptertime = cols.get(5).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); + checkNewChapter(URL + 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) { + if (!delLinkHref.equals("No link")) + fetcher.fetchHtml(URL + delLinkHref); + } catch (IOException | InterruptedException e) { e.printStackTrace(); } @@ -101,16 +91,15 @@ public class FanFiktionReader { } } - } - catch (IOException | InterruptedException | URISyntaxException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } Random random = new Random(); - this.time = random.nextLong(24*60,34*60); + this.time = random.nextLong(24 * 60, 34 * 60); } /** - * Liest neue Storys von der Alarm Seite aus + * Liest neue Storys von der Alarm-Seite aus * * @param row */ @@ -125,24 +114,22 @@ public class FanFiktionReader { Element link = cols.get(1).selectFirst("a"); String linkHref = (link != null) ? link.attr("href") : "No link"; - String[] hrefElements = linkHref.split("/s/",2); + 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); + if (SQLiteStorage.isStoryInvalid(storyId)) { + DataStory newStory = new DataStory(storyName, URL + "/s/" + storyId, storyId, storyUnixtime, storyAutor, 0); SQLiteStorage.insertStorys(newStory); - dco.newStory(jda, newStory); - + new StoryNotifier(jda, newStory).sendOutput(); + addStoryToFavorit(storyId); } 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) { + if (!delLinkHref.equals("No link")) + fetcher.fetchHtml(URL + delLinkHref); + } catch (IOException | InterruptedException e) { e.printStackTrace(); } } @@ -157,29 +144,21 @@ public class FanFiktionReader { 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; - } - } + Document storyPage = fetchHTMLWithAuthentication(url); + if (storyPage == null) + return; + // Auswahl Select für Kapitel Auswahl Element chapterSelect = storyPage.getElementById("kA"); - if(chapterSelect != null) { + 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("'" , ""); + .replace("location.href = '", "") + .replace("' + this.value + '", chapterNumer) + .replace("'", ""); // Regulärer Ausdruck für die ffid nach /s/ String regex = "/s/([a-zA-Z0-9]+)/"; @@ -193,26 +172,26 @@ public class FanFiktionReader { // todo: fehler behandlung } - if(SQLiteStorage.checkChapter(ffid, Long.parseLong(chapterNumer))) { + if (!SQLiteStorage.checkChapter(ffid, Long.parseLong(chapterNumer))) { /* todo: neue Chapter gefunden Ausgabe */ - if(!SQLiteStorage.checkStory(ffid)) { - readandSaveStory(storyPage); + if (SQLiteStorage.isStoryInvalid(ffid)) { + readandSaveStory(storyPage); } DataStory ds = SQLiteStorage.getStory(ffid); - DataChapter newChapter = new DataChapter(chapterName, "https://www.fanfiktion.de" + onChange, ffid, chapterDate, Long.parseLong(chapterNumer)); + DataChapter newChapter = new DataChapter(chapterName, URL + onChange, ffid, chapterDate, Long.parseLong(chapterNumer)); SQLiteStorage.insertChapter(newChapter); //clearChapter(); - dco.newChapter(jda, newChapter, ds); + new ChapterNotifier(jda, newChapter, ds).sendOutput(); } } - } - catch (IOException | InterruptedException | URISyntaxException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } } /** * List alle Infos aus einer Story Seite aus, um das relevante zu speichern + * * @param storyPage */ public void readandSaveStory(Document storyPage) { @@ -231,22 +210,24 @@ public class FanFiktionReader { if (matcher.find()) { // Die gefundene Nummernfolge ausgeben ffid = matcher.group(1); - } else { + } + else { // todo: fehler behandlung + return; } - if(!SQLiteStorage.checkStory(ffid)) { - DataStory newStory = new DataStory(storyName, "https://www.fanfiktion.de/s/" + ffid, ffid, storyUnixtime, storyAutor, 0); + if (SQLiteStorage.isStoryInvalid(ffid)) { + DataStory newStory = new DataStory(storyName, URL + "/s/" + ffid, ffid, storyUnixtime, storyAutor, 0); SQLiteStorage.insertStorys(newStory); + addStoryToFavorit(ffid); } } 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); + DataStory ds = new DataStory(storyTitle, URL, "", Instant.now().getEpochSecond(), autor, 0); + DataChapter dc = new DataChapter(chapterTextTitle, URL, "", Instant.now().getEpochSecond(), 1); + new ChapterNotifier(jda, dc, ds).setVersion(chapterId).sendOutput(tagetChannel); } /** @@ -257,19 +238,23 @@ public class FanFiktionReader { */ public long DateToTimestamp(String dateString) { - //dateString = "02.08.2024 | 21:21 Uhr"; + //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'"); + // 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); + 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 { + ZoneId zoneId = ZoneId.of("Europe/Berlin"); + ZonedDateTime zonedDateTime = dateTime.atZone(zoneId); + Instant instant = zonedDateTime.toInstant(); + + return instant.getEpochSecond(); + } } @@ -290,24 +275,25 @@ public class FanFiktionReader { * Führt den Login aus */ public void login() { - + System.out.print("Login muss ausgeführt werden"); try { ConfigLoader configLoader = new ConfigLoader(); - String loginURL = "https://www.fanfiktion.de/?a=l"; - Document logintPage = fetcher.fetchURL(loginURL); + String loginURL = URL + "/?a=l"; + Document logintPage = fetcher.fetchHtml(loginURL); - Elements nameBox = logintPage.select ("input[name='nickname']"); - Elements pwBox = logintPage.select ("input[name='passwd']"); + Elements nameBox = logintPage.select("input[name='nickname']"); + Elements pwBox = logintPage.select("input[name='passwd']"); - if(nameBox.isEmpty()) { - // todo: Fehler wenn Login nicht geht + if (nameBox.isEmpty()) { + time += 2 * 60 * 60; // Zeit um 2h erhöhen + new InfoNotifier(jda).setMessagetext("Login Fehlgeschlagen").sendOutput(); } - Elements aBox = logintPage.select ("input[name='a']"); - Elements uBox = logintPage.select ("input[name='u']"); + Elements aBox = logintPage.select("input[name='a']"); + Elements uBox = logintPage.select("input[name='u']"); - String uValue = fetcher.getCookieValue("https://www.fanfiktion.de/", "u"); + String uValue = fetcher.getCookieValue(URL, "u"); uValue = java.net.URLDecoder.decode(uValue, StandardCharsets.UTF_8); String ffNickname = configLoader.getUsername(); @@ -319,16 +305,15 @@ public class FanFiktionReader { + "&savelogindata=" + java.net.URLEncoder.encode("1", StandardCharsets.UTF_8); + Document loginRequest = fetcher.fetchHtml(URL, postData); + nameBox = loginRequest.select("input[name='nickname']"); - Document loginRequest = fetcher.fetchURL("https://www.fanfiktion.de/", postData); - nameBox = loginRequest.select ("input[name='nickname']"); - - if(!nameBox.isEmpty()) { - // todo: Fehler wenn Login nicht geht + if (!nameBox.isEmpty()) { + time += 2 * 60 * 60; // Zeit um 2h erhöhen + new InfoNotifier(jda).setMessagetext("Login Fehlgeschlagen").sendOutput(); } - } - catch (IOException | InterruptedException | URISyntaxException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } @@ -339,23 +324,45 @@ public class FanFiktionReader { 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; - } + public Document fetchHTMLWithAuthentication(String alertURL) throws IOException, InterruptedException { + + Document alertPage = fetcher.fetchHtml(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.fetchHtml(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 null; } } - catch (IOException | InterruptedException | URISyntaxException e) { - e.printStackTrace(); + + return alertPage; + } + + public void addStoryToFavorit(String ffid) { + + ConfigLoader configLoader = new ConfigLoader(); + String ffFavList = configLoader.getFfFavList(); + String postData = "storyid=" + java.net.URLEncoder.encode(ffid, StandardCharsets.UTF_8) + + "&favlist=" + java.net.URLEncoder.encode(ffFavList, StandardCharsets.UTF_8) + + "&alert=1"; + try { + Document sendToFav = fetcher.fetchHtml(URL + "/?a=q&a1=v&t=addstory&lbi=popup-addtofav-body&ar=1&action=i", postData); + if(sendToFav.getElementsByClass("successbox").isEmpty()) { + System.out.print("Story konnte nicht hinzugefügt werden"); + new InfoNotifier(jda).setMessagetext("Story " + ffid + "konnte nicht hinzugefügt werden. \n" + sendToFav.toString()).sendOutput(); } + } + catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + + } } diff --git a/src/main/java/de/ocame/ffdiscordbot/Main.java b/src/main/java/de/ocame/ffdiscordbot/Main.java index 271aaad..72d27dc 100644 --- a/src/main/java/de/ocame/ffdiscordbot/Main.java +++ b/src/main/java/de/ocame/ffdiscordbot/Main.java @@ -5,7 +5,6 @@ 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; @@ -19,9 +18,6 @@ public class Main { 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) @@ -33,7 +29,8 @@ public class Main { try { TimeUnit.SECONDS.sleep(10); - } catch (InterruptedException e) { + } + catch (InterruptedException e) { } FanFiktionReader ffr = new FanFiktionReader(jda); @@ -43,7 +40,7 @@ public class Main { socketThread.start(); - //ffr.checkAlerts(); + ffr.checkAlerts(); ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); final long[] delay = {30 * 60}; @@ -52,7 +49,7 @@ public class Main { Runnable task = new Runnable() { @Override public void run() { - //ffr.checkAlerts(); + ffr.checkAlerts(); // Berechne das neue Intervall basierend auf ffr.getTime() delay[0] = ffr.getTime(); @@ -64,8 +61,6 @@ public class Main { // 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/StoryOutputText.java b/src/main/java/de/ocame/ffdiscordbot/OutputContentElement.java similarity index 74% rename from src/main/java/de/ocame/ffdiscordbot/StoryOutputText.java rename to src/main/java/de/ocame/ffdiscordbot/OutputContentElement.java index 8b0ebd7..376bc8f 100644 --- a/src/main/java/de/ocame/ffdiscordbot/StoryOutputText.java +++ b/src/main/java/de/ocame/ffdiscordbot/OutputContentElement.java @@ -2,23 +2,23 @@ package de.ocame.ffdiscordbot; import java.awt.*; -public class StoryOutputText { +public class OutputContentElement { private final String message; private final String image; private final Color color; - public StoryOutputText(String message) { + public OutputContentElement(String message) { this.message = message; this.image = null; this.color = new Color(36, 153, 245); } - public StoryOutputText(String message, String image) { + public OutputContentElement(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) { + public OutputContentElement(String message, String image, Color color) { this.message = message; this.image = image; this.color = color; diff --git a/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java b/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java index c7853ab..d428055 100644 --- a/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java +++ b/src/main/java/de/ocame/ffdiscordbot/SQLiteStorage.java @@ -123,7 +123,7 @@ public class SQLiteStorage { } - public static boolean checkStory(String ffid) { + public static boolean isStoryInvalid(String ffid) { String sql = "SELECT COUNT(*) FROM Storys WHERE ffid = ?"; @@ -132,13 +132,13 @@ public class SQLiteStorage { if (rs.next()) { int count = rs.getInt(1); - return count > 0; // Gibt true zurück, wenn die ID existiert, sonst false + return count <= 0; // Gibt true zurück, wenn die ID existiert, sonst false } } catch (SQLException e) { System.out.println(e.getMessage()); } - return false; + return true; } public static boolean checkChapter(String ffid, long number) { @@ -215,25 +215,6 @@ public class SQLiteStorage { } } - - 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; @@ -291,11 +272,13 @@ public class SQLiteStorage { System.out.println("Datenbank gesperrt, versuche erneut... (Versuch " + attempt + " von " + MAX_RETRIES + ")"); try { Thread.sleep(RETRY_WAIT_TIME_MS); - } catch (InterruptedException ie) { + } + catch (InterruptedException ie) { Thread.currentThread().interrupt(); throw new SQLException("Der Thread wurde unterbrochen.", ie); } - } else { + } + else { throw e; // Andere SQL-Fehler weiterwerfen } } @@ -328,13 +311,14 @@ public class SQLiteStorage { } return null; } - } catch (SQLException e) { + } + catch (SQLException e) { System.out.println(e.getMessage()); } return null; } - public static List getChapterTexte(DataStory ds) { + public static List getChapterTexte(DataStory ds) { String sql = """ SELECT\s CT.id, @@ -359,10 +343,10 @@ public class SQLiteStorage { try (Connection conn = DriverManager.getConnection(DB_URL)) { ResultSet rs = executeQueryWithRetry(conn, sql, ds.tagId()); - List outputList = new ArrayList<>(); + List outputList = new ArrayList<>(); - if (rs.next()) { - outputList.add(new StoryOutputText( + while (rs.next()) { + outputList.add(new OutputContentElement( rs.getString("chapterText"), rs.getString("imageUrl"), Color.decode(rs.getString("color"))) @@ -370,14 +354,15 @@ public class SQLiteStorage { } return outputList; - } catch (SQLException e) { + } + catch (SQLException e) { System.out.println(e.getMessage()); } return null; } - public static List getChapterTexte(int ctId) { + public static List getChapterTexte(int ctId) { String sql = """ SELECT\s CT.id, @@ -394,10 +379,10 @@ public class SQLiteStorage { try (Connection conn = DriverManager.getConnection(DB_URL)) { ResultSet rs = executeQueryWithRetry(conn, sql, ctId); - List outputList = new ArrayList<>(); + List outputList = new ArrayList<>(); - if (rs.next()) { - outputList.add(new StoryOutputText( + while (rs.next()) { + outputList.add(new OutputContentElement( rs.getString("chapterText"), rs.getString("imageUrl"), Color.decode(rs.getString("color"))) @@ -405,14 +390,15 @@ public class SQLiteStorage { } return outputList; - } catch (SQLException e) { + } + catch (SQLException e) { System.out.println(e.getMessage()); } return null; } - public static List getStoryTexte() { + public static List getStoryTexte() { String sql = """ SELECT\s CT.id, @@ -428,10 +414,10 @@ public class SQLiteStorage { try (Connection conn = DriverManager.getConnection(DB_URL)) { ResultSet rs = executeQueryWithRetry(conn, sql); - List outputList = new ArrayList<>(); + List outputList = new ArrayList<>(); - if (rs.next()) { - outputList.add(new StoryOutputText( + while (rs.next()) { + outputList.add(new OutputContentElement( rs.getString("storyText"), rs.getString("imageUrl"), Color.decode(rs.getString("color"))) @@ -439,7 +425,8 @@ public class SQLiteStorage { } return outputList; - } catch (SQLException e) { + } + catch (SQLException e) { System.out.println(e.getMessage()); } @@ -447,11 +434,4 @@ public class SQLiteStorage { } } -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 index d16a4a2..a915461 100644 --- a/src/main/java/de/ocame/ffdiscordbot/SocketServerRunnable.java +++ b/src/main/java/de/ocame/ffdiscordbot/SocketServerRunnable.java @@ -37,68 +37,85 @@ class SocketServerRunnable implements Runnable { 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; + switch (action) { + case "channelCheck" -> { + if (channelCheckAction(jsonData, out)) + 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); + case "check" -> checkAction(out); + case "chapterTest" -> chapterTest(jsonData, out); + case "addFavo" -> addFavo(jsonData); } - } catch (IOException | NullPointerException e) { + } + catch (IOException | NullPointerException e) { System.out.println("Fehler bei der Kommunikation mit dem Client: " + e.getMessage()); + e.printStackTrace(); } } - } catch (IOException e) { + } + catch (IOException e) { System.out.println("Server konnte nicht gestartet werden: " + e.getMessage()); } } + private Boolean channelCheckAction(JSONObject jsonData, PrintWriter out) { + JSONObject jsonResponse= new JSONObject(); + Long channelId = null; + + Object channelIdObj = jsonData.get("channelId"); + if(channelIdObj.equals("")) { + jsonResponse.put("status", "fail"); + return true; + } + + 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"); + return true; + } + + 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); + return false; + } + private void checkAction(PrintWriter out) { + ffr.checkAlerts(); + out.println( new JSONObject().put("status", "success")); + } + private void chapterTest(JSONObject jsonData, PrintWriter out) { + String storyName = jsonData.getString("storyName"); + String chapterName = jsonData.getString("chapterName"); + String author = jsonData.getString("author"); + int id = jsonData.getInt("id"); + Long target = jsonData.getLong("target"); + ffr.outputCheapterTester(id, chapterName, storyName, author, target); + out.println( new JSONObject().put("status", "success")); + } + + private void addFavo(JSONObject jsonData) { + String ffId = jsonData.getString("ffId"); + ffr.addStoryToFavorit(ffId); + } } \ No newline at end of file diff --git a/src/main/java/de/ocame/ffdiscordbot/HtmlFetcher.java b/src/main/java/de/ocame/ffdiscordbot/fetcher/DefaultHtmlFetcher.java similarity index 82% rename from src/main/java/de/ocame/ffdiscordbot/HtmlFetcher.java rename to src/main/java/de/ocame/ffdiscordbot/fetcher/DefaultHtmlFetcher.java index 81f45a8..eb50d24 100644 --- a/src/main/java/de/ocame/ffdiscordbot/HtmlFetcher.java +++ b/src/main/java/de/ocame/ffdiscordbot/fetcher/DefaultHtmlFetcher.java @@ -1,17 +1,12 @@ -package de.ocame.ffdiscordbot; +package de.ocame.ffdiscordbot.fetcher; import java.io.IOException; import java.io.FileReader; import java.io.FileWriter; -import java.net.URI; +import java.net.*; 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; @@ -21,16 +16,13 @@ import com.google.gson.reflect.TypeToken; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import java.net.http.HttpRequest.BodyPublishers; - - -public class HtmlFetcher { +public class DefaultHtmlFetcher implements HtmlFetcher { private final HttpClient client; private final CookieManager cookieManager; private static final String COOKIE_FILE = "cookies.json"; - public HtmlFetcher() { + public DefaultHtmlFetcher() { cookieManager = new CookieManager(); cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); client = HttpClient.newBuilder() @@ -42,10 +34,12 @@ public class HtmlFetcher { loadCookies(); } - public Document fetchURL(String url) throws IOException, InterruptedException, URISyntaxException { - return fetchURL(url, ""); + @Override + public Document fetchHtml(String url) throws IOException, InterruptedException { + return fetchHtml(url, null); } - public Document fetchURL(String url, String postData) throws IOException, InterruptedException, URISyntaxException { + + public Document fetchHtml(String url, String postData) throws IOException, InterruptedException { 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") @@ -60,19 +54,12 @@ public class HtmlFetcher { // Wenn postData nicht leer ist, füge die POST-Daten hinzu requestBuilder .header("Content-Type", "application/x-www-form-urlencoded") - .POST(BodyPublishers.ofString(postData)); + .POST(HttpRequest.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(); @@ -108,6 +95,17 @@ public class HtmlFetcher { } } + 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 + } + private static class SerializableCookie { private String uri; private String name; @@ -139,19 +137,4 @@ public class HtmlFetcher { 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/fetcher/HtmlFetcher.java b/src/main/java/de/ocame/ffdiscordbot/fetcher/HtmlFetcher.java new file mode 100644 index 0000000..18db788 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/fetcher/HtmlFetcher.java @@ -0,0 +1,9 @@ +package de.ocame.ffdiscordbot.fetcher; + +import org.jsoup.nodes.Document; +import java.io.IOException; + + +public interface HtmlFetcher { + Document fetchHtml(String url) throws IOException, InterruptedException; +} diff --git a/src/main/java/de/ocame/ffdiscordbot/output/BaseNotifier.java b/src/main/java/de/ocame/ffdiscordbot/output/BaseNotifier.java new file mode 100644 index 0000000..94b516e --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/output/BaseNotifier.java @@ -0,0 +1,132 @@ +package de.ocame.ffdiscordbot.output; + +import de.ocame.ffdiscordbot.DataStory; +import de.ocame.ffdiscordbot.OutputContentElement; +import de.ocame.ffdiscordbot.SQLiteStorage; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Random; + +// Basisklasse für Benachrichtigungen +public abstract class BaseNotifier > { + protected final JDA jda; + private Integer version; + + public void setStory(DataStory story) { + this.story = story; + } + + private DataStory story; + private Long targetChannel; + + public BaseNotifier(JDA jda) { + this.jda = jda; + } + + // Allgemeine Benachrichtigungsmethode + public void sendOutput(Long targetChannel) { + this.targetChannel = targetChannel; + sendOutput(); + } + + public void sendOutput() { + EmbedBuilder outputBox = createEmbed(); + Long[] channelIds = SQLiteStorage.getChannelIds(); + if(targetChannel != null) { + Objects.requireNonNull(jda.getTextChannelById(targetChannel)) + // .sendMessage("Hier ist ein Text vor dem Embed <@&423156046115242005> :") + // .setEmbeds(outputBox.build()) + .sendMessageEmbeds(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) { + // + } + } + } + } + + // Abstrakte Methode, um den spezifischen Titel zu liefern + protected abstract String getEmbedTitle(); + + // Abstrakte Methode zur Lieferung der Platzhalter und deren Ersetzungen + protected abstract Map getMessagePlaceholders(); + + // Abstrakte Methode zur Lieferung des Links (falls erforderlich) + protected abstract String getLink(); + + // Methode zum Erstellen des Embeds, die Platzhalter verwendet + protected EmbedBuilder createEmbed() { + EmbedBuilder outputBox = new EmbedBuilder(); + OutputContentElement outputContentElement = getOCE(); + + // Titel und Link von der spezifischen Implementierung holen + outputBox.setTitle(getEmbedTitle(), getLink()) + .setColor(outputContentElement.getColor()); + + // Nachricht holen und Platzhalter ersetzen + String message = outputContentElement.getMessage(); + for (Map.Entry entry : getMessagePlaceholders().entrySet()) { + message = message.replace(entry.getKey(), entry.getValue()); + } + outputBox.setDescription(message); + + // Zeitformatierung und Footer + Instant instant = Instant.ofEpochSecond(getDate()); + 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)); + + // Thumbnail setzen, falls vorhanden + if (outputContentElement.getImage() != null && !Objects.equals(outputContentElement.getImage(), "")) { + outputBox.setThumbnail(outputContentElement.getImage()); + } + + return outputBox; + } + + // Methode zur Auswahl eines OutputContentElement, kann auch einheitlich gehalten werden + protected OutputContentElement getOCE() { + List chapterTexte; + if(version != null) { + chapterTexte = SQLiteStorage.getChapterTexte(version); + } + else { + chapterTexte = SQLiteStorage.getChapterTexte(story); + } + + assert chapterTexte != null; + if(chapterTexte.isEmpty()) + return null; + + Random random = new Random(); + + return chapterTexte.get(random.nextInt(chapterTexte.size())); + } + + // Abstrakte Methode zur Rückgabe des Zeitstempels + protected abstract long getDate(); + + @SuppressWarnings("unchecked") + public T setVersion(Integer version) { + this.version = version; + return (T) this; + } +} diff --git a/src/main/java/de/ocame/ffdiscordbot/output/ChapterNotifier.java b/src/main/java/de/ocame/ffdiscordbot/output/ChapterNotifier.java new file mode 100644 index 0000000..e1ff08a --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/output/ChapterNotifier.java @@ -0,0 +1,49 @@ +package de.ocame.ffdiscordbot.output; + +import net.dv8tion.jda.api.JDA; +import de.ocame.ffdiscordbot.DataChapter; +import de.ocame.ffdiscordbot.DataStory; + + +import java.util.Map; + + +// Handles notifications about new chapters +public class ChapterNotifier extends BaseNotifier { + + private DataChapter chapter; + private DataStory story; + + public ChapterNotifier(JDA jda, DataChapter chapter, DataStory story) { + super(jda); + this.chapter = chapter; + this.story = story; + super.setStory(story); + } + + + @Override + protected String getEmbedTitle() { + return "Neues Kapitel:"; + } + + @Override + protected Map getMessagePlaceholders() { + return Map.of( + "%chapterName%", "**" + chapter.name() + "**", + "%storyName%", "**" + story.name() + "**", + "%autor%", "**" + story.author() + "**" + ); + } + + @Override + protected String getLink() { + return chapter.link(); + } + + @Override + protected long getDate() { + return chapter.date(); + } + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/output/InfoNotifier.java b/src/main/java/de/ocame/ffdiscordbot/output/InfoNotifier.java new file mode 100644 index 0000000..832906a --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/output/InfoNotifier.java @@ -0,0 +1,54 @@ +package de.ocame.ffdiscordbot.output; + +import de.ocame.ffdiscordbot.ConfigLoader; +import de.ocame.ffdiscordbot.OutputContentElement; +import net.dv8tion.jda.api.JDA; + +import java.time.Instant; +import java.util.Map; +import java.util.Properties; + +public class InfoNotifier extends BaseNotifier { + + private String messagetext; + + public InfoNotifier(JDA jda) { + super(jda); + this.sendOutput(new ConfigLoader().getDebugChannel() ); + } + + + + @Override + protected String getEmbedTitle() { + return "Problem:"; + } + + @Override + protected Map getMessagePlaceholders() { + return Map.of( + "%storyName%", "" + ); + } + + @Override + protected String getLink() { + return ""; + } + + @Override + protected long getDate() { + return Instant.now().getEpochSecond(); + } + + @Override + protected OutputContentElement getOCE() { + return new OutputContentElement("Fehler " + messagetext); + } + + public InfoNotifier setMessagetext(String messagetext) { + this.messagetext = messagetext; + return this; + } + +} diff --git a/src/main/java/de/ocame/ffdiscordbot/output/StoryNotifier.java b/src/main/java/de/ocame/ffdiscordbot/output/StoryNotifier.java new file mode 100644 index 0000000..135b307 --- /dev/null +++ b/src/main/java/de/ocame/ffdiscordbot/output/StoryNotifier.java @@ -0,0 +1,62 @@ +package de.ocame.ffdiscordbot.output; + +import de.ocame.ffdiscordbot.OutputContentElement; +import de.ocame.ffdiscordbot.SQLiteStorage; +import net.dv8tion.jda.api.JDA; +import de.ocame.ffdiscordbot.DataStory; + + +import java.util.List; +import java.util.Map; +import java.util.Random; + +// Handles notifications about new stories +public class StoryNotifier extends BaseNotifier { + + private final DataStory story; + + public StoryNotifier(JDA jda, DataStory story) { + super(jda); + this.story = story; + } + + + @Override + protected String getEmbedTitle() { + return "Neue Geschichte:"; + } + + @Override + protected Map getMessagePlaceholders() { + return Map.of( + "%storyName%", "**" + story.name() + "**", + "%autor%", "**" + story.author() + "**" + ); + } + + @Override + protected String getLink() { + return story.link(); + } + + @Override + protected long getDate() { + return story.date(); + } + + // Methode zur Auswahl eines OutputContentElement, kann auch einheitlich gehalten werden + @Override + protected OutputContentElement getOCE() { + List storyTexte; + storyTexte = SQLiteStorage.getStoryTexte(); + + assert storyTexte != null; + if(storyTexte.isEmpty()) + return null; + + Random random = new Random(); + return storyTexte.get(random.nextInt(storyTexte.size())); + } + + +}