Compare commits
22 Commits
d78c4ec9fd
...
feat/laval
Author | SHA1 | Date | |
---|---|---|---|
740166f79f
|
|||
ef0c5f5d29
|
|||
b1431a165a
|
|||
bf17ae800c
|
|||
9aae9cd77b
|
|||
ca52d8f04c | |||
6dc1446143
|
|||
eb18df79c9
|
|||
e1f20aeeaf
|
|||
a2e96c7df9
|
|||
be62aad19c
|
|||
d6d37f391a
|
|||
9e80e06d18
|
|||
de46e7625a
|
|||
614ca976b9
|
|||
2b3423c1c9
|
|||
3473657fbb | |||
373a6b4279 | |||
942719d19c | |||
fd7fa1e078 | |||
5532dc1769 | |||
879ee55ac4 |
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
ENVIRONMENT=Production
|
||||
TOKEN=<YOUR BOT TOKEN>
|
17
.gitea/workflows/github-mirror.yml
Normal file
17
.gitea/workflows/github-mirror.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: github-mirror
|
||||
on: push
|
||||
jobs:
|
||||
push-github:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Push to GitHub
|
||||
uses: pixta-dev/repository-mirroring-action@v1
|
||||
with:
|
||||
target_repo_url:
|
||||
git@github.com:EkiciLP/TomatenMusic3.git
|
||||
ssh_private_key:
|
||||
${{ secrets.MIRROR_SSH_PRIVATE_KEY }}
|
47
.gitea/workflows/gradle-build.yml
Normal file
47
.gitea/workflows/gradle-build.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
Gradle-Build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '23'
|
||||
check-latest: true
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
add-job-summary: always
|
||||
cache-cleanup: on-success
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Build
|
||||
run: chmod +x gradlew; ./gradlew assemble
|
||||
|
||||
- name: Move artifacts
|
||||
run: mkdir artifacts; mv app/build/libs/*.jar artifacts;
|
||||
|
||||
- name: Upload artifact
|
||||
uses: christopherhx/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: artifacts
|
||||
path: artifacts
|
53
.gitea/workflows/gradle-test.yml
Normal file
53
.gitea/workflows/gradle-test.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
Gradle-Test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '23'
|
||||
check-latest: true
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
add-job-summary: always
|
||||
cache-cleanup: on-success
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
DISCORD_TEST_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
run: chmod +x gradlew; ./gradlew test
|
||||
|
||||
- name: Move Test Results
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
run: |
|
||||
mkdir test-results/;
|
||||
[ -d app/build/test-results/test/ ] && mv app/build/test-results/test/*.xml test-results/ || true
|
||||
|
||||
- name: Upload Test Result
|
||||
uses: christopherhx/gitea-upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,3 +3,6 @@
|
||||
|
||||
# Ignore Gradle build output directory
|
||||
build
|
||||
.vscode
|
||||
.env
|
||||
lavanodes.json
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# TomatenMusic3
|
||||
|
||||
A simple Discord Music Bot written in Java with Discord4J
|
||||
A simple Discord Music Bot written in Java with Javacord
|
@@ -8,11 +8,14 @@
|
||||
plugins {
|
||||
// Apply the application plugin to add support for building a CLI application in Java.
|
||||
application
|
||||
id("com.gradleup.shadow") version "8.3.3"
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
maven("https://maven.lavalink.dev/releases")
|
||||
maven("https://git.tomatentum.net/api/packages/tueem/maven")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -22,13 +25,24 @@ dependencies {
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
// This dependency is used by the application.
|
||||
implementation(libs.guava)
|
||||
implementation(libs.javacord)
|
||||
implementation(libs.dotenv)
|
||||
implementation(libs.slf4j)
|
||||
implementation(libs.logback)
|
||||
implementation(libs.log4jtoslf4j)
|
||||
implementation(libs.jultoslf4j)
|
||||
implementation(libs.jackson)
|
||||
implementation(libs.lavalink)
|
||||
|
||||
implementation(libs.marinaralib)
|
||||
implementation(libs.marinarajavacord)
|
||||
|
||||
}
|
||||
|
||||
// Apply a specific Java toolchain to ease working on different environments.
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
languageVersion = JavaLanguageVersion.of(23)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +51,13 @@ application {
|
||||
mainClass = "net.tomatentum.tomatenmusic3.App"
|
||||
}
|
||||
|
||||
|
||||
tasks.withType<Jar> {
|
||||
manifest {
|
||||
attributes["Main-Class"] = application.mainClass
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<Test>("test") {
|
||||
// Use JUnit Platform for unit tests.
|
||||
useJUnitPlatform()
|
||||
|
@@ -1,10 +1,58 @@
|
||||
/*
|
||||
* This source file was generated by the Gradle 'init' task
|
||||
*/
|
||||
package net.tomatentum.tomatenmusic3;
|
||||
|
||||
import org.javacord.api.DiscordApi;
|
||||
import org.javacord.api.DiscordApiBuilder;
|
||||
import org.javacord.api.entity.intent.Intent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import net.tomatentum.marinara.Marinara;
|
||||
import net.tomatentum.marinara.wrapper.javacord.JavacordWrapper;
|
||||
import net.tomatentum.tomatenmusic3.command.PingCommand;
|
||||
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new App().connect();
|
||||
}
|
||||
|
||||
private JsonFactory jsonFactory;
|
||||
private Config config;
|
||||
private DiscordApi client;
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Marinara marinara;
|
||||
|
||||
private App() {
|
||||
this.jsonFactory = JsonFactory.builder().build();
|
||||
Dotenv env = Dotenv.configure().ignoreIfMissing().load();
|
||||
this.config = new Config(env);
|
||||
|
||||
LoggerContext loggerctx = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
if (config.isDevelopment())
|
||||
loggerctx.getLogger("root").setLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
client = new DiscordApiBuilder()
|
||||
.setToken(config.token())
|
||||
.addIntents(Intent.GUILD_VOICE_STATES)
|
||||
.login().join();
|
||||
initMarinara();
|
||||
logger.info("connected as {}", client.getYourself().getName());
|
||||
}
|
||||
|
||||
private void initMarinara() {
|
||||
this.marinara = Marinara.load(new JavacordWrapper(client));
|
||||
|
||||
marinara.getRegistry().addInteractions(new PingCommand());
|
||||
|
||||
marinara.getRegistry().registerCommands();
|
||||
}
|
||||
|
||||
}
|
||||
|
20
app/src/main/java/net/tomatentum/tomatenmusic3/Config.java
Normal file
20
app/src/main/java/net/tomatentum/tomatenmusic3/Config.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.tomatentum.tomatenmusic3;
|
||||
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
|
||||
public class Config {
|
||||
|
||||
private Dotenv env;
|
||||
|
||||
public Config(Dotenv env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public String token() {
|
||||
return env.get("TOKEN");
|
||||
}
|
||||
|
||||
public boolean isDevelopment() {
|
||||
return env.get("ENVIRONMENT").equals("Development");
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package net.tomatentum.tomatenmusic3.command;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.javacord.api.entity.message.MessageFlag;
|
||||
import org.javacord.api.interaction.SlashCommandInteraction;
|
||||
|
||||
import net.tomatentum.marinara.interaction.InteractionHandler;
|
||||
import net.tomatentum.marinara.interaction.commands.annotation.SlashCommand;
|
||||
|
||||
public class PingCommand implements InteractionHandler {
|
||||
|
||||
@SlashCommand(
|
||||
name = "ping",
|
||||
description = "Tests bot's connection."
|
||||
)
|
||||
public void execPing(SlashCommandInteraction interaction) {
|
||||
Duration ping = Duration.between(interaction.getCreationTimestamp(), Instant.now());
|
||||
interaction.createImmediateResponder()
|
||||
.append("Pong! " + ping.toMillis() + "ms")
|
||||
.setFlags(MessageFlag.EPHEMERAL)
|
||||
.respond().join();
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package net.tomatentum.tomatenmusic3.lavalink;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
import dev.arbjerg.lavalink.client.loadbalancing.IRegionFilter;
|
||||
import dev.arbjerg.lavalink.client.loadbalancing.RegionGroup;
|
||||
|
||||
public class IRegionFilterDeserializer extends StdDeserializer<IRegionFilter> {
|
||||
|
||||
public static ObjectMapper register(ObjectMapper mapper) {
|
||||
SimpleModule module =
|
||||
new SimpleModule("IRegionFilterDeserializer", new Version(1, 0, 0, null, null, null));
|
||||
module.addDeserializer(IRegionFilter.class, new IRegionFilterDeserializer());
|
||||
mapper.registerModule(module);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public IRegionFilterDeserializer() {
|
||||
super(IRegionFilter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRegionFilter deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JacksonException {
|
||||
String regionString = parser.getValueAsString();
|
||||
return RegionGroup.INSTANCE.valueOf(regionString);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package net.tomatentum.tomatenmusic3.lavalink;
|
||||
|
||||
import dev.arbjerg.lavalink.client.NodeOptions;
|
||||
import dev.arbjerg.lavalink.client.loadbalancing.IRegionFilter;
|
||||
|
||||
public record LavalinkNodeOptions(String name, String host, int port, String password, IRegionFilter regionGroup) {
|
||||
|
||||
public NodeOptions toNodeOptions() {
|
||||
return new NodeOptions.Builder()
|
||||
.setName(name)
|
||||
.setServerUri("ws://{}:{}".formatted(host, port))
|
||||
.setPassword(password)
|
||||
.setRegionFilter(regionGroup)
|
||||
.build();
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package net.tomatentum.tomatenmusic3.lavalink;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.javacord.api.DiscordApi;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import dev.arbjerg.lavalink.client.Helpers;
|
||||
import dev.arbjerg.lavalink.client.LavalinkClient;
|
||||
import net.tomatentum.tomatenmusic3.Config;
|
||||
|
||||
public class LavalinkWrapper {
|
||||
|
||||
private static final String NODES_FILE_NAME = "lavanodes.json";
|
||||
private static final String NODES_INFO_FORMAT = "Node {} info:\n\tURI: {}\n\tRegion: {}";
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private DiscordApi client;
|
||||
private JsonFactory jsonFactory;
|
||||
|
||||
private LavalinkClient lavaClient;
|
||||
|
||||
public LavalinkWrapper(Config config, DiscordApi client, JsonFactory jsonFactory) {
|
||||
this.client = client;
|
||||
this.jsonFactory = jsonFactory;
|
||||
|
||||
this.lavaClient = new LavalinkClient(Helpers.getUserIdFromToken(config.token()));
|
||||
getNodes().forEach(node -> {
|
||||
lavaClient.addNode(node.toNodeOptions());
|
||||
logger.info("Registered node {}", node.name());
|
||||
logger.debug(NODES_INFO_FORMAT, node.name(), node.toNodeOptions().getServerUri(), node.regionGroup());
|
||||
});
|
||||
}
|
||||
|
||||
protected Set<LavalinkNodeOptions> getNodes() {
|
||||
File nodesFile = new File(
|
||||
new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile()).getParent(),
|
||||
NODES_FILE_NAME);
|
||||
ObjectMapper objectMapper = new ObjectMapper(jsonFactory);
|
||||
IRegionFilterDeserializer.register(objectMapper);
|
||||
|
||||
try {
|
||||
List<LavalinkNodeOptions> nodes = objectMapper.readValue(nodesFile, new TypeReference<List<LavalinkNodeOptions>>(){});
|
||||
return new HashSet<>(nodes);
|
||||
} catch (IOException e) {
|
||||
logger.error("lavanodes.json seems to not exist.", e);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
public LavalinkClient client() {
|
||||
return this.lavaClient;
|
||||
}
|
||||
|
||||
public DiscordApi discordClient() {
|
||||
return this.client;
|
||||
}
|
||||
}
|
11
app/src/main/resources/logback.xml
Normal file
11
app/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{STRICT} %-20.-20t %-46([%boldWhite(%logger{30})]) %highlight(%p): %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
16
build.gradle.kts
Normal file
16
build.gradle.kts
Normal file
@@ -0,0 +1,16 @@
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
allprojects {
|
||||
group = "net.tomatentum.tomatenmusic3"
|
||||
version = "1.0.0-RC1" + (if (!project.hasProperty("release")) ("-" + getGitHash()) else "")
|
||||
description = "A simple Discord Music Bot written in Java with Javacord"
|
||||
}
|
||||
|
||||
fun getGitHash(): String {
|
||||
val output = ByteArrayOutputStream()
|
||||
project.exec {
|
||||
commandLine("git", "rev-parse", "--short", "HEAD")
|
||||
standardOutput = output
|
||||
}
|
||||
return output.toString().trim()
|
||||
}
|
@@ -2,9 +2,27 @@
|
||||
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
||||
|
||||
[versions]
|
||||
guava = "33.0.0-jre"
|
||||
junit-jupiter = "5.10.2"
|
||||
javacord = "3.8.0"
|
||||
dotenv = "3.0.0"
|
||||
logback = "1.5.15"
|
||||
slf4j = "2.0.16"
|
||||
log4jtoslf4j = "2.24.3"
|
||||
jultoslf4j = "2.0.16"
|
||||
jackson = "2.18.2"
|
||||
marinara = "1.0.0-RC1-9d88ca9"
|
||||
lavalink = "3.1.0"
|
||||
|
||||
[libraries]
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
|
||||
javacord = { module = "org.javacord:javacord", version.ref = "javacord" }
|
||||
dotenv = { module = "io.github.cdimascio:dotenv-java", version.ref = "dotenv"}
|
||||
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback"}
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j"}
|
||||
log4jtoslf4j = { module = "org.apache.logging.log4j:log4j-to-slf4j", version.ref = "log4jtoslf4j"}
|
||||
jultoslf4j = { module = "org.slf4j:jul-to-slf4j", version.ref = "jultoslf4j"}
|
||||
jackson = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson"}
|
||||
lavalink = { module = "dev.arbjerg:lavalink-client", version.ref = "lavalink"}
|
||||
|
||||
marinaralib = { module = "net.tomatentum.Marinara:lib-dev", version.ref = "marinara"}
|
||||
marinarajavacord = { module = "net.tomatentum.Marinara:wrapper-javacord-dev", version.ref = "marinara"}
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
9
lavanodes.example.json
Normal file
9
lavanodes.example.json
Normal file
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"name": "node1",
|
||||
"host": "localhost",
|
||||
"port": 2333,
|
||||
"password": "youshallnotpass",
|
||||
"regionGroup": "EUROPE"
|
||||
}
|
||||
]
|
Reference in New Issue
Block a user