Add Twitter integration

Add Job Listening Integration
Various Refactorings
This commit is contained in:
Wieland Schöbl
2021-05-28 22:09:32 +02:00
parent fa00466eb0
commit f23a4d9ce5
12 changed files with 271 additions and 113 deletions

124
.idea/uiDesigner.xml generated Normal file
View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

50
.idea/workspace.xml generated
View File

@@ -4,12 +4,23 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="1aabf22b-2f57-46ac-9973-367d8668ffd3" name="Default Changelist" comment="Update stuff"> <list default="true" id="1aabf22b-2f57-46ac-9973-367d8668ffd3" name="Default Changelist" comment="Add shards count configuration">
<change afterPath="$PROJECT_DIR$/.idea/uiDesigner.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/Parser.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/AdminCli.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/AdminCli.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DataIO.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DataIO.kt" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DataIO.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DataIO.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Main.kt" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Main.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/extensions/Jsoup.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/extensions/Jsoup.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/model/BlogPostPreview.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/model/BlogPostPreview.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/model/JobListingPreview.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/model/JobListingPreview.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/BlogPostParser.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/JobListingParser.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/SiteWatcher.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/SiteWatcher.kt" afterDir="false" />
</list>
<list id="44283a45-f406-407f-bce2-a31bb9bfc0cc" name="Changes by Valentin" comment="">
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Channels.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Channels.kt" afterDir="false" />
</list> </list>
<list id="44283a45-f406-407f-bce2-a31bb9bfc0cc" name="Changes by Valentin" comment="" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -43,10 +54,10 @@
<component name="FileTemplateManagerImpl"> <component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
<option value="Class" />
<option value="Kotlin Class" /> <option value="Kotlin Class" />
<option value="Kotlin Object" /> <option value="Kotlin Object" />
<option value="Kotlin File" /> <option value="Kotlin File" />
<option value="Class" />
</list> </list>
</option> </option>
</component> </component>
@@ -58,6 +69,14 @@
</option> </option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="HighlightingSettingsPerFile"> <component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/build.gradle" root0="SKIP_INSPECTION" /> <setting file="file://$PROJECT_DIR$/build.gradle" root0="SKIP_INSPECTION" />
</component> </component>
@@ -78,14 +97,18 @@
<property name="aspect.path.notification.shown" value="true" /> <property name="aspect.path.notification.shown" value="true" />
<property name="codeWithMe.voiceChat.enabledByDefault" value="false" /> <property name="codeWithMe.voiceChat.enabledByDefault" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" /> <property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="project.structure.last.edited" value="Project" /> <property name="project.structure.last.edited" value="Project" />
<property name="project.structure.proportion" value="0.15" /> <property name="project.structure.proportion" value="0.15" />
<property name="project.structure.side.proportion" value="0.2" /> <property name="project.structure.side.proportion" value="0.2" />
<property name="settings.editor.selected.configurable" value="preferences.pluginManager" /> <property name="settings.editor.selected.configurable" value="preferences.pluginManager" />
</component> </component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY"> <key name="CreateClassDialog.RecentsKey">
<recent name="de.wulkanat" /> <recent name="org.hmcore" />
</key> </key>
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\build\libs" /> <recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\build\libs" />
@@ -95,6 +118,9 @@
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot" /> <recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot" />
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\src\main\kotlin\de\wulkanat" /> <recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\src\main\kotlin\de\wulkanat" />
</key> </key>
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY">
<recent name="de.wulkanat" />
</key>
</component> </component>
<component name="RunManager" selected="Kotlin.Main"> <component name="RunManager" selected="Kotlin.Main">
<configuration default="true" type="ArquillianJUnit" factoryName="" nameIsGenerated="true"> <configuration default="true" type="ArquillianJUnit" factoryName="" nameIsGenerated="true">
@@ -203,7 +229,7 @@
<option name="number" value="Default" /> <option name="number" value="Default" />
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1597322033373</updated> <updated>1597322033373</updated>
<workItem from="1622225780094" duration="598000" /> <workItem from="1622225780094" duration="6687000" />
</task> </task>
<task id="LOCAL-00001" summary="Add auto publish feature"> <task id="LOCAL-00001" summary="Add auto publish feature">
<created>1597437833375</created> <created>1597437833375</created>
@@ -254,7 +280,14 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1622224992757</updated> <updated>1622224992757</updated>
</task> </task>
<option name="localTasksCounter" value="8" /> <task id="LOCAL-00008" summary="Add shards count configuration">
<created>1622226400158</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1622226400158</updated>
</task>
<option name="localTasksCounter" value="9" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -280,7 +313,8 @@
<MESSAGE value="prepare twitter integration" /> <MESSAGE value="prepare twitter integration" />
<MESSAGE value="Update Json Serialization" /> <MESSAGE value="Update Json Serialization" />
<MESSAGE value="Update stuff" /> <MESSAGE value="Update stuff" />
<option name="LAST_COMMIT_MESSAGE" value="Update stuff" /> <MESSAGE value="Add shards count configuration" />
<option name="LAST_COMMIT_MESSAGE" value="Add shards count configuration" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<watches-manager> <watches-manager>

