add fat jar compile mode

This commit is contained in:
Wieland Schöbl
2020-08-14 01:50:04 +02:00
parent a9e5aceb96
commit d6a3691756
14 changed files with 224 additions and 47 deletions

View File

@@ -0,0 +1,124 @@
package de.wulkanat
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.entities.User
import java.awt.Color
object Admin {
val userId: Long
val token: String
val updateMs: Long
init {
val admin = Json(JsonConfiguration.Stable).parse(AdminFile.serializer(), ADMIN_FILE.readText())
userId = admin.adminId
token = admin.token
updateMs = admin.updateMs
}
var jda: JDA? = null
set(value) {
field = value
admin = value?.retrieveUserById(userId)?.complete()
if (admin == null) {
kotlin.io.println("Connection to de.wulkanat.Admin failed!")
} else {
kotlin.io.println("Connected to ${admin!!.name}. No further errors will be printed here.")
}
}
private var admin: User? = null
fun println(msg: String) {
sendDevMessage(
EmbedBuilder()
.setTitle(msg)
.setColor(Color.WHITE)
.build(),
msg
)
}
fun printlnBlocking(msg: String) {
senDevMessageBlocking(
EmbedBuilder()
.setTitle(msg)
.setColor(Color.WHITE)
.build(),
msg
)
}
fun error(msg: String, error: Exception) {
sendDevMessage(
EmbedBuilder()
.setTitle(msg)
.setDescription(error.message)
.setColor(Color.RED)
.build()
, "$msg\n\n${error.message}"
)
}
fun errorBlocking(msg: String, error: Exception) {
senDevMessageBlocking(
EmbedBuilder()
.setTitle(msg)
.setDescription(error.message)
.setColor(Color.RED)
.build()
, "$msg\n\n${error.message}"
)
}
fun warning(msg: String) {
sendDevMessage(
EmbedBuilder()
.setTitle(msg)
.setColor(Color.YELLOW)
.build(),
msg
)
}
fun ready() {
sendDevMessage(
EmbedBuilder()
.setTitle("Now watching for new Hytale Blogposts every ${updateMs / 1000}s")
.setDescription(Channels.getServerNames().joinToString("\n"))
.setColor(Color.GREEN)
.build(),
"Now watching for new Hytale BlogPosts"
)
}
fun silent(msg: String) {
kotlin.io.println(msg)
}
private fun senDevMessageBlocking(messageEmbed: MessageEmbed, fallback: String) {
val devChannel = admin?.openPrivateChannel() ?: kotlin.run {
kotlin.io.println(fallback)
return
}
devChannel.queue {
it.sendMessage(messageEmbed).complete()
}
}
private fun sendDevMessage(messageEmbed: MessageEmbed, fallback: String) {
val devChannel = admin?.openPrivateChannel() ?: kotlin.run {
kotlin.io.println(fallback)
return
}
devChannel.queue {
it.sendMessage(messageEmbed).queue()
}
}
}

View File

@@ -0,0 +1,21 @@
package de.wulkanat
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
class Bot : ListenerAdapter() {
override fun onMessageReceived(event: MessageReceivedEvent) {
val message = event.message
if (message.contentRaw == "!ping") {
val channel = message.channel
val time = System.currentTimeMillis()
channel.sendMessage("Pong!")
.queue {
it.editMessageFormat("Pong: %d ms", System.currentTimeMillis() - time)
.queue()
}
}
}
}

View File

@@ -0,0 +1,93 @@
package de.wulkanat
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.entities.TextChannel
object Channels {
var jda: JDA? = null
val json = Json(JsonConfiguration.Stable)
/**
* List of (ServerID, ChannelID)
*/
val channels: MutableList<DiscordChannel> =
json.parse(DiscordChannel.serializer().list, SERVERS_FILE.readText()).toMutableList()
fun sentToAll(messageEmbed: MessageEmbed) {
if (jda == null)
return
for (channel_pair in channels) {
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
if (channel_pair.mentionedRole != null) {
val message = if (channel_pair.mentionedRole == "everyone") {
"New Blogpost @everyone"
} else {
"New Blogpost <@&${channel_pair.mentionedRole}>"
}
channel.sendMessage(message).queue()
}
channel.sendMessage(messageEmbed).queue()
}
}
fun checkEveryonePermission() {
for (channel_pair in channels) {
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
if (channel_pair.mentionedRole == "everyone" &&
channel.guild.selfMember.hasPermission(Permission.MESSAGE_MENTION_EVERYONE)
) {
Admin.warning("Cannot mention everyone on ${channel.guild.name}")
} else if (channel.guild.selfMember.hasPermission(Permission.MESSAGE_WRITE)) {
Admin.warning("Cannot send any messages on ${channel.guild.name}")
}
}
}
fun getServerNames(): List<String> {
if (jda == null)
return listOf()
return channels.map {
val channel = jda!!.getTextChannelById(it.id)
if (channel == null) {
Admin.warning("Channel ${it.id} is no longer active!")
return@map "**${it.id}** *(inactive)*"
}
val role = if (it.mentionedRole == null) {
""
} else if (it.mentionedRole == "everyone") {
" @everyone"
} else {
" @${channel.guild.getRoleById(it.mentionedRole)?.name}"
}
"**${channel.guild.name}**\n#${channel.name}${role}"
}
}
fun testServerId(id: Long): TextChannel? {
return jda?.getTextChannelById(id)
}
fun addChannel(id: Long, role: String?) {
channels.add(DiscordChannel(id, role))
saveChannels()
}
private fun saveChannels() {
SERVERS_FILE.writeText(
json.stringify(
DiscordChannel.serializer().list,
channels
))
}
}

