Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 5 additions & 25 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
plugins {
kotlin("jvm") version "2.2.21"
id("fabric-loom") version "1.11.5"
id("com.gradleup.shadow") version "9.0.2"
}

version = project.property("mod_version") as String
Expand All @@ -24,6 +22,7 @@ loom {
repositories {
mavenCentral()
maven("https://maven.isxander.dev/releases")
maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1")
}

dependencies {
Expand All @@ -33,12 +32,11 @@ dependencies {
modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}")

modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}")
modImplementation(
"net.fabricmc:fabric-language-kotlin:${project.property("fabric_kotlin_version")}")

shadow(implementation("org.mongodb:mongodb-driver-kotlin-sync:5.5.1")!!)
shadow(implementation("org.mongodb:bson-kotlinx:5.5.1")!!)
shadow(implementation("net.dv8tion:JDA:5.6.1") { exclude("opus-java") })
include(implementation("org.mongodb:mongodb-driver-sync:5.5.1")!!)
include(implementation("net.dv8tion:JDA:5.6.1") { exclude("opus-java") })

modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:1.2.2")
}

tasks.processResources {
Expand All @@ -58,22 +56,4 @@ tasks.processResources {

tasks.withType<JavaCompile> { options.encoding = "UTF-8" }

tasks {
shadowJar {
from(sourceSets["main"].output)
from(sourceSets["client"].output)
configurations = listOf(project.configurations.shadow.get())
archiveClassifier = "shadowed-only"
// minimize()
}
remapJar {
dependsOn(shadowJar)
mustRunAfter(shadowJar)
inputFile = file(shadowJar.get().archiveFile)
archiveClassifier = ""
}
}

java { toolchain.languageVersion = JavaLanguageVersion.of(21) }

kotlin { jvmToolchain(21) }
5 changes: 2 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.configuration-cache=true
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.10
loader_version=0.17.2
# Mod Properties
mod_version=1.0.1
mod_version=2.0.0
maven_group=net.legitimoose
archives_base_name=Legitimoose-Bot
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.138.3+1.21.10
fabric_kotlin_version=1.13.7+kotlin.2.2.21
4 changes: 3 additions & 1 deletion run/config/legitimoosebot-config.properties.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ webhookUrl=
errorWebhookUrl=
mongoUri=mongodb://127.0.0.1:27017/
secretPrefix=::
channelId=
channelId=
discordGuildId=
waitMinutesBetweenScrapes=
17 changes: 17 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{ pkgs ? import <nixpkgs> {} }:
let
runtimeLibs = with pkgs; lib.makeLibraryPath [
glfw
libGL
xorg.libX11
xorg.libXcursor
xorg.libXext
xorg.libXrandr
xorg.libXxf86vm
libglvnd
];
in pkgs.mkShell {
shellHook = ''
export LD_LIBRARY_PATH=${runtimeLibs}:$LD_LIBRARY_PATH
'';
}
221 changes: 221 additions & 0 deletions src/client/java/net/legitimoose/bot/LegitimooseBotClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package net.legitimoose.bot;

import net.dv8tion.jda.api.entities.Member;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.legitimoose.bot.discord.DiscordBot;
import net.legitimoose.bot.discord.DiscordWebhook;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.*;
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.resolver.ServerAddress;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static net.legitimoose.bot.LegitimooseBot.CONFIG;
import static net.legitimoose.bot.LegitimooseBot.LOGGER;

public class LegitimooseBotClient implements ClientModInitializer {
public static final Minecraft mc = Minecraft.getInstance();
public static final Scraper scraper = new Scraper();

private final Pattern joinPattern = Pattern.compile("^\\[\\+]\\s*(?:[^|]+\\|\\s*)?(\\S+)");
private final Pattern switchPattern = Pattern.compile("^\\[→]\\s*(?:[^|]+\\|\\s*)?(\\S+)");
private final Pattern leavePattern = Pattern.compile("^\\[-]\\s*(?:[^|]+\\|\\s*)?(\\S+)");

private final Pattern chatPattern = Pattern.compile("^(?:\\[SHOUT]\\s*)?(?:[^|]+\\|\\s*)?([^:]+): (.*)");
private final Pattern msgPattern = Pattern.compile("\\[(.*) -> me] @(.*) (.*)");

static volatile private long lastJoinTimestamp = 0L;
private static final long REJOIN_COOLDOWN_MS = 5_000L;

Timer timer = new Timer();

@Override
public void onInitializeClient() {
timer.schedule(new TimerTask() {
@Override
public void run() {
System.exit(67);
}
}, TimeUnit.HOURS.toMillis(24), TimeUnit.HOURS.toMillis(24));

ClientCommandRegistrationCallback.EVENT.register((dispatcher, commandBuildContext) -> dispatcher.register(
ClientCommandManager.literal("scrape").executes((context -> {
new Thread(scraper::scrape).start();
return 1;
}))));

new Thread(DiscordBot::run).start();

ClientTickEvents.END_CLIENT_TICK.register((minecraft) -> rejoin(false));

new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage());
}
rejoin(false);
}).start();