View File

@@ -1,8 +1,7 @@
package de.wulkanat package de.wulkanat
import de.wulkanat.model.BlogPostPreview import de.wulkanat.web.fakeUpdateBlogPost
import net.dv8tion.jda.api.hooks.ListenerAdapter import net.dv8tion.jda.api.hooks.ListenerAdapter
import de.wulkanat.web.SiteWatcher
import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent
import org.hmcore.TwitterJob import org.hmcore.TwitterJob
@@ -24,14 +23,9 @@ class AdminCli : ListenerAdapter() {
when (command[0].value) { when (command[0].value) {
"stop" -> exitProcess(1) "stop" -> exitProcess(1)
"fakeUpdate" -> { "fakeUpdate" -> {
SiteWatcher.newestBlog = BlogPostPreview( // TODO: implement fake update for blog posts
title = "FakePost", // BLOG_POST_WATCHER.current = setOf()
imgUrl = "", fakeUpdateBlogPost()
fullPostUrl = "",
author = "wulkanat",
date = "now",
description = "Lorem Ipsum"
)
TwitterJob.lastTweetID = "poggers" TwitterJob.lastTweetID = "poggers"

View File

@@ -2,7 +2,11 @@
package de.wulkanat package de.wulkanat
import de.wulkanat.extensions.ensureExists import de.wulkanat.extensions.ensureExists
import de.wulkanat.model.BlogPostPreview
import de.wulkanat.model.JobListingPreview
import kotlinx.serialization.Required
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
@@ -28,23 +32,32 @@ data class CustomMessage(
@Serializable @Serializable
data class AdminFile( data class AdminFile(
val adminId: Long = 12345, @Required val adminId: Long = 12345,
val token: String = "12345", @Required val token: String = "12345",
val updateMs: Long = 30000, @Required val updateMs: Long = 30000,
val shards: Int = 6, @Required val shards: Int = 6,
val watchingMessage: String = "for new Blogposts", @Required val watchingMessage: String = "for new Blogposts",
val offlineMessage: String = "CONNECTION FAILED", @Required val offlineMessage: String = "CONNECTION FAILED",
var twitterApi: TwitterApi? = TwitterApi() @Required var twitterApi: TwitterApi? = TwitterApi()
) )
@Serializable @Serializable
data class TwitterApi( data class TwitterApi(
val accessToken: String = "accessTokenHere", @Required val accessToken: String = "accessTokenHere",
val accessTokenSecret: String = "accessTokenSecretHere", @Required val accessTokenSecret: String = "accessTokenSecretHere",
val apiKey: String = "apiKeyHere", @Required val apiKey: String = "apiKeyHere",
val apiKeySecret: String = "Api Key secret here" @Required val apiKeySecret: String = "Api Key secret here"
) )
@Serializable
data class Webhooks(
@Required val blogPostsWebhookUrl: String = "https://...",
@Required val jobListingsWebhookUrl: String = "https://...",
)
val WEBHOOKS_FILE = File("webhooks.json").ensureExists(Json.encodeToString(Webhooks()))
val WEBHOOKS = Json.decodeFromString<Webhooks>(WEBHOOKS_FILE.readText())
val SERVERS_FILE = File("servers.json").ensureExists(Json.encodeToString(listOf<DiscordChannel>())) val SERVERS_FILE = File("servers.json").ensureExists(Json.encodeToString(listOf<DiscordChannel>()))
val SERVICE_CHANNELS_FILE = val SERVICE_CHANNELS_FILE =
File("service_channels.json").ensureExists(Json.encodeToString(listOf<ServiceChannel>())) File("service_channels.json").ensureExists(Json.encodeToString(listOf<ServiceChannel>()))

View File

@@ -1,6 +1,6 @@
package de.wulkanat package de.wulkanat
import de.wulkanat.web.SiteWatcher import de.wulkanat.web.getNewBlogPosts
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.MessageBuilder import net.dv8tion.jda.api.MessageBuilder
@@ -59,8 +59,8 @@ object Main {
}) })
timer("Updater", daemon = true, initialDelay = 0L, period = Admin.updateMs) { timer("Updater", daemon = true, initialDelay = 0L, period = Admin.updateMs) {
if (SiteWatcher.hasNewBlogPost()) { getNewBlogPosts()?.forEach {
Channels.sentToAll(MessageBuilder().setEmbed(SiteWatcher.newestBlog!!.toMessageEmbed()).build()) Channels.sentToAll(MessageBuilder().setEmbed(it.toMessageEmbed()).build())
} }
} }

View File

@@ -7,4 +7,5 @@ operator fun Element.get(className: String): Elements =
this.getElementsByClass(className) this.getElementsByClass(className)
val Elements.text get() = text().trim() val Elements.text get() = text().trim()
val Element.absUrl get(): String = child(0).absUrl("href") val Element.absUrl get(): String = child(0).absUrl("href")
val Element.imgSrc get(): String = child(0).attr("src")

View File

@@ -3,7 +3,9 @@ package de.wulkanat.model
import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.entities.MessageEmbed import net.dv8tion.jda.api.entities.MessageEmbed
import de.wulkanat.extensions.hex2Rgb import de.wulkanat.extensions.hex2Rgb
import kotlinx.serialization.Serializable
@Serializable
data class BlogPostPreview( data class BlogPostPreview(
val title: String, val title: String,
val description: String, val description: String,

View File

@@ -1,10 +1,12 @@
package de.wulkanat.model package de.wulkanat.model
import de.wulkanat.extensions.hex2Rgb import de.wulkanat.extensions.hex2Rgb
import kotlinx.serialization.Serializable
import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.entities.MessageEmbed import net.dv8tion.jda.api.entities.MessageEmbed
class JobListingPreview( @Serializable
data class JobListingPreview(
val title: String, val title: String,
val department: String, val department: String,
val location: String, val location: String,

View File

@@ -1,24 +0,0 @@
package de.wulkanat.web
import de.wulkanat.model.BlogPostPreview
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
object BlogPostParser {
fun getFistBlog(doc: Document): BlogPostPreview {
val posts = doc.getElementsByClass("postWrapper")
return parseBlog(posts.first())
}
private fun parseBlog(elm: Element): BlogPostPreview {
return BlogPostPreview(
title = elm.getElementsByClass("post__details__heading").first().text(),
imgUrl = elm.getElementsByClass("post__image__frame").first().child(0).attr("src"),
fullPostUrl = elm.child(0).absUrl("href"),
date = elm.getElementsByClass("post__details__meta__date").first().text(),
author = elm.getElementsByClass("post__details__meta__author").first().text(),
description = elm.getElementsByClass("post__details__body").first().text()
)
}
}

View File

@@ -1,21 +0,0 @@
package de.wulkanat.web
import de.wulkanat.extensions.get
import de.wulkanat.extensions.text
import de.wulkanat.extensions.absUrl
import de.wulkanat.model.JobListingPreview
import org.jsoup.nodes.Document
fun parseJobListings(doc: Document) =
doc["current-jobs__departments"].flatMap { jobDepartment ->
val jobDepartmentName = jobDepartment["current-jobs__department-name"].text
jobDepartment["current-jobs__job"].map { job ->
JobListingPreview(
title = job["current-jobs__job-title"].text,
department = jobDepartmentName,
location = job["current-jobs__job-location"].text,
fullListingUrl = job.absUrl
)
}
}

View File

@@ -0,0 +1,40 @@
package de.wulkanat.web
import de.wulkanat.extensions.absUrl
import de.wulkanat.extensions.get
import de.wulkanat.extensions.imgSrc
import de.wulkanat.extensions.text
import de.wulkanat.model.BlogPostPreview
import de.wulkanat.model.JobListingPreview
private const val BLOG_POST_STATE_FILE_NAME = "blog_state.json"
fun fakeUpdateBlogPost() = removeFirstFromSiteSave<BlogPostPreview>(BLOG_POST_STATE_FILE_NAME)
fun getNewBlogPosts() = updateSite("https://hytale.com/news", BLOG_POST_STATE_FILE_NAME) { doc ->
doc["postWrapper"].map {
BlogPostPreview(
title = it["post__details__heading"].text,
imgUrl = it["post__image__frame"].first().imgSrc,
fullPostUrl = it.absUrl,
date = it["post__details__meta__date"].text,
author = it["post__details__meta__author"].text,
description = it["post__details__body"].text,
)
}
}
private const val JOB_LISTING_STATE_FILE_NAME = "jobs_state.json"
fun fakeUpdateJobListings() = removeFirstFromSiteSave<JobListingPreview>(JOB_LISTING_STATE_FILE_NAME)
fun getNewJobListings() = updateSite("https://hypixelstudios.com/jobs/", JOB_LISTING_STATE_FILE_NAME) { doc ->
doc["current-jobs__departments"].flatMap { jobDepartment ->
val jobDepartmentName = jobDepartment["current-jobs__department-name"].text
jobDepartment["current-jobs__job"].map { job ->
JobListingPreview(
title = job["current-jobs__job-title"].text,
department = jobDepartmentName,
location = job["current-jobs__job-location"].text,
fullListingUrl = job.absUrl
)
}
}
}

View File

@@ -1,44 +1,37 @@
package de.wulkanat.web package de.wulkanat.web
import de.wulkanat.Admin import kotlinx.serialization.decodeFromString
import de.wulkanat.DiscordRpc import kotlinx.serialization.encodeToString
import de.wulkanat.model.BlogPostPreview import kotlinx.serialization.json.Json
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.io.File
import java.io.IOException import java.io.IOException
object SiteWatcher { /**
private const val BLOG_INDEX_URL = "https://www.hytale.com/news" * Removes the first element of a saved JSON list file
var newestBlog: BlogPostPreview? = null */
private var siteOnline = false inline fun <reified T>
removeFirstFromSiteSave(fileName: String) = File(fileName).takeIf { it.exists() }?.let {
it.writeText(Json.encodeToString(Json.decodeFromString<List<T>>(it.readText()).toMutableList().apply { removeFirst() }))
}
fun hasNewBlogPost(): Boolean { inline fun <reified T> updateSite(url: String, fileName: String, parser: (Document) -> List<T>) = try {
try { val currentStateFile = File(fileName)
val doc = Jsoup.connect(BLOG_INDEX_URL).get()
val newBlog = BlogPostParser.getFistBlog(doc)
if (newestBlog == newBlog) { val retrievedElements = parser(Jsoup.connect(url).get())
return false var currentElements = if (currentStateFile.exists())
} Json.decodeFromString(currentStateFile.readText()) else retrievedElements
if (newestBlog == null) { val newElements = retrievedElements - currentElements
newestBlog = newBlog currentElements = retrievedElements
return false currentStateFile.writeText(Json.encodeToString(currentElements))
} else {
newestBlog = newBlog
}
} catch (e: IOException) {
Admin.error("Connection to Hytale Server failed", e.message ?: e.localizedMessage)
siteOnline = false
DiscordRpc.updatePresence(siteOnline)
return false newElements
} } catch (e: IOException) {
// TODO: put this somewhere else
// Admin.error("""Fetching "$url" failed!""", e.message ?: e.localizedMessage)
// DiscordRpc.updatePresence(canUpdate.also { canUpdate = false })
if (!siteOnline) { null
siteOnline = true }
DiscordRpc.updatePresence(siteOnline)
}
return true
}
}