diff --git a/build.gradle b/build.gradle index 6332628..11e98ff 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ plugins { id "java" id "java-library" - id "io.freefair.lombok" version "8.4" + id "io.freefair.lombok" version "8.7.1" id "maven-publish" } group 'com.cadiducho' -version '1.8' +version '1.9' java { withJavadocJar() @@ -63,11 +63,11 @@ dependencies { api group: 'com.cadiducho', name: 'TelegramBotAPI', version: '6.9' //Tests - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.10.0' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0' - testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.8.0' - testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '5.8.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.11.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.11.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.11.0' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.12.0' + testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '5.12.0' } test { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd49..a4b76b9 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e09..9355b41 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4..f5feea6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 6689b85..9b42019 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/src/main/java/com/cadiducho/zincite/ConsoleManager.java b/src/main/java/com/cadiducho/zincite/ConsoleManager.java index 081ab96..1648695 100644 --- a/src/main/java/com/cadiducho/zincite/ConsoleManager.java +++ b/src/main/java/com/cadiducho/zincite/ConsoleManager.java @@ -20,31 +20,42 @@ public final class ConsoleManager { private boolean running = true; - public ConsoleManager(ZinciteBot server) { + public ConsoleManager(ZinciteBot server, boolean enableFileLog) { this.server = server; - for (Handler h : logger.getHandlers()) { - logger.removeHandler(h); - } + if (enableFileLog) { + for (Handler h : logger.getHandlers()) { + logger.removeHandler(h); + } - // add log handler which writes to console - logger.addHandler(new FancyConsoleHandler()); + // add log handler which writes to console + logger.addHandler(new FancyConsoleHandler()); - // set system output streams - System.setOut(new PrintStream(new LoggerOutputStream(Level.INFO), true)); - System.setErr(new PrintStream(new LoggerOutputStream(Level.WARNING), true)); + // set system output streams + System.setOut(new PrintStream(new LoggerOutputStream(Level.INFO), true)); + System.setErr(new PrintStream(new LoggerOutputStream(Level.WARNING), true)); + } } - public void startConsole() { - for (Handler handler : logger.getHandlers()) { - if (handler.getClass() == FancyConsoleHandler.class) { - handler.setFormatter(new DateOutputFormatter(CONSOLE_DATE)); + public void startConsole(boolean enableConsoleReader, boolean enableFileLog) { + if (enableFileLog) { + for (Handler handler : logger.getHandlers()) { + if (handler.getClass() == FancyConsoleHandler.class) { + handler.setFormatter(new DateOutputFormatter(CONSOLE_DATE)); + } } } - Thread thread = new ConsoleCommandThread(); - thread.setName("ConsoleCommandThread"); - thread.setDaemon(true); - thread.start(); + + if (enableConsoleReader) { + startConsoleReader(); + } + } + + private void startConsoleReader() { + ConsoleCommandThread consoleCommandThread = new ConsoleCommandThread(); + consoleCommandThread.setName("ZinciteConsoleCommandThread"); + consoleCommandThread.setDaemon(true); + consoleCommandThread.start(); } public void startFile(String logfile) { @@ -143,12 +154,12 @@ public synchronized void flush() { private class ConsoleCommandThread extends Thread { @Override public void run() { - while (running) { - try { - BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in)); - final String consoleCommand = buffer.readLine(); //No confundir con los comandos a través de Telegram - if (consoleCommand == null || consoleCommand.trim().isEmpty()) + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in))) { + while (running) { + String consoleCommand = buffer.readLine(); + if (consoleCommand == null || consoleCommand.trim().isEmpty()) { continue; + } switch (consoleCommand) { case "stop": @@ -160,9 +171,9 @@ public void run() { default: System.out.println("Opción no válida.\n"); } - } catch (IOException | IllegalArgumentException ex) { - logger.log(Level.SEVERE, "Error while reading commands", ex); } + } catch (IOException | IllegalArgumentException ex) { + logger.log(Level.SEVERE, "Error while reading commands", ex); } } } diff --git a/src/main/java/com/cadiducho/zincite/ZinciteBot.java b/src/main/java/com/cadiducho/zincite/ZinciteBot.java index 883a9e3..4901a84 100644 --- a/src/main/java/com/cadiducho/zincite/ZinciteBot.java +++ b/src/main/java/com/cadiducho/zincite/ZinciteBot.java @@ -30,6 +30,11 @@ public class ZinciteBot { */ private final ConsoleManager consoleManager; + /** + * The Zincite configuration + */ + @Getter private final ZinciteConfig config; + /** * The telegram token */ @@ -55,56 +60,26 @@ public class ZinciteBot { /** * Create a ZinciteBot - * @param token Telegram bot token - */ - public ZinciteBot(String token) { - this(token, null, "1.0"); - } - - /** - * Create a ZinciteBot - * @param token Telegram bot token - * @param ownerId Telegram ID of the owner - */ - public ZinciteBot(String token, Long ownerId) { - this(token, ownerId, "1.0"); - } - - /** - * Create a ZinciteBot - * @param token Telegram bot token - * @param ownerId Telegram owner's user id - * @param version Version of the bot + * @param config The Zincite configuration */ - public ZinciteBot(String token, Long ownerId, String version) { - this(token, ownerId, version, "logs", "modules"); - } - - /** - * Create a ZinciteBot - * @param token Telegram bot token - * @param ownerId Telegram owner's user id - * @param version Version of the bot - * @param logsPath Path where logs are stored - * @param modulesPath Path where modules are stored - */ - public ZinciteBot(String token, Long ownerId, String version, String logsPath, String modulesPath) { + public ZinciteBot(ZinciteConfig config) { + if (config == null) { + throw new IllegalArgumentException("Config cannot be null"); + } + if (config.token == null) { + throw new IllegalArgumentException("Token cannot be null"); + } + this.config = config; instance = this; - this.token = token; - this.ownerId = ownerId; - this.version = version; + this.token = config.token; + this.ownerId = config.ownerId; + this.version = config.version; - this.consoleManager = new ConsoleManager(instance); - this.consoleManager.startFile(logsPath + "/log-%D.txt"); - if (token == null) { - log.warning("Token cannot be null"); - } - if (version == null) { - log.warning("Version cannot be null"); - } + this.consoleManager = new ConsoleManager(instance, config.enableFileLog); + this.consoleManager.startFile(config.logsPath + "/log-%D.txt"); - this.moduleManager = new ModuleManager(new File(modulesPath)); + this.moduleManager = new ModuleManager(new File(config.modulesPath)); this.commandManager = new CommandManager(instance); this.telegramBot = new TelegramBot(token); @@ -115,12 +90,12 @@ public ZinciteBot(String token, Long ownerId, String version, String logsPath, S * This includes load modules and start polling Telegram Bot API */ public void startServer() { - consoleManager.startConsole(); + consoleManager.startConsole(config.enableConsoleReader, config.enableFileLog); log.info("Servidor arrancado"); try { moduleManager.loadModules(); - } catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException ex) { + } catch (Exception ex) { log.warning("Can't load modules!"); log.warning(ex.getMessage()); } diff --git a/src/main/java/com/cadiducho/zincite/ZinciteConfig.java b/src/main/java/com/cadiducho/zincite/ZinciteConfig.java new file mode 100644 index 0000000..3f29ee0 --- /dev/null +++ b/src/main/java/com/cadiducho/zincite/ZinciteConfig.java @@ -0,0 +1,44 @@ +package com.cadiducho.zincite; + +import lombok.Builder; + +/** + * Zincite configuration class + */ +@Builder +public class ZinciteConfig { + /** + * The Telegram bot token + */ + public String token; + + /** + * The owner Telegram ID, if is set. Used to send exceptions to owner + */ + public Long ownerId; + + /** + * The bot version + */ + @Builder.Default public String version = "1.0"; + + /** + * The path to the logs folder + */ + @Builder.Default public String logsPath = "logs"; + + /** + * The path to the modules folder + */ + @Builder.Default public String modulesPath = "modules"; + + /** + * Enable if you want to use the console reader. Console will capture input from System.in + */ + @Builder.Default public boolean enableConsoleReader = true; + + /** + * Enable if you want to log to a file + */ + @Builder.Default public boolean enableFileLog = false; +} diff --git a/src/main/java/com/cadiducho/zincite/api/module/ModuleManager.java b/src/main/java/com/cadiducho/zincite/api/module/ModuleManager.java index fdf20f5..ec76085 100644 --- a/src/main/java/com/cadiducho/zincite/api/module/ModuleManager.java +++ b/src/main/java/com/cadiducho/zincite/api/module/ModuleManager.java @@ -6,6 +6,7 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; @@ -16,6 +17,9 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; +/** + * Clase para gestionar los módulos de Zincite + */ @Log @RequiredArgsConstructor public class ModuleManager { @@ -31,8 +35,17 @@ public class ModuleManager { public void registerModule(ZinciteModule module) { modules.add(module); } - - public void loadModules() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { + + /** + * Cargar los módulos desde la carpeta de módulos + * @throws IOException Si ocurre un error al cargar los módulos + * @throws ClassNotFoundException Si no se encuentra la clase del módulo + * @throws IllegalAccessException Si no se puede acceder a la clase del módulo + * @throws InstantiationException Si no se puede instanciar la clase del módulo + * @throws NoSuchMethodException Si no se encuentra el constructor de la clase del módulo + * @throws InvocationTargetException Si ocurre un error al invocar el constructor de la clase del módulo + */ + public void loadModules() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { log.info("Cargando módulos..."); if (Files.notExists(modulesFolder.toPath())) { Files.createDirectories(modulesFolder.toPath()); @@ -49,20 +62,20 @@ public void loadModules() throws IOException, ClassNotFoundException, IllegalAcc urls[i] = files[i].toURI().toURL(); } - final URLClassLoader classLoader = new URLClassLoader(urls); - - for (final File file : files) { - final JarFile jarFile = new JarFile(file); - final Enumeration entries = jarFile.entries(); - - while (entries.hasMoreElements()) { - final JarEntry jarEntry = entries.nextElement(); - if (jarEntry.getName().endsWith(".class")) { - //server.debugLog(jarEntry.getName().replace("/", ".").substring(0, jarEntry.getName().length() - 6)); - Class targetClass = classLoader.loadClass(jarEntry.getName().replace("/", ".").substring(0, jarEntry.getName().length() - 6)); - if (ZinciteModule.class.isAssignableFrom(targetClass)) { - final ZinciteModule module = (ZinciteModule) targetClass.newInstance(); - modules.add(module); + try (URLClassLoader classLoader = new URLClassLoader(urls)) { + for (File file : files) { + try (JarFile jarFile = new JarFile(file)) { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry jarEntry = entries.nextElement(); + if (jarEntry.getName().endsWith(".class")) { + String className = jarEntry.getName().replace("/", ".").replace(".class", ""); + Class targetClass = classLoader.loadClass(className); + if (ZinciteModule.class.isAssignableFrom(targetClass)) { + ZinciteModule module = (ZinciteModule) targetClass.getDeclaredConstructor().newInstance(); + modules.add(module); + } + } } } }