mirror of
https://github.com/sigmasternchen/MineTroll
synced 2025-03-14 23:58:55 +00:00
initial commit
This commit is contained in:
commit
ff1d26f585
3 changed files with 332 additions and 0 deletions
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
83
pom.xml
Normal file
83
pom.xml
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>xyz.xn--4xa</groupId>
|
||||
<artifactId>MineTroll</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>paper-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dv8tion</groupId>
|
||||
<artifactId>JDA</artifactId>
|
||||
<version>5.0.1</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>club.minnced</groupId>
|
||||
<artifactId>opus-java</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.theokanning.openai-gpt3-java</groupId>
|
||||
<artifactId>service</artifactId>
|
||||
<version>0.18.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.theokanning.openai-gpt3-java</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>0.18.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.theokanning.openai-gpt3-java</groupId>
|
||||
<artifactId>client</artifactId>
|
||||
<version>0.18.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
214
src/main/java/xyz/xn__4xa/MineTroll.java
Normal file
214
src/main/java/xyz/xn__4xa/MineTroll.java
Normal file
|
@ -0,0 +1,214 @@
|
|||
package xyz.xn__4xa;
|
||||
|
||||
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
|
||||
import com.theokanning.openai.completion.chat.ChatCompletionResult;
|
||||
import com.theokanning.openai.completion.chat.ChatMessage;
|
||||
import com.theokanning.openai.completion.chat.ChatMessageRole;
|
||||
import com.theokanning.openai.service.OpenAiService;
|
||||
import com.theokanning.openai.utils.TikTokensUtil;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.utils.concurrent.Task;
|
||||
import net.kyori.adventure.text.TranslatableComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MineTroll extends JavaPlugin implements Listener {
|
||||
static private final String DISCORD_BOT_TOKEN_PATH = "discord.token";
|
||||
static private final String DISCORD_GUILD_ID_PATH = "discord.guild";
|
||||
static private final String OPENAI_TOKEN_PATH = "openai.token";
|
||||
|
||||
static private final String SYSTEM_PROMPT = """
|
||||
Please come up with a somewhat mean-spirited comment regarding a player's death in Minecraft. The comment should be short. It should be in German. Do not use a formal tone - be casual, use colloquial language.
|
||||
You can refer to the player name by using this placeholder: {}
|
||||
|
||||
Examples:
|
||||
|
||||
Hey {}, du weißt schon dass man hin und wieder auch Luft holen sollte?
|
||||
|
||||
lol, was für ein noob
|
||||
|
||||
Das war beeindruckened! Im negativen Sinne.
|
||||
|
||||
War das Absicht oder einfach nur Inkompetenz?
|
||||
|
||||
Versuch's doch mal mit einer anderen Strategie... Oder überhaupt einer.
|
||||
|
||||
Ha! {} ist gegen 'ne Wand geflogen - so ein Anfänger.
|
||||
|
||||
Na {}? Ist der Drache so einfach zu besiegen wie du dachtest?
|
||||
|
||||
Ich frage mich ja, ob {} das wohl absichtlich macht...
|
||||
|
||||
Da war 'ne Diamant-Spitzhacke dabei, oder? Das tut weh.
|
||||
|
||||
Ups. ^^
|
||||
""";
|
||||
|
||||
static private final String USER_PROMPT = """
|
||||
Death Message: %s
|
||||
Levels lost: %d
|
||||
Inventory lost: %s
|
||||
""";
|
||||
|
||||
final static private String MESSAGE_FORMAT =
|
||||
"[<aqua>Discord</aqua> | %rolecolor%%role%<reset>] %name% » %message%";
|
||||
final static private String PLACEHOLDER_ROLE = "%role%";
|
||||
final static private String PLACEHOLDER_ROLE_COLOR = "%rolecolor%";
|
||||
final static private String PLACEHOLDER_MESSAGE = "%message%";
|
||||
final static private String PLACEHOLDER_NAME = "%name%";
|
||||
|
||||
private JDA discord;
|
||||
private OpenAiService openai;
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
FileConfiguration config = getConfig();
|
||||
config.addDefault(DISCORD_BOT_TOKEN_PATH, "Discord token");
|
||||
config.addDefault(DISCORD_GUILD_ID_PATH, "Discord guild id");
|
||||
config.addDefault(OPENAI_TOKEN_PATH, "OpenAI token");
|
||||
config.options().copyDefaults(true);
|
||||
saveConfig();
|
||||
|
||||
|
||||
discord = JDABuilder.createLight(
|
||||
config.getString(DISCORD_BOT_TOKEN_PATH, ""),
|
||||
GatewayIntent.GUILD_MEMBERS
|
||||
).build();
|
||||
|
||||
openai = new OpenAiService(config.getString(OPENAI_TOKEN_PATH, ""));
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
private Member getRandomMember() {
|
||||
List<Member> members = Optional.ofNullable(
|
||||
discord.getGuildById(getConfig().getString(DISCORD_GUILD_ID_PATH, ""))
|
||||
)
|
||||
.map(Guild::loadMembers)
|
||||
.map(Task::get)
|
||||
.orElse(List.of())
|
||||
.stream()
|
||||
.filter(m -> !m.getUser().isBot())
|
||||
.toList();
|
||||
return members.get(random.nextInt(members.size()));
|
||||
}
|
||||
|
||||
private Role getRole(Member member) {
|
||||
List<Role> roles = member.getRoles();
|
||||
|
||||
if (roles.isEmpty()) {
|
||||
return member.getGuild().getPublicRole();
|
||||
}
|
||||
|
||||
return roles.stream().max(Comparator.comparing(Role::getPosition)).get();
|
||||
}
|
||||
|
||||
private String getMessage(PlayerDeathEvent event) {
|
||||
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
|
||||
.builder()
|
||||
.model(TikTokensUtil.ModelEnum.GPT_3_5_TURBO.getName())
|
||||
.messages(List.of(
|
||||
new ChatMessage(ChatMessageRole.SYSTEM.value(), SYSTEM_PROMPT),
|
||||
new ChatMessage(ChatMessageRole.USER.value(), USER_PROMPT.formatted(
|
||||
event.deathMessage() instanceof TranslatableComponent component
|
||||
? component.key()
|
||||
: "unknown",
|
||||
event.getPlayer().getLevel(),
|
||||
event.getDrops()
|
||||
.stream()
|
||||
.map(i -> i.getAmount() + "x " + i.getType().name().toLowerCase())
|
||||
.collect(Collectors.joining(", "))
|
||||
))
|
||||
))
|
||||
.build();
|
||||
|
||||
ChatCompletionResult result = openai.createChatCompletion(chatCompletionRequest);
|
||||
|
||||
return result
|
||||
.getChoices().get(0)
|
||||
.getMessage().getContent()
|
||||
.replaceAll("[{][^}]*[}]", event.getPlayer().getName());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDie(PlayerDeathEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(this, () -> {
|
||||
Member member = getRandomMember();
|
||||
String message = getMessage(event);
|
||||
Role role = getRole(member);
|
||||
|
||||
String richMessage = MESSAGE_FORMAT
|
||||
.replaceAll(PLACEHOLDER_ROLE, role.getName())
|
||||
.replaceAll(PLACEHOLDER_ROLE_COLOR,
|
||||
"<" + getClosestColor(Objects.requireNonNullElse(role.getColor(), Color.DARK_GRAY)) + ">")
|
||||
.replaceAll(PLACEHOLDER_NAME, member.getEffectiveName())
|
||||
.replaceAll(PLACEHOLDER_MESSAGE, message);
|
||||
|
||||
getLogger().info(event.getPlayer().getName() + ": " + member.getEffectiveName() + ": " + message);
|
||||
|
||||
event.getPlayer().sendRichMessage(richMessage);
|
||||
}, 20 * 5);
|
||||
}
|
||||
|
||||
private String getClosestColor(Color color) {
|
||||
return Arrays.stream(MinecraftColor.values())
|
||||
.map(c -> new ColorTuple(c, (
|
||||
Math.abs(c.r - color.getRed()) +
|
||||
Math.abs(c.g - color.getGreen()) +
|
||||
Math.abs(c.b - color.getBlue())
|
||||
))
|
||||
).min(Comparator.comparing(t -> t.difference))
|
||||
.map(ColorTuple::color)
|
||||
.orElse(MinecraftColor.BLACK)
|
||||
.name;
|
||||
}
|
||||
|
||||
private record ColorTuple(MinecraftColor color, int difference) {}
|
||||
|
||||
private enum MinecraftColor {
|
||||
BLACK("black", 0, 0, 0),
|
||||
DARKBLUE("dark_blue", 0, 0, 170),
|
||||
DARKGREEN("dark_green", 0, 170, 0),
|
||||
DARKAQUA("dark_aqua", 0, 170, 170),
|
||||
DARKRED("dark_red", 170, 0, 0),
|
||||
DARKPURPLE("dark_purple", 170, 0, 170),
|
||||
GOLD("gold", 255, 170, 0),
|
||||
GREY("gray", 170, 170, 170),
|
||||
DARKGREY("dark_gray", 85, 85, 85),
|
||||
BLUE("blue", 85, 85, 255),
|
||||
GREEN("green", 85, 255, 85),
|
||||
AQUA("aqua", 85, 255, 255),
|
||||
RED("red", 255, 85, 85),
|
||||
LIGHTPURPLE("light_purple", 255, 85, 255),
|
||||
YELLOW("yellow", 255, 255, 85),
|
||||
WHITE("white", 255, 255, 255),
|
||||
MINECOIN_GOLD("minecoin_gold", 221, 214, 5),
|
||||
;
|
||||
|
||||
final String name;
|
||||
final int r, g, b;
|
||||
|
||||
MinecraftColor(String name, int r, int g, int b) {
|
||||
this.name = name;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue