Skip to content
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c5a6d1e
Begin working on Discord integration
Jakubk15 Jan 16, 2026
6307b0e
Finish implementing Discord integration
Jakubk15 Jan 17, 2026
9e4a998
Add `@Async` annotation to command executors to ensure that the serve…
Jakubk15 Jan 17, 2026
1109f11
Remove `@Async` annotations
Jakubk15 Jan 17, 2026
67ac9a2
Refactor Discord integration to use reactive programming for login an…
Jakubk15 Jan 17, 2026
e98e263
Remove redundant check for bot admin role ID in Discord configuration…
Jakubk15 Jan 17, 2026
8751030
Update src/main/java/com/eternalcode/parcellockers/discord/repository…
Jakubk15 Jan 17, 2026
031d466
Update src/main/java/com/eternalcode/parcellockers/discord/repository…
Jakubk15 Jan 17, 2026
fc95d79
Merge branch 'master' into discord
Jakubk15 Jan 17, 2026
ed98f10
Add service layer
Jakubk15 Jan 22, 2026
14ae198
Add DiscordSRV hook integration
Jakubk15 Jan 22, 2026
7de214f
feat: Update DiscordSRV integration and add parcel delivery notificat…
Jakubk15 Jan 24, 2026
143d8a1
feat: Implement abstract DiscordNotificationService with Discord4J an…
Jakubk15 Jan 24, 2026
161cec0
fix: declare appropriate events as async
Jakubk15 Jan 24, 2026
f2b470a
fix: avoid DiscordClientManager#getClient race conditions by using bl…
Jakubk15 Jan 24, 2026
9342e82
Remove redundant discord properties
Jakubk15 Jan 24, 2026
a271e9f
Update src/main/java/com/eternalcode/parcellockers/discord/command/Di…
Jakubk15 Jan 24, 2026
8809808
fix: initialize DiscordLinkRepository only when fallback Discord inte…
Jakubk15 Jan 24, 2026
457490b
Update src/main/java/com/eternalcode/parcellockers/discord/command/Di…
Jakubk15 Jan 24, 2026
dee0d6c
Update src/main/java/com/eternalcode/parcellockers/discord/command/Di…
Jakubk15 Jan 24, 2026
73477f6
Update src/main/java/com/eternalcode/parcellockers/configuration/impl…
Jakubk15 Jan 24, 2026
152dc6f
Merge remote-tracking branch 'origin/discord' into discord
Jakubk15 Jan 24, 2026
b8d54bf
Update src/main/java/com/eternalcode/parcellockers/discord/command/Di…
Jakubk15 Jan 24, 2026
a7f2a35
fix: add logging to DiscordSrvLinkService for better error handling
Jakubk15 Jan 24, 2026
2ddcf51
Merge remote-tracking branch 'origin/discord' into discord
Jakubk15 Jan 24, 2026
6d1c84e
Update PluginConfig.java
Jakubk15 Jan 24, 2026
5cbffe7
Update ParcelDeliverNotificationController.java
Jakubk15 Jan 24, 2026
77b19aa
Update ParcelDeliverNotificationController.java
Jakubk15 Jan 24, 2026
d0ed324
Add missing import
Jakubk15 Jan 24, 2026
3555651
WIP - Adjust to DMK suggestions
Jakubk15 Jan 25, 2026
8df0ed9
Use OfflinePlayer instead of String
Jakubk15 Jan 25, 2026
a31f0c2
Use OfflinePlayer in unlinkPlayer executor too
Jakubk15 Jan 25, 2026
e4070c3
Align with single-responsibility-principle
Jakubk15 Jan 25, 2026
7dd0cc7
Ensure proper encapsulation
Jakubk15 Jan 25, 2026
055acc2
Refactor sendPrivateMessage method to return void and improve message…
Jakubk15 Jan 25, 2026
57ac290
Improve error logging in DiscordClientManager during login failure
Jakubk15 Jan 25, 2026
00cf84d
Merge branch 'master' into discord
Jakubk15 Jan 25, 2026
201c382
Refactor Discord ID handling to use long, add NoticeHandler, remove r…
Jakubk15 Jan 25, 2026
5d50f24
Delete random javadocs
Jakubk15 Jan 25, 2026
cfd7509
Refactor Discord notification handling, introduce Formatter for messa…
Jakubk15 Jan 25, 2026
bc63ada
Refactor notification handling to use thenAcceptBoth for improved mes…
Jakubk15 Jan 27, 2026
c48268b
Refactor Discord link/unlink to use result enums
Jakubk15 Jan 29, 2026
d2cabe2
Use FutureHandler
Jakubk15 Jan 30, 2026
8eccb8a
Subscribe to the DiscordClient logout rather than blocking the thread.
Jakubk15 Jan 31, 2026
be5d0f7
Refactor Discord verification handling to utilize configurable link c…
Jakubk15 Jan 31, 2026
52dd98c
Adjust to Gemini suggestions
Jakubk15 Jan 31, 2026
a3f16f4
Merge branch 'master' into discord
Jakubk15 Feb 1, 2026
d2a5deb
Merge branch 'master' into discord
Jakubk15 Feb 1, 2026
dd665ff
Adjust to Martin suggestion with delay calculation
Jakubk15 Feb 5, 2026
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
12 changes: 12 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ repositories {
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.eternalcode.pl/releases")
maven("https://storehouse.okaeri.eu/repository/maven-public/")
maven("https://nexus.scarsz.me/content/groups/public/") // DiscordSRV
}

