Compare commits

...

4 Commits
main ... kotlin

Author SHA1 Message Date
Etzelia b8339e659f JDK 17 2021-06-21 17:16:07 +00:00
Etzelia 877704daf3 Kotlincord 2021-06-21 17:09:52 +00:00
Etzelia 41dbde93d9
Serialize OffsetDateTime
Signed-off-by: Etzelia <etzelia@hotmail.com>
2021-06-20 15:21:23 -05:00
Etzelia abf36d1eb2
Kotlin
Signed-off-by: Etzelia <etzelia@hotmail.com>
2021-06-20 14:56:31 -05:00
25 changed files with 318 additions and 410 deletions

View File

@ -9,7 +9,7 @@ trigger:
steps:
- name: build
pull: always
image: gradle:6.6
image: gradle:7.1-jdk16
commands:
- gradle build
- gradle test
@ -27,7 +27,7 @@ trigger:
steps:
- name: publish
pull: always
image: gradle:6.6
image: gradle:7.1-jdk16
environment:
MAVEN_PASSWORD:
from_secret: maven_password

View File

@ -1,4 +1,4 @@
# Javacord
# ~~Javacord~~ Kotlincord
A small, no-dependency Discord utility library. Currently used for webhooks/embeds.

View File

@ -1,10 +1,13 @@
plugins {
id 'java'
id 'maven-publish'
id 'org.jetbrains.kotlin.jvm' version '1.5.20-RC'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.5.10'
id 'tech.formatter-kt.formatter' version '0.7.7'
}
group = 'xyz.etztech'
version = '0.2.2'
version = '0.3.0'
sourceCompatibility = '8'
repositories {
@ -18,6 +21,8 @@ test {
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1'
}
publishing {
@ -37,3 +42,13 @@ publishing {
}
}
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

View File

@ -1,40 +0,0 @@
package xyz.etztech;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
public class Javacord {
public static String escapeFormat(String input) {
return input
.replaceAll("_", "\\\\\\\\_")
.replaceAll("\\*", "\\\\\\\\*")
.replaceAll("~", "\\\\\\\\~")
.replaceAll("\\|", "\\\\\\\\|");
}
public static String escapeQuote(String input) {
return input.replaceAll("\"", "\\\\\"");
}
public static void sendWebhook(String webhookURL, Webhook webhook) throws Exception {
URL url = new URL(webhookURL);
URLConnection con = url.openConnection();
HttpURLConnection http = (HttpURLConnection) con;
http.setRequestMethod("POST");
http.setDoOutput(true);
byte[] out = webhook.toString().getBytes(StandardCharsets.UTF_8);
int length = out.length;
http.setFixedLengthStreamingMode(length);
http.setRequestProperty("Content-Type", "application/json; utf-8");
http.setRequestProperty("User-Agent", "Javacord Agent");
try (OutputStream os = http.getOutputStream()) {
os.write(out, 0, out.length);
}
}
}

View File

@ -1,27 +0,0 @@
package xyz.etztech;
import xyz.etztech.embed.Embed;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Webhook {
private final String content;
private final List<Embed> embeds;
public Webhook(String content, Embed... embeds) {
this.content = content;
this.embeds = Arrays.asList(embeds);
}
public String toString() {
StringBuilder builder = new StringBuilder("{");
List<String> json = new ArrayList<>();
if (!"".equals(content)) json.add(String.format("\"content\":\"%s\"", Javacord.escapeQuote(content)));
if (embeds.size() > 0) json.add(String.format("\"embeds\":[%s]", embeds.stream().map(Embed::toJSON).collect(Collectors.joining(","))));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
}

View File

@ -1,31 +0,0 @@
package xyz.etztech.embed;
import xyz.etztech.Javacord;
import java.util.ArrayList;
import java.util.List;
public class Author {
private final String name;
private final String url;
private final String iconURL;
private final String proxyIconURL;
public Author(String name, String url, String iconURL, String proxyIconURL) {
this.name = name;
this.url = url;
this.iconURL = iconURL;
this.proxyIconURL = proxyIconURL;
}
public String toJSON() {
StringBuilder builder = new StringBuilder("{");
List<String> json = new ArrayList<>();
if (!"".equals(name)) json.add(String.format("\"name\":\"%s\"", Javacord.escapeQuote(name)));
if (!"".equals(url)) json.add(String.format("\"url\":\"%s\"", Javacord.escapeQuote(url)));
if (!"".equals(iconURL)) json.add(String.format("\"icon_url\":\"%s\"", Javacord.escapeQuote(iconURL)));
if (!"".equals(proxyIconURL)) json.add(String.format("\"proxy_icon_url\":\"%s\"", Javacord.escapeQuote(proxyIconURL)));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
}

View File

@ -1,125 +0,0 @@
package xyz.etztech.embed;
import xyz.etztech.Javacord;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Embed {
private String content;
private String title;
private String description;
private String url;
private int color;
private OffsetDateTime timestamp;
private Footer footer;
private Thumbnail thumbnail;
private Image image;
private Author author;
private List<Field> fields;
public Embed() {
this.content = "";
this.title = "";
this.description = "";
this.url = "";
this.color = 0;
this.timestamp = null;
this.footer = null;
this.thumbnail = null;
this.image = null;
this.author = null;
this.fields = new ArrayList<>();
}
public Embed content(String content) {
this.content = content;
return this;
}
public Embed title(String title) {
this.title = title;
return this;
}
public Embed description(String description) {
this.description = description;
return this;
}
public Embed url(String url) {
this.url = url;
return this;
}
public Embed color(int color) {
this.color = color;
return this;
}
public Embed timestamp(OffsetDateTime timestamp) {
this.timestamp = timestamp;
return this;
}
public Embed footer(Footer footer) {
this.footer = footer;
return this;
}
public Embed thumbnail(Thumbnail thumbnail) {
this.thumbnail = thumbnail;
return this;
}
public Embed image(Image image) {
this.image = image;
return this;
}
public Embed author(Author author) {
this.author = author;
return this;
}
public Embed fields(List<Field> fields) {
this.fields = fields;
return this;
}
public Embed addField(Field field) {
this.fields.add(field);
return this;
}
public String toJSON() {
StringBuilder builder = new StringBuilder("{");
List<String> json = new ArrayList<>();
if (!"".equals(title)) json.add(String.format("\"title\":\"%s\"", Javacord.escapeQuote(title)));
if (!"".equals(description)) json.add(String.format("\"description\":\"%s\"", Javacord.escapeQuote(description)));
if (!"".equals(url)) json.add(String.format("\"url\":\"%s\"", Javacord.escapeQuote(url)));
if (color != 0) json.add(String.format("\"color\":\"%d\"", color));
if (timestamp != null) json.add(String.format("\"timestamp\":\"%s\"", timestamp.format(DateTimeFormatter.ISO_INSTANT)));
if (footer != null) json.add(String.format("\"footer\":%s", footer.toJSON()));
if (thumbnail != null) json.add(String.format("\"thumbnail\":%s", thumbnail.toJSON()));
if (image != null) json.add(String.format("\"image\":%s", image.toJSON()));
if (author != null) json.add(String.format("\"author\":%s", author.toJSON()));
if (fields.size() > 0) json.add(String.format("\"fields\":[%s]", fields.stream().map(Field::toJSON).collect(Collectors.joining(","))));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
public String toString() {
StringBuilder builder = new StringBuilder("{");
List<String> json = new ArrayList<>();
if (!"".equals(content)) json.add(String.format("\"content\":\"%s\"", Javacord.escapeQuote(content)));
if (!"{}".equals(toJSON())) json.add(String.format("\"embed\":%s", toJSON()));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
}

View File

@ -1,34 +0,0 @@
package xyz.etztech.embed;
import xyz.etztech.Javacord;
import java.util.ArrayList;
import java.util.List;
public class Field {
private final String name;
private final String value;
private final boolean inline;
public Field(String name, String value) {
this.name = name;
this.value = value;
this.inline = false;
}
public Field(String name, String value, boolean inline) {
this.name = name;
this.value = value;
this.inline = inline;
}
public String toJSON() {
StringBuilder builder = new StringBuilder("{");
List<String> json = new ArrayList<>();
json.add(String.format("\"name\":\"%s\"", Javacord.escapeQuote(name)));
json.add(String.format("\"value\":\"%s\"", Javacord.escapeQuote(value)));
if (inline) json.add(String.format("\"inline\":%s", true));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
}

View File

@ -1,35 +0,0 @@
package xyz.etztech.embed;
import xyz.etztech.Javacord;
import java.util.ArrayList;
import java.util.List;
public class Footer {
private final String text;
private final String iconURL;
private final String proxyIconURL;
public Footer(String text) {
this.text = text;
this.iconURL = "";
this.proxyIconURL = "";
}
public Footer(String text, String iconURL, String proxyIconURL) {
this.text = text;
this.iconURL = iconURL;
this.proxyIconURL = proxyIconURL;
}
public String toJSON() {
StringBuilder builder = new StringBuilder("{");
builder.append(String.format("\"text\":\"%s\"", Javacord.escapeQuote(text)));
List<String> json = new ArrayList<>();
if (!"".equals(iconURL)) json.add(String.format("\"icon_url\":\"%s\"", Javacord.escapeQuote(iconURL)));
if (!"".equals(proxyIconURL)) json.add(String.format("\"proxy_icon_url\":\"%s\"", Javacord.escapeQuote(proxyIconURL)));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
}

View File

@ -1,31 +0,0 @@
package xyz.etztech.embed;
import xyz.etztech.Javacord;
import java.util.ArrayList;
import java.util.List;
public class Image {
private final String url;
private final String proxyURL;
private final int height;
private final int width;
public Image(String url, String proxyURL, int height, int width) {
this.url = url;
this.proxyURL = proxyURL;
this.height = height;
this.width = width;
}
public String toJSON() {
StringBuilder builder = new StringBuilder("{");
List<String> json = new ArrayList<>();
if (!"".equals(url)) json.add(String.format("\"url\":\"%s\"", Javacord.escapeQuote(url)));
if (!"".equals(proxyURL)) json.add(String.format("\"proxy_url\":\"%s\"", Javacord.escapeQuote(proxyURL)));
if (height != 0) json.add(String.format("\"height\":\"%d\"", height));
if (width != 0) json.add(String.format("\"width\":\"%d\"", width));
builder.append(String.join(",", json));
return builder.append("}").toString();
}
}

View File

@ -1,7 +0,0 @@
package xyz.etztech.embed;
public class Thumbnail extends Image {
public Thumbnail(String url, String proxyURL, int height, int width) {
super(url, proxyURL, height, width);
}
}

View File

@ -0,0 +1,27 @@
package xyz.etztech.javacord
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
fun escapeFormat(input: String): String {
return input.replace("_".toRegex(), "\\\\\\\\_")
.replace("\\*".toRegex(), "\\\\\\\\*")
.replace("~".toRegex(), "\\\\\\\\~")
.replace("\\|".toRegex(), "\\\\\\\\|")
}
fun escapeQuote(input: String): String {
return input.replace("\"".toRegex(), "\\\\\"")
}
fun sendWebhook(webhookURL: String, webhook: Webhook): HttpResponse<String> {
val client = HttpClient.newHttpClient()
val request =
HttpRequest.newBuilder(URI.create(webhookURL))
.POST(HttpRequest.BodyPublishers.ofString(webhook.toString()))
.headers("Content-Type", "application/json; utf-8", "User-Agent", "Javacord Agent")
.build()
return client.send(request, HttpResponse.BodyHandlers.ofString())
}

View File

@ -0,0 +1,13 @@
package xyz.etztech.javacord
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import xyz.etztech.javacord.embed.Embed
@Serializable
class Webhook(private val content: String, vararg val embeds: Embed) {
override fun toString(): String {
return Json.encodeToString(this)
}
}

View File

@ -0,0 +1,11 @@
package xyz.etztech.javacord.embed
import kotlinx.serialization.Serializable
@Serializable
class Author(
private val name: String,
private val url: String,
private val iconURL: String,
private val proxyIconURL: String
)

View File

@ -0,0 +1,101 @@
package xyz.etztech.javacord.embed
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encodeToString
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
@Serializable
class Embed {
private var content: String? = null
private var title: String? = null
private var description: String? = null
private var url: String? = null
private var color: Int? = null
@Serializable(with = OffsetDateTimeSerializer::class)
private var timestamp: OffsetDateTime? = null
private var footer: Footer? = null
private var thumbnail: Thumbnail? = null
private var image: Image? = null
private var author: Author? = null
private var fields: MutableList<Field>? = mutableListOf()
fun content(content: String): Embed {
this.content = content
return this
}
fun title(title: String): Embed {
this.title = title
return this
}
fun description(description: String): Embed {
this.description = description
return this
}
fun url(url: String): Embed {
this.url = url
return this
}
fun color(color: Int): Embed {
this.color = color
return this
}
fun timestamp(timestamp: OffsetDateTime?): Embed {
this.timestamp = timestamp
return this
}
fun footer(footer: Footer?): Embed {
this.footer = footer
return this
}
fun thumbnail(thumbnail: Thumbnail?): Embed {
this.thumbnail = thumbnail
return this
}
fun image(image: Image?): Embed {
this.image = image
return this
}
fun author(author: Author?): Embed {
this.author = author
return this
}
fun fields(fields: MutableList<Field>): Embed {
this.fields = fields
return this
}
fun addField(field: Field): Embed {
fields?.add(field)
return this
}
override fun toString(): String {
return Json.encodeToString(this)
}
}
object OffsetDateTimeSerializer : KSerializer<OffsetDateTime> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("OffsetDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: OffsetDateTime) =
encoder.encodeString(value.format(DateTimeFormatter.ISO_INSTANT))
override fun deserialize(decoder: Decoder): OffsetDateTime =
OffsetDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_INSTANT)
}

View File

@ -0,0 +1,22 @@
package xyz.etztech.javacord.embed
import kotlinx.serialization.Serializable
@Serializable
class Field {
private val name: String
private val value: String
private val inline: Boolean
constructor(name: String, value: String) {
this.name = name
this.value = value
inline = false
}
constructor(name: String, value: String, inline: Boolean) {
this.name = name
this.value = value
this.inline = inline
}
}

View File

@ -0,0 +1,22 @@
package xyz.etztech.javacord.embed
import kotlinx.serialization.Serializable
@Serializable
class Footer {
private val text: String
private val iconURL: String
private val proxyIconURL: String
constructor(text: String) {
this.text = text
iconURL = ""
proxyIconURL = ""
}
constructor(text: String, iconURL: String, proxyIconURL: String) {
this.text = text
this.iconURL = iconURL
this.proxyIconURL = proxyIconURL
}
}

View File

@ -0,0 +1,11 @@
package xyz.etztech.javacord.embed
import kotlinx.serialization.Serializable
@Serializable
class Image(
private val url: String,
private val proxyURL: String,
private val height: Int,
private val width: Int
)

View File

@ -0,0 +1,11 @@
package xyz.etztech.javacord.embed
import kotlinx.serialization.Serializable
@Serializable
class Thumbnail(
private val url: String,
private val proxyURL: String,
private val height: Int,
private val width: Int
)

View File

@ -1,28 +0,0 @@
import xyz.etztech.Javacord;
import xyz.etztech.embed.Author;
import xyz.etztech.embed.Embed;
import xyz.etztech.Webhook;
import xyz.etztech.embed.Field;
import java.time.OffsetDateTime;
public class Test {
public static void main(String[] args) throws Exception {
Embed embed = new Embed()
.color(3306460)
.timestamp(OffsetDateTime.now())
.title("Etzelia found some \"diamond\" ore")
.author(new Author("Etzelia", "", "https://minotar.net/helm/Etzelia/100.png", ""));
embed.description(Javacord.escapeFormat("*Test* **Test** _Test_ __Test__ ~Test~ ~~Test~~ |Test| ||Test||"));
embed.addField(new Field("Title", "Description"));
embed.addField(new Field("Title2", "Description2"));
Webhook webhook = new Webhook("@here", embed);
System.out.println(webhook);
String url = System.getenv("JAVACORD_WEBHOOK");
if (!"".equals(url)) {
Javacord.sendWebhook(url, webhook);
}
}
}

View File

@ -1,23 +0,0 @@
package xyz.etztech;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class JavacordTest {
@Test
void TestEscapeFormat() {
String raw = "*Test* **Test** _Test_ __Test__ ~Test~ ~~Test~~ |Test| ||Test||";
String escaped = Javacord.escapeFormat(raw);
String expected = "\\\\*Test\\\\* \\\\*\\\\*Test\\\\*\\\\* \\\\_Test\\\\_ \\\\_\\\\_Test\\\\_\\\\_ \\\\~Test\\\\~ \\\\~\\\\~Test\\\\~\\\\~ \\\\|Test\\\\| \\\\|\\\\|Test\\\\|\\\\|";
Assertions.assertEquals(expected, escaped);
}
@Test
void TestEscapeQuote() {
String raw = "\"Test\"";
String escaped = Javacord.escapeQuote(raw);
String expected = "\\\"Test\\\"";
Assertions.assertEquals(expected, escaped);
}
}

View File

@ -1,25 +0,0 @@
package xyz.etztech.embed;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
public class EmbedTest {
@Test
void TestEmbed() {
Embed embed = new Embed()
.color(0x151515)
.author(new Author("Etzelia", "https://etzel.ia", "" ,""))
.title("Test \"Title\"")
.description("Test Description")
.fields(Arrays.asList(
new Field("Field1", "foo"),
new Field("Field2", "bar"),
new Field("Field3", "baz", true)
));
String expected = "{\"embed\":{\"title\":\"Test \\\"Title\\\"\",\"description\":\"Test Description\",\"color\":\"1381653\",\"author\":{\"name\":\"Etzelia\",\"url\":\"https://etzel.ia\"},\"fields\":[{\"name\":\"Field1\",\"value\":\"foo\"},{\"name\":\"Field2\",\"value\":\"bar\"},{\"name\":\"Field3\",\"value\":\"baz\",\"inline\":true}]}}";
Assertions.assertEquals(expected, embed.toString());
}
}

View File

@ -0,0 +1,24 @@
package xyz.etztech.javacord
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
class JavacordTest {
@Test
fun testEscapeFormat() {
val raw = "*Test* **Test** _Test_ __Test__ ~Test~ ~~Test~~ |Test| ||Test||"
val escaped = escapeFormat(raw)
val expected =
"\\\\*Test\\\\* \\\\*\\\\*Test\\\\*\\\\* \\\\_Test\\\\_ \\\\_\\\\_Test\\\\_\\\\_ \\\\" +
"~Test\\\\~ \\\\~\\\\~Test\\\\~\\\\~ \\\\|Test\\\\| \\\\|\\\\|Test\\\\|\\\\|"
Assertions.assertEquals(expected, escaped)
}
@Test
fun testEscapeQuote() {
val raw = "\"Test\""
val escaped = escapeQuote(raw)
val expected = "\\\"Test\\\""
Assertions.assertEquals(expected, escaped)
}
}

View File

@ -0,0 +1,25 @@
package xyz.etztech.javacord
import java.time.OffsetDateTime
import xyz.etztech.javacord.embed.Author
import xyz.etztech.javacord.embed.Embed
import xyz.etztech.javacord.embed.Field
fun main() {
val embed =
Embed().color(3306460)
.timestamp(OffsetDateTime.now())
.title("Etzelia found some \"diamond\" ore")
.author(Author("Etzelia", "", "https://minotar.net/helm/Etzelia/100.png", ""))
embed.description(
escapeFormat("*Test* **Test** _Test_ __Test__ ~Test~ ~~Test~~ |Test| ||Test||")
)
embed.addField(Field("Title", "Description"))
embed.addField(Field("Title2", "Description2"))
val webhook = Webhook("@here", embed)
println(webhook)
val url = System.getenv("JAVACORD_WEBHOOK")
if ("" != url) {
sendWebhook(url, webhook)
}
}

View File

@ -0,0 +1,32 @@
package xyz.etztech.javacord.embed
import java.time.OffsetDateTime
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
class EmbedTest {
@Test
fun testEmbed() {
val embed =
Embed().color(0x151515)
.author(Author("Etzelia", "https://etzel.ia", "", ""))
.title("Test \"Title\"")
.description("Test Description")
.fields(
mutableListOf(
Field("Field1", "foo"),
Field("Field2", "bar"),
Field("Field3", "baz", true)
)
)
.timestamp(OffsetDateTime.MIN)
val expected =
"{\"title\":\"Test \\\"Title\\\"\",\"description\":\"Test Description\",\"color\"" +
":1381653,\"timestamp\":\"-1000000000-12-31T06:00:00Z\",\"author\":{\"name\":\"" +
"Etzelia\",\"url\":\"https://etzel.ia\",\"iconURL" +
"\":\"\",\"proxyIconURL\":\"\"},\"fields\":[{\"name\":\"Field1\",\"value\":\"foo" +
"\",\"inline\":false},{\"name\":\"Field2\",\"value\":\"bar\",\"inline\":false},{" +
"\"name\":\"Field3\",\"value\":\"baz\",\"inline\":true}]}"
Assertions.assertEquals(expected, embed.toString())
}
}