new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage());
}
while (true) {
scraper.scrape();
try {
TimeUnit.MINUTES.sleep((long) CONFIG.getOrDefault("waitMinutesBetweenScrapes", 20));
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage());
}
}
}).start();

new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage());
}
while (true) {
try {
mc.player
.connection
.
sendChat("<br><red>I am a bot that syncs lobby chat to a community Discord<br>To prevent messages being sent to discord, prefix your messages with <u>::<br><reset>You can check out our work at <b>https://legiti.dev/");
TimeUnit.MINUTES.sleep(20);
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage());
}
}
}).start();

ClientReceiveMessageEvents.GAME.register(((message, overlay) ->
new Thread(() -> {
String msg = message.getString();
String username = "";
String cleanMessage = msg;

Matcher joinMatcher = joinPattern.matcher(msg);
Matcher switchMatcher = switchPattern.matcher(msg);
Matcher leaveMatcher = leavePattern.matcher(msg);
Matcher chatMatcher = chatPattern.matcher(msg);
Matcher msgMatcher = msgPattern.matcher(msg);

DiscordWebhook webhook = new DiscordWebhook(CONFIG.getOrDefault("webhookUrl", ""));
if (joinMatcher.find()) {
username = joinMatcher.group(1);
cleanMessage = String.format("**%s** joined the server.", username);
webhook.setEmbedThumbnail(String.format("https://mc-heads.net/head/%s/50/left", username));
webhook.setContent(cleanMessage.replace("@", ""));
try {
webhook.execute(0x57F287);
} catch (IOException | URISyntaxException e) {
LOGGER.warn(e.getMessage());
}
return;
} else if (switchMatcher.find()) {
username = switchMatcher.group(1);
cleanMessage = String.format("**%s** switched servers.", username);
webhook.setEmbedThumbnail(String.format("https://mc-heads.net/head/%s/50/left", username));
webhook.setContent(cleanMessage.replace("@", ""));
try {
webhook.execute(0xF2F257);
} catch (IOException | URISyntaxException e) {
LOGGER.warn(e.getMessage());
}
return;
} else if (leaveMatcher.find()) {
username = leaveMatcher.group(1);
cleanMessage = String.format("**%s** left the server.", username);
webhook.setEmbedThumbnail(String.format("https://mc-heads.net/head/%s/50/left", username));
webhook.setContent(cleanMessage.replace("@", ""));
try {
webhook.execute(0xF25757);
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
return;
} else if (chatMatcher.find()) {
username = chatMatcher.group(1);
cleanMessage = chatMatcher.group(2);
if (msg.startsWith("[SHOUT]")) {
webhook.setUsername("[SHOUT] $username");
} else {
webhook.setUsername(username);
}
webhook.setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", username));
} else if (msgMatcher.find()) {
String username1 = msgMatcher.group(1);
String username2 = msgMatcher.group(2);
String msg1 = msgMatcher.group(3);
Member member =
DiscordBot.jda
.getGuildById(CONFIG.getOrDefault("discordGuildId", "1311574348989071440"))
.findMembers(s -> s.getUser().getName().equals(username2))
.get().getFirst();
member.getUser()
.openPrivateChannel()
.flatMap(channel -> channel.sendMessage(String.format("%s: %s", username1, msg1)))
.queue();
return;
}

if (username.equals(mc.player.getName().getString())) return;

if (!username.isEmpty() &&
!cleanMessage.startsWith(CONFIG.getOrDefault("secretPrefix", "::"))
) {
webhook.setContent(cleanMessage.replace("@", ""));
try {
webhook.execute();
} catch (IOException | URISyntaxException e) {
LOGGER.warn(e.getMessage());
}
}
}).start()));
}

public static void rejoin(boolean force) {
if (FabricLoader.getInstance().isDevelopmentEnvironment()) return;
Screen screen = mc.screen;
if (screen instanceof DisconnectedScreen ||
screen instanceof JoinMultiplayerScreen ||
screen instanceof TitleScreen ||
screen instanceof AccessibilityOnboardingScreen ||
(mc.getConnection() != null && force)
) {
Minecraft.getInstance().schedule(() -> {
long now = System.currentTimeMillis();
if (now - lastJoinTimestamp >= REJOIN_COOLDOWN_MS) {
lastJoinTimestamp = now;
LOGGER.info("Attempting to reconnect to server");
ServerData info = new ServerData("Server", "legitimoose.com", ServerData.Type.OTHER);
ConnectScreen.startConnecting(
new JoinMultiplayerScreen(null),
mc,
ServerAddress.parseString("legitimoose.com"),
info,
false,
null
);
}
});
}
}
}
Loading