dependencies {
Expand Down Expand Up @@ -78,6 +79,12 @@ dependencies {
// vault
compileOnly("com.github.MilkBowl:VaultAPI:1.7.1")

// discord integration library
paperLibrary("com.discord4j:discord4j-core:3.3.0")

// discordsrv (optional integration)
compileOnly("com.discordsrv:discordsrv:1.30.4")

testImplementation("org.junit.jupiter:junit-jupiter-api:6.0.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:6.0.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:6.0.2")
Expand Down Expand Up @@ -108,6 +115,10 @@ paper {
required = true
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
register("DiscordSRV") {
required = false
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
}
}

Expand All @@ -124,6 +135,7 @@ tasks {
downloadPlugins.modrinth("luckperms", "v5.5.17-bukkit")
downloadPlugins.modrinth("vaultunlocked", "2.17.0")
downloadPlugins.modrinth("essentialsx", "2.21.2")
downloadPlugins.modrinth("discordsrv", "1.30.4")
}

test {
Expand Down
122 changes: 112 additions & 10 deletions src/main/java/com/eternalcode/parcellockers/ParcelLockers.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,30 @@
import com.eternalcode.parcellockers.configuration.ConfigService;
import com.eternalcode.parcellockers.configuration.implementation.MessageConfig;
import com.eternalcode.parcellockers.configuration.implementation.PluginConfig;
import com.eternalcode.parcellockers.configuration.implementation.PluginConfig.DiscordSettings;
import com.eternalcode.parcellockers.content.ParcelContentManager;
import com.eternalcode.parcellockers.content.repository.ParcelContentRepository;
import com.eternalcode.parcellockers.content.repository.ParcelContentRepositoryOrmLite;
import com.eternalcode.parcellockers.database.DatabaseManager;
import com.eternalcode.parcellockers.delivery.DeliveryManager;
import com.eternalcode.parcellockers.delivery.repository.DeliveryRepositoryOrmLite;
import com.eternalcode.parcellockers.discord.DiscordClientManager;
import com.eternalcode.parcellockers.discord.DiscordFallbackLinkService;
import com.eternalcode.parcellockers.discord.DiscordLinkService;
import com.eternalcode.parcellockers.discord.DiscordSrvLinkService;
import com.eternalcode.parcellockers.discord.argument.SnowflakeArgument;
import com.eternalcode.parcellockers.discord.command.DiscordLinkCommand;
import com.eternalcode.parcellockers.discord.command.DiscordSrvLinkCommand;
import com.eternalcode.parcellockers.discord.command.DiscordSrvUnlinkCommand;
import com.eternalcode.parcellockers.discord.command.DiscordUnlinkCommand;
import com.eternalcode.parcellockers.discord.controller.DiscordDeliverNotificationController;
import com.eternalcode.parcellockers.discord.notification.Discord4JNotificationService;
import com.eternalcode.parcellockers.discord.notification.DiscordNotificationService;
import com.eternalcode.parcellockers.discord.notification.DiscordSrvNotificationService;
import com.eternalcode.parcellockers.discord.repository.DiscordLinkRepository;
import com.eternalcode.parcellockers.discord.repository.DiscordLinkRepositoryOrmLite;
import com.eternalcode.parcellockers.discord.verification.DiscordLinkValidationService;
import com.eternalcode.parcellockers.discord.verification.DiscordVerificationService;
import com.eternalcode.parcellockers.gui.GuiManager;
import com.eternalcode.parcellockers.gui.implementation.locker.LockerGui;
import com.eternalcode.parcellockers.gui.implementation.remote.MainGui;
Expand Down Expand Up @@ -54,6 +72,7 @@
import dev.rollczi.liteskullapi.LiteSkullFactory;
import dev.rollczi.liteskullapi.SkullAPI;
import dev.triumphteam.gui.TriumphGui;
import discord4j.common.util.Snowflake;
import java.io.File;
import java.sql.SQLException;
import java.time.Duration;
Expand All @@ -72,6 +91,7 @@ public final class ParcelLockers extends JavaPlugin {
private SkullAPI skullAPI;
private DatabaseManager databaseManager;
private Economy economy;
private DiscordClientManager discordClientManager;

@Override
public void onEnable() {
Expand All @@ -82,7 +102,8 @@ public void onEnable() {

ConfigService configService = new ConfigService();
PluginConfig config = configService.create(PluginConfig.class, new File(this.getDataFolder(), "config.yml"));
MessageConfig messageConfig = configService.create(MessageConfig.class, new File(this.getDataFolder(), "messages.yml"));
MessageConfig messageConfig =
configService.create(MessageConfig.class, new File(this.getDataFolder(), "messages.yml"));
Server server = this.getServer();
NoticeService noticeService = new NoticeService(messageConfig, miniMessage);
Scheduler scheduler = new BukkitSchedulerImpl(this);
Expand Down Expand Up @@ -113,7 +134,8 @@ public void onEnable() {
// database repositories
ParcelRepositoryOrmLite parcelRepository = new ParcelRepositoryOrmLite(databaseManager, scheduler);
LockerRepositoryOrmLite lockerRepository = new LockerRepositoryOrmLite(databaseManager, scheduler);
ParcelContentRepository parcelContentRepository = new ParcelContentRepositoryOrmLite(databaseManager, scheduler);
ParcelContentRepository parcelContentRepository =
new ParcelContentRepositoryOrmLite(databaseManager, scheduler);
DeliveryRepositoryOrmLite deliveryRepository = new DeliveryRepositoryOrmLite(databaseManager, scheduler);
ItemStorageRepository itemStorageRepository = new ItemStorageRepositoryOrmLite(databaseManager, scheduler);
UserRepository userRepository = new UserRepositoryOrmLite(databaseManager, scheduler);
Expand All @@ -132,7 +154,8 @@ public void onEnable() {
UserValidationService userValidationService = new UserValidator();
UserManager userManager = new UserManagerImpl(userRepository, userValidationService, server);
LockerValidationService lockerValidationService = new LockerValidator();
LockerManager lockerManager = new LockerManager(config, lockerRepository, lockerValidationService, parcelRepository, server);
LockerManager lockerManager =
new LockerManager(config, lockerRepository, lockerValidationService, parcelRepository, server);
ParcelContentManager parcelContentManager = new ParcelContentManager(parcelContentRepository);
ItemStorageManager itemStorageManager = new ItemStorageManager(itemStorageRepository, server);
DeliveryManager deliveryManager = new DeliveryManager(deliveryRepository);
Expand Down Expand Up @@ -175,19 +198,92 @@ public void onEnable() {
this.skullAPI
);

this.liteCommands = LiteBukkitFactory.builder(this.getName(), this)
var liteCommandsBuilder = LiteBukkitFactory.builder(this.getName(), this)
.extension(new LiteAdventureExtension<>())
.argument(Snowflake.class, new SnowflakeArgument())
.message(LiteBukkitMessages.PLAYER_ONLY, messageConfig.playerOnlyCommand)
.message(LiteBukkitMessages.PLAYER_NOT_FOUND, messageConfig.playerNotFound)
.commands(LiteCommandsAnnotations.of(
new ParcelCommand(mainGUI),
new ParcelLockersCommand(configService, config, noticeService),
new DebugCommand(parcelService, lockerManager, itemStorageManager, parcelContentManager,
new DebugCommand(
parcelService, lockerManager, itemStorageManager, parcelContentManager,
noticeService, deliveryManager)
))
.invalidUsage(new InvalidUsageHandlerImpl(noticeService))
.missingPermission(new MissingPermissionsHandlerImpl(noticeService))
.build();
.missingPermission(new MissingPermissionsHandlerImpl(noticeService));

DiscordSettings discordSettings = config.discord;
if (discordSettings.enabled) {
DiscordNotificationService notificationService;
DiscordLinkService activeLinkService;

if (server.getPluginManager().isPluginEnabled("DiscordSRV")) {
this.getLogger().info("DiscordSRV detected! Using DiscordSRV for account linking.");
DiscordSrvLinkService discordSrvLinkService = new DiscordSrvLinkService(this.getLogger());
activeLinkService = discordSrvLinkService;
notificationService = new DiscordSrvNotificationService(this.getLogger());

liteCommandsBuilder.commands(
new DiscordSrvLinkCommand(discordSrvLinkService, noticeService),
new DiscordSrvUnlinkCommand(discordSrvLinkService, noticeService)
);
} else {
if (config.discord.botToken == null || config.discord.botToken.isBlank()) {
this.getLogger()
.severe("Discord integration is enabled but some of the properties are not set! Disabling...");
server.getPluginManager().disablePlugin(this);
return;
}

this.discordClientManager = new DiscordClientManager(
discordSettings.botToken,
this.getLogger()
);
this.discordClientManager.initialize();

DiscordLinkRepository discordLinkRepository =
new DiscordLinkRepositoryOrmLite(databaseManager, scheduler);
activeLinkService = new DiscordFallbackLinkService(discordLinkRepository);
notificationService = new Discord4JNotificationService(
this.discordClientManager.getClient(),
this.getLogger()
);

DiscordLinkValidationService validationService = new DiscordLinkValidationService(
activeLinkService,
this.discordClientManager.getClient()
);

DiscordVerificationService verificationService = DiscordVerificationService.create(
activeLinkService,
noticeService,
messageConfig,
miniMessage
);

liteCommandsBuilder.commands(
new DiscordLinkCommand(
activeLinkService,
validationService,
verificationService,
noticeService),
new DiscordUnlinkCommand(activeLinkService, noticeService)
);
}

server.getPluginManager().registerEvents(
new DiscordDeliverNotificationController(
notificationService,
activeLinkService,
userManager,
messageConfig
),
this
);
}

this.liteCommands = liteCommandsBuilder.build();

Stream.of(
new LockerInteractionController(lockerManager, lockerGUI, scheduler),
Expand All @@ -197,16 +293,18 @@ public void onEnable() {
new LoadUserController(userManager, server)
).forEach(controller -> server.getPluginManager().registerEvents(controller, this));

new Metrics(this, 17677);
new UpdaterService(this.getPluginMeta().getVersion());
Metrics metrics = new Metrics(this, 17677);
UpdaterService updaterService = new UpdaterService(this.getPluginMeta().getVersion());

parcelRepository.findAll().thenAccept(optionalParcels -> optionalParcels
.stream()
.filter(parcel -> parcel.status() != ParcelStatus.DELIVERED)
.forEach(parcel -> deliveryRepository.find(parcel.uuid()).thenAccept(optionalDelivery ->
optionalDelivery.ifPresent(delivery -> {
long delay = Math.max(0, delivery.deliveryTimestamp().toEpochMilli() - System.currentTimeMillis());
scheduler.runLaterAsync(new ParcelSendTask(parcel, parcelService, deliveryManager), Duration.ofMillis(delay));
scheduler.runLaterAsync(
new ParcelSendTask(parcel, parcelService, deliveryManager),
Duration.ofMillis(delay));
})
)));
}
Expand All @@ -224,6 +322,10 @@ public void onDisable() {
if (this.skullAPI != null) {
this.skullAPI.shutdown();
}

if (this.discordClientManager != null) {
this.discordClientManager.shutdown();
}
}

private boolean setupEconomy() {
Expand Down
Loading