chore: unify checkstyle, fix violations, cleanup bin/

This commit is contained in:
loki5512344 2026-07-12 00:59:21 +02:00
parent 9c5e5bccae
commit da9271b283
Signed by: boba
GPG key ID: 253067914055423B
17 changed files with 363 additions and 67 deletions

View file

@ -1,6 +1,7 @@
plugins { plugins {
java java
id("com.gradleup.shadow") version "9.3.1" apply false id("com.gradleup.shadow") version "9.3.1" apply false
checkstyle
} }
allprojects { allprojects {
@ -18,8 +19,24 @@ allprojects {
subprojects { subprojects {
apply(plugin = "java") apply(plugin = "java")
apply(plugin = "checkstyle")
java { java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21)) toolchain.languageVersion.set(JavaLanguageVersion.of(21))
} }
checkstyle {
toolVersion = "10.21.4"
configFile = rootProject.file("config/checkstyle/checkstyle.xml")
isIgnoreFailures = false
maxErrors = 0
maxWarnings = 0
}
tasks.withType<Checkstyle>().configureEach {
reports {
xml.required.set(true)
html.required.set(true)
}
}
} }

View file

@ -40,7 +40,9 @@ public final class YamlConfig {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T get(final String key, final T defaultValue) { public <T> T get(final String key, final T defaultValue) {
Object val = data.get(key); Object val = data.get(key);
if (val == null) return defaultValue; if (val == null) {
return defaultValue;
}
try { try {
return (T) val; return (T) val;
} catch (ClassCastException e) { } catch (ClassCastException e) {

View file

@ -8,7 +8,11 @@ import dev.loki.loAuth.common.util.CodeGenerator;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import java.sql.*; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -159,7 +163,9 @@ public final class DatabaseManager implements AutoCloseable {
throw new SQLException("INSERT returned no rows"); throw new SQLException("INSERT returned no rows");
} else { } else {
try (ResultSet rs = ps.executeQuery()) { try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return mapUser(rs); if (rs.next()) {
return mapUser(rs);
}
throw new SQLException("INSERT returned no rows"); throw new SQLException("INSERT returned no rows");
} }
} }
@ -240,7 +246,9 @@ public final class DatabaseManager implements AutoCloseable {
} }
public Optional<String> consumeVerificationCode(final String code) { public Optional<String> consumeVerificationCode(final String code) {
if (sqlite) return consumeDeleteSelect("pending_verifications", "code", code); if (sqlite) {
return consumeDeleteSelect("pending_verifications", "code", code);
}
try (Connection conn = ds.getConnection(); try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(""" PreparedStatement ps = conn.prepareStatement("""
DELETE FROM pending_verifications DELETE FROM pending_verifications
@ -309,7 +317,9 @@ public final class DatabaseManager implements AutoCloseable {
} }
public Optional<String> consumeLoginToken(final String token) { public Optional<String> consumeLoginToken(final String token) {
if (sqlite) return consumeDeleteSelect("pending_logins", "token", token); if (sqlite) {
return consumeDeleteSelect("pending_logins", "token", token);
}
try (Connection conn = ds.getConnection(); try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(""" PreparedStatement ps = conn.prepareStatement("""
DELETE FROM pending_logins DELETE FROM pending_logins
@ -327,7 +337,9 @@ public final class DatabaseManager implements AutoCloseable {
} }
public Optional<String> consumeLoginTokenByMessageId(final String messageId) { public Optional<String> consumeLoginTokenByMessageId(final String messageId) {
if (sqlite) return consumeDeleteSelect("pending_logins", "message_id", messageId); if (sqlite) {
return consumeDeleteSelect("pending_logins", "message_id", messageId);
}
try (Connection conn = ds.getConnection(); try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement(""" PreparedStatement ps = conn.prepareStatement("""
DELETE FROM pending_logins DELETE FROM pending_logins
@ -378,7 +390,9 @@ public final class DatabaseManager implements AutoCloseable {
"SELECT minecraft_nick FROM pending_logins WHERE expires_at <= ?")) { "SELECT minecraft_nick FROM pending_logins WHERE expires_at <= ?")) {
ps.setLong(1, now); ps.setLong(1, now);
try (ResultSet rs = ps.executeQuery()) { try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) nicks.add(rs.getString("minecraft_nick")); while (rs.next()) {
nicks.add(rs.getString("minecraft_nick"));
}
} }
} }
if (!nicks.isEmpty()) { if (!nicks.isEmpty()) {
@ -399,7 +413,9 @@ public final class DatabaseManager implements AutoCloseable {
"DELETE FROM pending_logins WHERE expires_at <= ? RETURNING minecraft_nick")) { "DELETE FROM pending_logins WHERE expires_at <= ? RETURNING minecraft_nick")) {
ps.setLong(1, now); ps.setLong(1, now);
try (ResultSet rs = ps.executeQuery()) { try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) nicks.add(rs.getString("minecraft_nick")); while (rs.next()) {
nicks.add(rs.getString("minecraft_nick"));
}
} }
} catch (SQLException e) { } catch (SQLException e) {
throw new DatabaseException("Failed to pop expired logins", e); throw new DatabaseException("Failed to pop expired logins", e);
@ -456,23 +472,27 @@ public final class DatabaseManager implements AutoCloseable {
} }
private static void validateNick(final String nick) { private static void validateNick(final String nick) {
if (nick == null || !nick.matches("^[a-zA-Z0-9_]{3,16}$")) if (nick == null || !nick.matches("^[a-zA-Z0-9_]{3,16}$")) {
throw new IllegalArgumentException("Invalid Minecraft nick: " + nick); throw new IllegalArgumentException("Invalid Minecraft nick: " + nick);
} }
}
private static void validateDiscordId(final String id) { private static void validateDiscordId(final String id) {
if (id == null || !id.matches("^[0-9]{17,20}$")) if (id == null || !id.matches("^[0-9]{17,20}$")) {
throw new IllegalArgumentException("Invalid Discord ID: " + id); throw new IllegalArgumentException("Invalid Discord ID: " + id);
} }
}
private static RuntimeException translateSqlException(final SQLException e) { private static RuntimeException translateSqlException(final SQLException e) {
String state = e.getSQLState(); String state = e.getSQLState();
if ("23505".equals(state) || "23000".equals(state)) { if ("23505".equals(state) || "23000".equals(state)) {
String msg = e.getMessage(); String msg = e.getMessage();
if (msg != null && (msg.contains("minecraft_nick") || msg.contains("UNIQUE constraint failed: users.minecraft_nick"))) if (msg != null && (msg.contains("minecraft_nick") || msg.contains("UNIQUE constraint failed: users.minecraft_nick"))) {
return new DuplicateUserException("Minecraft nick already registered"); return new DuplicateUserException("Minecraft nick already registered");
if (msg != null && (msg.contains("discord_id") || msg.contains("UNIQUE constraint failed: users.discord_id"))) }
if (msg != null && (msg.contains("discord_id") || msg.contains("UNIQUE constraint failed: users.discord_id"))) {
return new DuplicateUserException("Discord ID already registered"); return new DuplicateUserException("Discord ID already registered");
}
return new DuplicateUserException("Duplicate entry"); return new DuplicateUserException("Duplicate entry");
} }
return new DatabaseException("Database error", e); return new DatabaseException("Database error", e);

View file

@ -1,5 +1,7 @@
package dev.loki.loAuth.common.exception; package dev.loki.loAuth.common.exception;
public final class DatabaseException extends RuntimeException { public final class DatabaseException extends RuntimeException {
public DatabaseException(String msg, Throwable cause) { super(msg, cause); } public DatabaseException(String msg, Throwable cause) {
super(msg, cause);
}
} }

View file

@ -1,5 +1,7 @@
package dev.loki.loAuth.common.exception; package dev.loki.loAuth.common.exception;
public final class DuplicateUserException extends RuntimeException { public final class DuplicateUserException extends RuntimeException {
public DuplicateUserException(String msg) { super(msg); } public DuplicateUserException(String msg) {
super(msg);
}
} }

View file

@ -10,12 +10,14 @@ public final class CodeGenerator {
private CodeGenerator() {} private CodeGenerator() {}
public static String generate(final int length) { public static String generate(final int length) {
if (length < 4 || length > 16) if (length < 4 || length > 16) {
throw new IllegalArgumentException("Code length must be between 4 and 16"); throw new IllegalArgumentException("Code length must be between 4 and 16");
}
StringBuilder sb = new StringBuilder(length); StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++) {
sb.append(ALPHABET.charAt(RNG.nextInt(ALPHABET.length()))); sb.append(ALPHABET.charAt(RNG.nextInt(ALPHABET.length())));
}
return sb.toString(); return sb.toString();
} }
} }

View file

@ -0,0 +1,89 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="error"/>
<property name="fileExtensions" value="java"/>
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value=".*[\\/]src[\\/]test[\\/].*"/>
</module>
<module name="SuppressWarningsFilter"/>
<module name="LineLength">
<property name="max" value="150"/>
<property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="TreeWalker">
<module name="SuppressWarningsHolder"/>
<module name="MethodLength">
<property name="max" value="80"/>
<property name="countEmpty" value="false"/>
</module>
<module name="CyclomaticComplexity">
<property name="max" value="20"/>
</module>
<module name="ParameterNumber">
<property name="max" value="7"/>
</module>
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<module name="RedundantImport"/>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-zA-Z][a-zA-Z0-9]*)*$"/>
</module>
<module name="TypeName"/>
<module name="MethodName"/>
<module name="MemberName"/>
<module name="ParameterName"/>
<module name="LocalVariableName"/>
<module name="ConstantName">
<property name="format" value="^log$|^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
</module>
<module name="NeedBraces"/>
<module name="LeftCurly"/>
<module name="RightCurly"/>
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="DefaultComesLast"/>
<module name="MultipleVariableDeclarations"/>
<module name="OneStatementPerLine"/>
<module name="FallThrough"/>
<module name="HideUtilityClassConstructor"/>
<module name="OneTopLevelClass"/>
<module name="UpperEll"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="OuterTypeFilename"/>
<module name="EmptyBlock">
<property name="option" value="text"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected|ignore"/>
</module>
<module name="EqualsAvoidNull"/>
<module name="NoFinalizer"/>
<module name="NoClone"/>
<module name="PackageDeclaration"/>
</module>
</module>

View file

@ -11,6 +11,8 @@ import java.nio.file.Path;
public final class BotLauncher { public final class BotLauncher {
private BotLauncher() {}
private static final Logger log = LoggerFactory.getLogger(BotLauncher.class); private static final Logger log = LoggerFactory.getLogger(BotLauncher.class);
public static void main(final String[] args) { public static void main(final String[] args) {
@ -33,7 +35,9 @@ public final class BotLauncher {
Path path = Path.of(System.getProperty("user.dir")).resolve(fileName); Path path = Path.of(System.getProperty("user.dir")).resolve(fileName);
if (Files.notExists(path)) { if (Files.notExists(path)) {
try (InputStream in = BotLauncher.class.getResourceAsStream("/" + fileName)) { try (InputStream in = BotLauncher.class.getResourceAsStream("/" + fileName)) {
if (in == null) throw new IOException("Resource not found: " + fileName); if (in == null) {
throw new IOException("Resource not found: " + fileName);
}
Files.copy(in, path); Files.copy(in, path);
} }
} }

View file

@ -9,7 +9,9 @@ import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.*; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public final class AuthService implements AutoCloseable { public final class AuthService implements AutoCloseable {
@ -36,7 +38,9 @@ public final class AuthService implements AutoCloseable {
public void verifyNewUser(final String code, final String discordId) { public void verifyNewUser(final String code, final String discordId) {
Optional<String> nick = db.consumeVerificationCode(code.toUpperCase()); Optional<String> nick = db.consumeVerificationCode(code.toUpperCase());
if (nick.isEmpty()) throw new IllegalArgumentException("Invalid or expired code: " + code); if (nick.isEmpty()) {
throw new IllegalArgumentException("Invalid or expired code: " + code);
}
db.addUser(nick.get(), discordId); db.addUser(nick.get(), discordId);
log.info("New user registered: {} -> {}", nick.get(), discordId); log.info("New user registered: {} -> {}", nick.get(), discordId);
} }
@ -75,8 +79,9 @@ public final class AuthService implements AutoCloseable {
private void kickExpiredLogins() { private void kickExpiredLogins() {
try { try {
List<String> expired = db.popExpiredLogins(); List<String> expired = db.popExpiredLogins();
if (!expired.isEmpty()) if (!expired.isEmpty()) {
log.info("Expired logins cleaned up: {}", expired); log.info("Expired logins cleaned up: {}", expired);
}
} catch (Exception e) { } catch (Exception e) {
log.error("Error during expired login cleanup", e); log.error("Error during expired login cleanup", e);
} }

View file

@ -13,13 +13,48 @@ public final class BotCfg implements BotConfig {
cfg = new YamlConfig(configPath); cfg = new YamlConfig(configPath);
} }
@Override public String getToken() { return cfg.getRequired("token", String.class); } @Override
@Override public String getDbType() { return cfg.getRequired("db_type", String.class); } public String getToken() {
@Override public String getDbHost() { return cfg.getRequired("db_host", String.class); } return cfg.getRequired("token", String.class);
@Override public int getDbPort() { return cfg.getRequired("db_port", Integer.class); } }
@Override public String getDbName() { return cfg.getRequired("db_name", String.class); }
@Override public String getDbUser() { return cfg.getRequired("db_user", String.class); } @Override
@Override public String getDbPassword() { return cfg.getRequired("db_password", String.class); } public String getDbType() {
@Override public int getAuthTimeoutSeconds() { return cfg.getRequired("auth_timeout_seconds", Integer.class); } return cfg.getRequired("db_type", String.class);
@Override public int getVerificationCodeLength() { return cfg.getRequired("verification_code_length",Integer.class); } }
@Override
public String getDbHost() {
return cfg.getRequired("db_host", String.class);
}
@Override
public int getDbPort() {
return cfg.getRequired("db_port", Integer.class);
}
@Override
public String getDbName() {
return cfg.getRequired("db_name", String.class);
}
@Override
public String getDbUser() {
return cfg.getRequired("db_user", String.class);
}
@Override
public String getDbPassword() {
return cfg.getRequired("db_password", String.class);
}
@Override
public int getAuthTimeoutSeconds() {
return cfg.getRequired("auth_timeout_seconds", Integer.class);
}
@Override
public int getVerificationCodeLength() {
return cfg.getRequired("verification_code_length", Integer.class);
}
} }

View file

@ -19,8 +19,12 @@ public final class LoginReactionListener extends ListenerAdapter {
@Override @Override
public void onMessageReactionAdd(final MessageReactionAddEvent event) { public void onMessageReactionAdd(final MessageReactionAddEvent event) {
if (event.getUser() == null || event.getUser().isBot()) return; if (event.getUser() == null || event.getUser().isBot()) {
if (!CHECKMARK.equals(event.getReaction().getEmoji().getName())) return; return;
}
if (!CHECKMARK.equals(event.getReaction().getEmoji().getName())) {
return;
}
// Ищем токен по message_id — бот сохранил его при отправке // Ищем токен по message_id — бот сохранил его при отправке
db.consumeLoginTokenByMessageId(event.getMessageId()).ifPresent(nick -> db.consumeLoginTokenByMessageId(event.getMessageId()).ifPresent(nick ->

View file

@ -18,11 +18,17 @@ public final class VerifyListener extends ListenerAdapter {
@Override @Override
public void onMessageReceived(final MessageReceivedEvent event) { public void onMessageReceived(final MessageReceivedEvent event) {
if (event.getAuthor().isBot()) return; if (event.getAuthor().isBot()) {
if (!event.isFromType(ChannelType.PRIVATE)) return; return;
}
if (!event.isFromType(ChannelType.PRIVATE)) {
return;
}
String content = event.getMessage().getContentRaw().trim(); String content = event.getMessage().getContentRaw().trim();
if (!content.toUpperCase().startsWith(COMMAND)) return; if (!content.toUpperCase().startsWith(COMMAND)) {
return;
}
String[] parts = content.split("\\s+", 2); String[] parts = content.split("\\s+", 2);
if (parts.length < 2) { if (parts.length < 2) {

0
gradlew vendored Normal file → Executable file
View file

View file

@ -68,15 +68,50 @@ public final class FoxAuth {
try { try {
discordBot = new Bot(new BotConfig() { discordBot = new Bot(new BotConfig() {
@Override public String getDbType() { return cfg.getDbType(); } @Override
@Override public String getToken() { return token; } public String getDbType() {
@Override public String getDbHost() { return cfg.getDbHost(); } return cfg.getDbType();
@Override public int getDbPort() { return cfg.getDbPort(); } }
@Override public String getDbName() { return cfg.getDbName(); }
@Override public String getDbUser() { return cfg.getDbUser(); } @Override
@Override public String getDbPassword() { return cfg.getDbPassword(); } public String getToken() {
@Override public int getAuthTimeoutSeconds() { return cfg.getAuthTimeoutSeconds(); } return token;
@Override public int getVerificationCodeLength() { return cfg.getVerificationCodeLength(); } }
@Override
public String getDbHost() {
return cfg.getDbHost();
}
@Override
public int getDbPort() {
return cfg.getDbPort();
}
@Override
public String getDbName() {
return cfg.getDbName();
}
@Override
public String getDbUser() {
return cfg.getDbUser();
}
@Override
public String getDbPassword() {
return cfg.getDbPassword();
}
@Override
public int getAuthTimeoutSeconds() {
return cfg.getAuthTimeoutSeconds();
}
@Override
public int getVerificationCodeLength() {
return cfg.getVerificationCodeLength();
}
}); });
log.info("Discord bot started in embedded mode."); log.info("Discord bot started in embedded mode.");
} catch (Exception e) { } catch (Exception e) {
@ -86,8 +121,12 @@ public final class FoxAuth {
@Subscribe @Subscribe
public void onShutdown(final ProxyShutdownEvent event) { public void onShutdown(final ProxyShutdownEvent event) {
if (discordBot != null) discordBot.shutdown(); if (discordBot != null) {
if (db != null) db.close(); discordBot.shutdown();
}
if (db != null) {
db.close();
}
} }
private Path getOrCreateConfig(final String fileName) throws IOException { private Path getOrCreateConfig(final String fileName) throws IOException {
@ -95,7 +134,9 @@ public final class FoxAuth {
Path target = dataDir.resolve(fileName); Path target = dataDir.resolve(fileName);
if (Files.notExists(target)) { if (Files.notExists(target)) {
try (InputStream in = getClass().getResourceAsStream("/" + fileName)) { try (InputStream in = getClass().getResourceAsStream("/" + fileName)) {
if (in == null) throw new IOException("Resource not found: " + fileName); if (in == null) {
throw new IOException("Resource not found: " + fileName);
}
Files.copy(in, target); Files.copy(in, target);
} }
} }

View file

@ -23,13 +23,33 @@ public final class MessageConfig {
this.raw = raw; this.raw = raw;
} }
public Component header() { return text("first_login.header"); } public Component header() {
public Component returningHeader() { return text("returning_login.header"); } return text("first_login.header");
public Component returningBody() { return text("returning_login.body"); } }
public Component returningTimeout() { return text("returning_login.timeout"); }
public Component expiredHeader() { return text("expired.header"); } public Component returningHeader() {
public Component expiredBody() { return text("expired.body"); } return text("returning_login.header");
public Component success() { return text("success"); } }
public Component returningBody() {
return text("returning_login.body");
}
public Component returningTimeout() {
return text("returning_login.timeout");
}
public Component expiredHeader() {
return text("expired.header");
}
public Component expiredBody() {
return text("expired.body");
}
public Component success() {
return text("success");
}
public Component code(final String code) { public Component code(final String code) {
return parse("first_login.code", Map.of("code", code)) return parse("first_login.code", Map.of("code", code))
@ -60,7 +80,9 @@ public final class MessageConfig {
private Component text(final String path, final Map<String, String> placeholders) { private Component text(final String path, final Map<String, String> placeholders) {
String rawStr = resolveRaw(path); String rawStr = resolveRaw(path);
if (rawStr == null) return Component.text('[' + path + ']'); if (rawStr == null) {
return Component.text('[' + path + ']');
}
return parse(rawStr, placeholders); return parse(rawStr, placeholders);
} }

View file

@ -15,18 +15,57 @@ public final class PluginCfg implements DbConfig {
cfg = new YamlConfig(configPath); cfg = new YamlConfig(configPath);
} }
public String getMode() { return cfg.getRequired("mode", String.class); } public String getMode() {
public String getToken() { return cfg.get("token", ""); } return cfg.getRequired("mode", String.class);
public String getDiscordLink() { return cfg.get("discord_link", "discord.gg/ЗАМЕНИ_НА_СВОЙ"); } }
@Override public String getDbType() { return cfg.getRequired("db_type", String.class); } public String getToken() {
@Override public String getDbHost() { return cfg.getRequired("db_host", String.class); } return cfg.get("token", "");
@Override public int getDbPort() { return cfg.getRequired("db_port", Integer.class); } }
@Override public String getDbName() { return cfg.getRequired("db_name", String.class); }
@Override public String getDbUser() { return cfg.getRequired("db_user", String.class); } public String getDiscordLink() {
@Override public String getDbPassword() { return cfg.getRequired("db_password", String.class); } return cfg.get("discord_link", "discord.gg/ЗАМЕНИ_НА_СВОЙ");
@Override public int getAuthTimeoutSeconds() { return cfg.getRequired("auth_timeout_seconds", Integer.class); } }
@Override public int getVerificationCodeLength(){ return cfg.getRequired("verification_code_length",Integer.class); }
@Override
public String getDbType() {
return cfg.getRequired("db_type", String.class);
}
@Override
public String getDbHost() {
return cfg.getRequired("db_host", String.class);
}
@Override
public int getDbPort() {
return cfg.getRequired("db_port", Integer.class);
}
@Override
public String getDbName() {
return cfg.getRequired("db_name", String.class);
}
@Override
public String getDbUser() {
return cfg.getRequired("db_user", String.class);
}
@Override
public String getDbPassword() {
return cfg.getRequired("db_password", String.class);
}
@Override
public int getAuthTimeoutSeconds() {
return cfg.getRequired("auth_timeout_seconds", Integer.class);
}
@Override
public int getVerificationCodeLength() {
return cfg.getRequired("verification_code_length", Integer.class);
}
public MessageConfig getMessages() { public MessageConfig getMessages() {
Object msgs = cfg.get("messages", null); Object msgs = cfg.get("messages", null);

View file

@ -13,7 +13,11 @@ import org.slf4j.Logger;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.*; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public final class LoginListener { public final class LoginListener {
@ -129,6 +133,8 @@ public final class LoginListener {
private void cancelKick(final String nick) { private void cancelKick(final String nick) {
ScheduledFuture<?> f = pendingKicks.remove(nick); ScheduledFuture<?> f = pendingKicks.remove(nick);
if (f != null) f.cancel(false); if (f != null) {
f.cancel(false);
}
} }
} }