View File

@@ -0,0 +1,59 @@
package de.wulkanat
import de.wulkanat.model.BlogPostPreview
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import de.wulkanat.web.SiteWatcher
import kotlin.system.exitProcess
class Cli : ListenerAdapter() {
override fun onMessageReceived(event: MessageReceivedEvent) {
val msg = event.message.contentRaw
if (event.author.idLong != Admin.userId ||
!msg.startsWith("%!")
) {
return
}
val command = msg.removePrefix("%!").split(" ")
try {
when (command[0]) {
"stop" -> exitProcess(1)
"fakeUpdate" -> {
SiteWatcher.newestBlog = BlogPostPreview(
title = "FakePost",
imgUrl = "",
fullPostUrl = "",
author = "wulkanat",
date = "now",
description = "Lorem Ipsum"
)
Admin.println("Posting on next update cycle.")
}
"addChannel" -> {
val channel = command[1].toLong()
var role: String? = null
if (command.size == 3) {
role = command[2]
}
val serverChannel = Channels.testServerId(channel)
val roleName = serverChannel?.guild?.getRoleById(role ?: "")
if (serverChannel != null) {
if (roleName != null || role == null || role == "everyone") {
Channels.addChannel(channel, role)
Admin.println("Added server '${serverChannel.name}' for role '${roleName ?: role}'")
} else {
Admin.warning("Unknown Role ID")
}
} else {
Admin.warning("Unknown Channel ID")
}
}
}
} catch (e: ArrayIndexOutOfBoundsException) {
// noop
}
}
}

View File

@@ -0,0 +1,20 @@
package de.wulkanat
import kotlinx.serialization.Serializable
import java.io.File
@Serializable
data class DiscordChannel(
val id: Long,
val mentionedRole: String? = null
)
@Serializable
data class AdminFile(
val adminId: Long,
val token: String,
val updateMs: Long
)
val SERVERS_FILE = File("servers.json")
val ADMIN_FILE = File("admin.json")

View File

@@ -0,0 +1,37 @@
package de.wulkanat
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.entities.Activity
import net.dv8tion.jda.api.requests.GatewayIntent
import de.wulkanat.web.SiteWatcher
import kotlin.concurrent.timer
fun main() {
// TODO: move toke into file
val builder = JDABuilder.createLight(
Admin.token,
GatewayIntent.GUILD_MESSAGES, GatewayIntent.DIRECT_MESSAGES)
.addEventListeners(Bot())
.setActivity(Activity.watching("for new Blogposts"))
.build()
builder.addEventListener(Cli())
builder.awaitReady()
Channels.jda = builder
Admin.jda = builder
Admin.ready()
Runtime.getRuntime().addShutdownHook(object : Thread() {
override fun run() {
println("Shutting down...")
Admin.printlnBlocking("Shutting down")
}
})
timer("Updater", daemon = true, initialDelay = 0L, period = Admin.updateMs) {
if (SiteWatcher.hasNewBlogPost()) {
Channels.sentToAll(SiteWatcher.newestBlog!!.toMessageEmbed())
}
}
}

View File

@@ -0,0 +1,11 @@
package de.wulkanat.extensions
import java.awt.Color
fun hex2Rgb(colorStr: String): Color {
return Color(
Integer.valueOf(colorStr.substring(1, 3), 16),
Integer.valueOf(colorStr.substring(3, 5), 16),
Integer.valueOf(colorStr.substring(5, 7), 16)
)
}

View File

@@ -0,0 +1,25 @@
package de.wulkanat.model
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.entities.MessageEmbed
import de.wulkanat.extensions.hex2Rgb
data class BlogPostPreview(
val title: String,
val description: String,
val date: String,
val author: String,
val imgUrl: String,
val fullPostUrl: String
) {
fun toMessageEmbed(): MessageEmbed {
return EmbedBuilder()
.setTitle(this.title, this.fullPostUrl)
.setDescription(this.description)
.setAuthor(this.author)
.setThumbnail(this.imgUrl)
.setFooter(this.date, "https://www.hytale.com/static/images/logo-h.png")
.setColor(hex2Rgb("#337fb0"))
.build()
}
}

View File

@@ -0,0 +1,24 @@
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

@@ -0,0 +1,37 @@
package de.wulkanat.web
import de.wulkanat.Admin
import de.wulkanat.model.BlogPostPreview
import org.jsoup.Jsoup
import java.io.IOException
object SiteWatcher {
private const val BLOG_INDEX_URL = "https://www.hytale.com/news"
var newestBlog: BlogPostPreview? = null
fun hasNewBlogPost(): Boolean {
Admin.silent("Updating...")
try {
val doc = Jsoup.connect(BLOG_INDEX_URL).get()
val newBlog = BlogPostParser.getFistBlog(doc)
if (newestBlog == newBlog) {
return false
}
if (newestBlog == null) {
newestBlog = newBlog
return false
} else {
newestBlog = newBlog
}
} catch (e: IOException) {
Admin.error("Connection to Hytale Server failed", e)
return false
}
return true
}
}